Created
September 21, 2014 17:19
-
-
Save yurivish/14c669a467fb1b65177a to your computer and use it in GitHub Desktop.
Antialiased Lines (in progress...)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
### 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