Skip to content

Instantly share code, notes, and snippets.

@yurivish yurivish/linegen.jl
Created Sep 21, 2014

Embed
What would you like to do?
Antialiased Lines (in progress...)
### Vectors ###
immutable Vec2
x::GLfloat
y::GLfloat
end
Base.getindex(a::Vec2, n) = n == 1 ? a.x : n == 2 ? a.y : error("Invalid Vec2 index: $n")
Base.length(a::Vec2) = 2
Base.norm(a::Vec2) = sqrt(a.x^2 + a.y^2)
normalize(a::Vec2) = a / norm(a)
flipx(a::Vec2) = Vec2(-a.x, a.y)
flipy(a::Vec2) = Vec2(a.x, -a.y)
-(a::Vec2) = Vec2(-a.x, -a.y)
-(a::Vec2, b::Vec2) = Vec2(a.x - b.x, a.y - b.y)
-(a::Vec2, b::Number) = Vec2(a.x - b, a.y - b)
+(a::Vec2, b::Vec2) = Vec2(a.x + b.x, a.y + b.y)
+(a::Number, b::Vec2) = Vec2(a + b.x, a + b.y)
+(a::Vec2, b::Number) = Vec2(a.x + b, a.y + b)
*(a::Vec2, b::Vec2) = Vec2(a.x * b.x, a.y * b.y)
*(a::Number, b::Vec2) = Vec2(a * b.x, a * b.y)
*(a::Vec2, b::Number) = Vec2(a.x * b, a.y * b)
/(a::Vec2, b::Vec2) = Vec2(a.x / b.x, a.y / b.y)
/(a::Vec2, b::Number) = Vec2(a.x / b, a.y / b)
⋅(a::Vec2, b::Vec2) = a.x * b.x + a.y * b.y # dot product
×(a::Vec2, b::Vec2) = a.x * b.y - a.y * b.x # 2d cross product
# http://mathworld.wolfram.com/Collinear.html
# TODO: What should the tolerance be?
collinear(a::Vec2, b::Vec2, c::Vec2) = abs(a.x * (b.y - c.y) + b.x * (c.y - a.y) + c.x * (a.y - b.y)) < 0.00001
Base.rand(::Type{Vec2}) = Vec2(rand(GLfloat), rand(GLfloat))
rot90l(a::Vec2) = Vec2(-a.y, a.x)
rot90r(a::Vec2) = Vec2( a.y, -a.x)
### Lines ###
#=
Want:
- Control line width and color per-vertex
- Set line `width in pixels
=#
immutable LineVertex
pos::Vec2
ext::Vec2
texnormal::GLfloat
end
type LineGen
vao::GLuint
vbo::GLuint
program::GLuint
verts::Array
numverts::Int
function LineGen(width, height) # Hack.
const vsh = """
#version 330
in vec2 a_position;
in vec2 a_extrude;
in float a_tex_normal;
out vec2 v_normal;
out float v_tex_normal;
void main() {
vec2 position = (a_position / vec2($width, $height)) * 2.0 - 1.0;
float u_lineWidth = 1. / $width; // 0.01;
v_normal = normalize(a_extrude);
v_tex_normal = a_tex_normal;
gl_Position = vec4(position + u_lineWidth * a_extrude, 0.0, 1.0);
}
"""
const fsh = """
#version 330
out vec4 outColor;
in vec2 v_normal;
in float v_tex_normal;
// threshold is constant, distance is smoothly varying
float aastep(float threshold, float dist) {
float afwidth = 0.7 * length(vec2(dFdx(dist), dFdy(dist)));
return smoothstep(threshold - afwidth, threshold + afwidth, dist);
}
void main() {
// outColor = vec4(1);
float aa = 1 - aastep(1, abs(v_tex_normal));
outColor = vec4(1, 1, 1, aa * 0.17);
}
"""
vao = glGenVertexArray()
vbo = glGenBuffer()
vertexShader = createshader(vsh, GL_VERTEX_SHADER)
fragmentShader = createshader(fsh, GL_FRAGMENT_SHADER)
program = createshaderprogram(vertexShader, fragmentShader)
glWithVertexArrayAndArrayBufferAndShaderProgram(vao, vbo, program) do
positionAttribute = glGetAttribLocation(program, "a_position")
glEnableVertexAttribArray(positionAttribute)
glVertexAttribPointer(positionAttribute, 2, GL_FLOAT, false, sizeof(LineVertex), 0)
extrudeAttribute = glGetAttribLocation(program, "a_extrude")
glEnableVertexAttribArray(extrudeAttribute)
glVertexAttribPointer(extrudeAttribute, 2, GL_FLOAT, false, sizeof(LineVertex), sizeof(GLfloat) * 2) # TODO: Offset into LineVertex
texNormalAttribute = glGetAttribLocation(program, "a_tex_normal")
glEnableVertexAttribArray(texNormalAttribute)
glVertexAttribPointer(texNormalAttribute, 1, GL_FLOAT, false, sizeof(LineVertex), sizeof(GLfloat) * 4) # TODO: Offset into LineVertex
end
verts = [LineVertex(Vec2(0, 0), Vec2(0, 0), 0) for i in 1:1000000]
new(vao, vbo, program, verts, 0)
end
end
function clear!(gen::LineGen)
gen.numverts = 0
end
function emit!(gen::LineGen, vert)
gen.numverts = mod1(gen.numverts + 1, 1000000) # TODO: Ringbuffer w/ maxsize passed in
gen.verts[gen.numverts] = vert
end
function line!(gen::LineGen, points)
@assert length(points) > 1
for (i, point) in enumerate(points)
if i == 1
next = points[i + 1]
normal = rot90r(normalize(next - point))
if gen.numverts > 0
# if we're adding to a nonempty vertex list,
# delineate with degenerate triangles.
emit!(gen, gen.verts[gen.numverts])
emit!(gen, LineVertex(point, normal, 1))
end
elseif i == length(points)
prev = points[i - 1]
normal = rot90r(normalize(point - prev))
else
prev, next = points[i - 1], points[i + 1]
if collinear(prev, point, next)
normal = gen.verts[gen.numverts].ext
else
to_next = normalize(next - point)
to_prev = normalize(prev - point)
dir = normalize(to_next + to_prev)
sinangle = dir × to_next
if abs(sinangle) < 0.1
# Hack to minimize the effect of crazy miter joins
sinangle = 0.1
end
normal = dir / sinangle
end
end
emit!(gen, LineVertex(point, normal, 1))
emit!(gen, LineVertex(point, -normal, -1))
end
end
function render(gen::LineGen)
# for i in 1:gen.numverts
# v = gen.verts[i]
# gen.verts[i] = LineVertex(v.pos + Vec2(0.001, 0001), v.ext, v.texnormal)
# end
glUseProgram(gen.program)
glBindVertexArray(gen.vao)
glBindBuffer(GL_ARRAY_BUFFER, gen.vbo)
glBufferData(GL_ARRAY_BUFFER, sizeof(gen.verts), gen.verts, GL_STREAM_DRAW)
glDrawArrays(GL_TRIANGLE_STRIP, 0, gen.numverts)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.