diff options
Diffstat (limited to 'source/blender/draw/modes/shaders/edit_mesh_overlay_geom_tri.glsl')
-rw-r--r-- | source/blender/draw/modes/shaders/edit_mesh_overlay_geom_tri.glsl | 307 |
1 files changed, 307 insertions, 0 deletions
diff --git a/source/blender/draw/modes/shaders/edit_mesh_overlay_geom_tri.glsl b/source/blender/draw/modes/shaders/edit_mesh_overlay_geom_tri.glsl new file mode 100644 index 00000000000..d9c902697b6 --- /dev/null +++ b/source/blender/draw/modes/shaders/edit_mesh_overlay_geom_tri.glsl @@ -0,0 +1,307 @@ + +/* Solid Wirefram implementation + * Mike Erwin, Clément Foucault */ + +/* This shader follows the principles of + * http://developer.download.nvidia.com/SDK/10/direct3d/Source/SolidWireframe/Doc/SolidWireframe.pdf */ + +layout(triangles) in; + +/* This is not perfect. Only a subset of intel gpus are affected. + * This fix have some performance impact. + * TODO Refine the range to only affect GPUs. */ + +#ifdef EDGE_FIX +/* To fix the edge artifacts, we render + * an outline strip around the screenspace + * triangle. Order is important. + * TODO diagram + */ + +#ifdef VERTEX_SELECTION +layout(triangle_strip, max_vertices=23) out; +#else +layout(triangle_strip, max_vertices=17) out; +#endif +#else +layout(triangle_strip, max_vertices=3) out; +#endif + +uniform mat4 ProjectionMatrix; +uniform vec2 viewportSize; + +in vec4 vPos[]; +in vec4 pPos[]; +in ivec4 vData[]; +#ifdef VERTEX_FACING +in float vFacing[]; +#endif + +/* these are the same for all vertices + * and does not need interpolation */ +flat out vec3 edgesCrease; +flat out vec3 edgesBweight; +flat out vec4 faceColor; +flat out ivec3 flag; +flat out int clipCase; +#ifdef VERTEX_SELECTION +out vec3 vertexColor; +#endif +#ifdef VERTEX_FACING +out float facing; +#endif + +/* See fragment shader */ +noperspective out vec2 eData1; +flat out vec2 eData2[3]; + +#define VERTEX_ACTIVE (1 << 0) +#define VERTEX_SELECTED (1 << 1) + +#define FACE_ACTIVE (1 << 2) +#define FACE_SELECTED (1 << 3) + +/* Table 1. Triangle Projection Cases */ +const ivec4 clipPointsIdx[6] = ivec4[6]( + ivec4(0, 1, 2, 2), + ivec4(0, 2, 1, 1), + ivec4(0, 0, 1, 2), + ivec4(1, 2, 0, 0), + ivec4(1, 1, 0, 2), + ivec4(2, 2, 0, 1) +); + +/* project to screen space */ +vec2 proj(vec4 pos) +{ + return (0.5 * (pos.xy / pos.w) + 0.5) * viewportSize; +} + +float dist(vec2 pos[3], vec2 vpos, int v) +{ + /* endpoints of opposite edge */ + vec2 e1 = pos[(v + 1) % 3]; + vec2 e2 = pos[(v + 2) % 3]; + /* Edge normalized vector */ + vec2 dir = normalize(e2 - e1); + /* perpendicular to dir */ + vec2 orthogonal = vec2(-dir.y, dir.x); + + return abs(dot(vpos - e1, orthogonal)); +} + +vec3 getVertexColor(int v) +{ + if ((vData[v].x & (VERTEX_ACTIVE | VERTEX_SELECTED)) != 0) + return colorEdgeSelect.rgb; + else + return colorWireEdit.rgb; +} + +vec4 getClipData(vec2 pos[3], ivec2 vidx) +{ + vec2 A = pos[vidx.x]; + vec2 Adir = normalize(A - pos[vidx.y]); + + return vec4(A, Adir); +} + +void doVertex(int v) +{ +#ifdef VERTEX_SELECTION + vertexColor = getVertexColor(v); +#endif + +#ifdef VERTEX_FACING + facing = vFacing[v]; +#endif + + gl_Position = pPos[v]; + + EmitVertex(); +} + +void doLoopStrip(int v, vec3 offset) +{ + doVertex(v); + + gl_Position.xyz += offset; + + EmitVertex(); +} + +#ifdef ANTI_ALIASING +#define Z_OFFSET 0.008 +#else +#define Z_OFFSET 0.0 +#endif + +void main() +{ + /* First we detect which case we are in */ + clipCase = 0; + + /* if perspective */ + if (ProjectionMatrix[3][3] == 0.0) { + /* See Table 1. Triangle Projection Cases */ + clipCase += int(pPos[0].z / pPos[0].w < -1 || vPos[0].z > 0.0) * 4; + clipCase += int(pPos[1].z / pPos[1].w < -1 || vPos[1].z > 0.0) * 2; + clipCase += int(pPos[2].z / pPos[2].w < -1 || vPos[2].z > 0.0) * 1; + } + + /* If triangle is behind nearplane, early out */ + if (clipCase == 7) + return; + + /* Edge */ + ivec3 eflag; vec3 ecrease, ebweight; + for (int v = 0; v < 3; ++v) { + flag[v] = eflag[v] = vData[v].y | (vData[v].x << 8); + edgesCrease[v] = ecrease[v] = vData[v].z / 255.0; + edgesBweight[v] = ebweight[v] = vData[v].w / 255.0; + } + + /* Face */ + if ((vData[0].x & FACE_ACTIVE) != 0) + faceColor = colorEditMeshActive; + else if ((vData[0].x & FACE_SELECTED) != 0) + faceColor = colorFaceSelect; + else + faceColor = colorFace; + + /* Vertex */ + vec2 pos[3] = vec2[3](proj(pPos[0]), proj(pPos[1]), proj(pPos[2])); + + /* Simple case : compute edge distances in geometry shader */ + if (clipCase == 0) { + + /* Packing screen positions and 2 distances */ + eData2[0] = pos[2]; + eData2[1] = pos[1]; + eData2[2] = pos[0]; + + /* Only pass the first 2 distances */ + for (int v = 0; v < 2; ++v) { + eData1[v] = dist(pos, pos[v], v); + doVertex(v); + eData1[v] = 0.0; + } + + /* and the last vertex */ + doVertex(2); + +#ifdef EDGE_FIX + vec2 fixvec[6]; + vec2 fixvecaf[6]; + vec2 cornervec[3]; + + /* This fix the case when 2 vertices are perfectly aligned + * and corner vectors have nowhere to go. + * ie: length(cornervec[i]) == 0 */ + const float epsilon = 1e-2; /* in pixel so not that much */ + const vec2 bias[3] = vec2[3]( + vec2( epsilon, epsilon), + vec2(-epsilon, epsilon), + vec2( 0.0, -epsilon) + ); + + for (int i = 0; i < 3; ++i) { + int i1 = (i + 1) % 3; + int i2 = (i + 2) % 3; + + vec2 v1 = pos[i] + bias[i]; + vec2 v2 = pos[i1] + bias[i1]; + vec2 v3 = pos[i2] + bias[i2]; + + /* Edge normalized vector */ + vec2 dir = normalize(v2 - v1); + vec2 dir2 = normalize(v3 - v1); + + cornervec[i] = -normalize(dir + dir2); + + /* perpendicular to dir */ + vec2 perp = vec2(-dir.y, dir.x); + + /* Backface case */ + if (dot(perp, dir2) > 0) { + perp = -perp; + } + + /* Make it view independent */ + perp *= sizeEdgeFix / viewportSize; + cornervec[i] *= sizeEdgeFix / viewportSize; + fixvec[i] = fixvecaf[i] = perp; + + /* Perspective */ + if (ProjectionMatrix[3][3] == 0.0) { + /* vPos[i].z is negative and we don't want + * our fixvec to be flipped */ + fixvec[i] *= -vPos[i].z; + fixvecaf[i] *= -vPos[i1].z; + cornervec[i] *= -vPos[i].z; + } + } + + /* to not let face color bleed */ + faceColor.a = 0.0; + + /* we don't want other edges : make them far */ + eData1 = vec2(1e10); + eData2[0] = vec2(1e10); + + /* Start with the same last vertex to create a + * degenerate triangle in order to "create" + * a new triangle strip */ + for (int i = 2; i < 5; ++i) { + int vbe = (i - 1) % 3; + int vaf = (i + 1) % 3; + int v = i % 3; + + /* Position of the "hidden" third vertex */ + eData2[0] = pos[vbe]; + doLoopStrip(v, vec3(fixvec[v], Z_OFFSET)); + + /* Now one triangle only shade one edge + * so we use the edge distance calculated + * in the fragment shader, the third edge; + * we do this because we need flat interp to + * draw a continuous triangle strip */ + eData2[1] = pos[vaf]; + eData2[2] = pos[v]; + flag[0] = (vData[v].x << 8); + flag[1] = (vData[vaf].x << 8); + flag[2] = eflag[vbe]; + edgesCrease[2] = ecrease[vbe]; + edgesBweight[2] = ebweight[vbe]; + + doLoopStrip(vaf, vec3(fixvecaf[v], Z_OFFSET)); + + /* corner vertices should not draw edges but draw point only */ + flag[2] = (vData[vbe].x << 8); +#ifdef VERTEX_SELECTION + doLoopStrip(vaf, vec3(cornervec[vaf], Z_OFFSET)); +#endif + } + + /* finish the loop strip */ + doLoopStrip(2, vec3(fixvec[2], Z_OFFSET)); +#endif + } + /* Harder case : compute visible edges vectors */ + else { + ivec4 vindices = clipPointsIdx[clipCase - 1]; + + vec4 tmp; + tmp = getClipData(pos, vindices.xz); + eData1 = tmp.xy; + eData2[0] = tmp.zw; + tmp = getClipData(pos, vindices.yw); + eData2[1] = tmp.xy; + eData2[2] = tmp.zw; + + for (int v = 0; v < 3; ++v) + doVertex(v); + } + + EndPrimitive(); +} |