T55456 EditMeshMode
[blender.git] / source / blender / draw / modes / shaders / edit_mesh_overlay_geom_tri.glsl
1
2 /* Solid Wirefram implementation
3  * Mike Erwin, ClĂ©ment Foucault */
4
5 /* This shader follows the principles of
6  * http://developer.download.nvidia.com/SDK/10/direct3d/Source/SolidWireframe/Doc/SolidWireframe.pdf */
7
8 layout(triangles) in;
9
10 /* This is not perfect. Only a subset of intel gpus are affected.
11  * This fix have some performance impact.
12  * TODO Refine the range to only affect GPUs. */
13
14 #ifdef EDGE_FIX
15 /* To fix the edge artifacts, we render
16  * an outline strip around the screenspace
17  * triangle. Order is important.
18  * TODO diagram
19  */
20
21 #ifdef VERTEX_SELECTION
22 layout(triangle_strip, max_vertices=23) out;
23 #else
24 layout(triangle_strip, max_vertices=17) out;
25 #endif
26 #else
27 layout(triangle_strip, max_vertices=3) out;
28 #endif
29
30 uniform mat4 ProjectionMatrix;
31 uniform vec2 viewportSize;
32
33 in vec4 vPos[];
34 in vec4 pPos[];
35 in ivec4 vData[];
36 #ifdef VERTEX_FACING
37 in float vFacing[];
38 #endif
39
40 /* these are the same for all vertices
41  * and does not need interpolation */
42 flat out vec3 edgesCrease;
43 flat out vec3 edgesBweight;
44 flat out vec4 faceColor;
45 flat out ivec3 flag;
46 flat out int clipCase;
47 #ifdef VERTEX_SELECTION
48 out vec3 vertexColor;
49 #endif
50 #ifdef VERTEX_FACING
51 out float facing;
52 #endif
53
54 /* See fragment shader */
55 noperspective out vec2 eData1;
56 flat out vec2 eData2[3];
57
58 #define VERTEX_ACTIVE   (1 << 0)
59 #define VERTEX_SELECTED (1 << 1)
60
61 #define FACE_ACTIVE     (1 << 2)
62 #define FACE_SELECTED   (1 << 3)
63
64 /* Table 1. Triangle Projection Cases */
65 const ivec4 clipPointsIdx[6] = ivec4[6](
66         ivec4(0, 1, 2, 2),
67         ivec4(0, 2, 1, 1),
68         ivec4(0, 0, 1, 2),
69         ivec4(1, 2, 0, 0),
70         ivec4(1, 1, 0, 2),
71         ivec4(2, 2, 0, 1)
72 );
73
74 /* project to screen space */
75 vec2 proj(vec4 pos)
76 {
77         return (0.5 * (pos.xy / pos.w) + 0.5) * viewportSize;
78 }
79
80 float dist(vec2 pos[3], vec2 vpos, int v)
81 {
82         /* endpoints of opposite edge */
83         vec2 e1 = pos[(v + 1) % 3];
84         vec2 e2 = pos[(v + 2) % 3];
85         /* Edge normalized vector */
86         vec2 dir = normalize(e2 - e1);
87         /* perpendicular to dir */
88         vec2 orthogonal = vec2(-dir.y, dir.x);
89
90         return abs(dot(vpos - e1, orthogonal));
91 }
92
93 vec3 getVertexColor(int v)
94 {
95         if ((vData[v].x & (VERTEX_ACTIVE | VERTEX_SELECTED)) != 0)
96                 return colorEdgeSelect.rgb;
97         else
98 #ifdef EDGE_SELECTION
99                 return colorWireEdit.rgb;
100 #else
101                 return colorWireInactive.rgb;
102 #endif
103 }
104
105 vec4 getClipData(vec2 pos[3], ivec2 vidx)
106 {
107         vec2 A = pos[vidx.x];
108         vec2 Adir = normalize(A - pos[vidx.y]);
109
110         return vec4(A, Adir);
111 }
112
113 void doVertex(int v)
114 {
115 #ifdef VERTEX_SELECTION
116         vertexColor = getVertexColor(v);
117 #endif
118
119 #ifdef VERTEX_FACING
120         facing = vFacing[v];
121 #endif
122
123         gl_Position = pPos[v];
124
125         EmitVertex();
126 }
127
128 void doLoopStrip(int v, vec3 offset)
129 {
130         doVertex(v);
131
132         gl_Position.xyz += offset;
133
134         EmitVertex();
135 }
136
137 #ifdef ANTI_ALIASING
138 #define Z_OFFSET 0.008
139 #else
140 #define Z_OFFSET 0.0
141 #endif
142
143 void main()
144 {
145         /* First we detect which case we are in */
146         clipCase = 0;
147
148         /* if perspective */
149         if (ProjectionMatrix[3][3] == 0.0) {
150                 /* See Table 1. Triangle Projection Cases  */
151                 clipCase += int(pPos[0].z / pPos[0].w < -1 || vPos[0].z > 0.0) * 4;
152                 clipCase += int(pPos[1].z / pPos[1].w < -1 || vPos[1].z > 0.0) * 2;
153                 clipCase += int(pPos[2].z / pPos[2].w < -1 || vPos[2].z > 0.0) * 1;
154         }
155
156         /* If triangle is behind nearplane, early out */
157         if (clipCase == 7)
158                 return;
159
160         /* Edge */
161         ivec3 eflag; vec3 ecrease, ebweight;
162         for (int v = 0; v < 3; ++v) {
163                 flag[v] = eflag[v] = vData[v].y | (vData[v].x << 8);
164                 edgesCrease[v] = ecrease[v] = vData[v].z / 255.0;
165                 edgesBweight[v] = ebweight[v] = vData[v].w / 255.0;
166         }
167
168         /* Face */
169         if ((vData[0].x & FACE_ACTIVE) != 0)
170                 faceColor = colorFaceSelect;
171         else if ((vData[0].x & FACE_SELECTED) != 0)
172                 faceColor = colorFaceSelect;
173         else
174                 faceColor = colorFace;
175
176         /* Vertex */
177         vec2 pos[3] = vec2[3](proj(pPos[0]), proj(pPos[1]), proj(pPos[2]));
178
179         /* Simple case : compute edge distances in geometry shader */
180         if (clipCase == 0) {
181
182                 /* Packing screen positions and 2 distances */
183                 eData2[0] = pos[2];
184                 eData2[1] = pos[1];
185                 eData2[2] = pos[0];
186
187                 /* Only pass the first 2 distances */
188                 for (int v = 0; v < 2; ++v) {
189                         eData1[v] = dist(pos, pos[v], v);
190                         doVertex(v);
191                         eData1[v] = 0.0;
192                 }
193
194                 /* and the last vertex */
195                 doVertex(2);
196
197 #ifdef EDGE_FIX
198                 vec2 fixvec[6];
199                 vec2 fixvecaf[6];
200                 vec2 cornervec[3];
201
202                 /* This fix the case when 2 vertices are perfectly aligned
203                  * and corner vectors have nowhere to go.
204                  * ie: length(cornervec[i]) == 0 */
205                 const float epsilon = 1e-2; /* in pixel so not that much */
206                 const vec2 bias[3] = vec2[3](
207                         vec2( epsilon,  epsilon),
208                         vec2(-epsilon,  epsilon),
209                         vec2(     0.0, -epsilon)
210                 );
211
212                 for (int i = 0; i < 3; ++i) {
213                         int i1 = (i + 1) % 3;
214                         int i2 = (i + 2) % 3;
215
216                         vec2 v1 = pos[i] + bias[i];
217                         vec2 v2 = pos[i1] + bias[i1];
218                         vec2 v3 = pos[i2] + bias[i2];
219
220                         /* Edge normalized vector */
221                         vec2 dir = normalize(v2 - v1);
222                         vec2 dir2 = normalize(v3 - v1);
223
224                         cornervec[i] = -normalize(dir + dir2);
225
226                         /* perpendicular to dir */
227                         vec2 perp = vec2(-dir.y, dir.x);
228
229                         /* Backface case */
230                         if (dot(perp, dir2) > 0) {
231                                 perp = -perp;
232                         }
233
234                         /* Make it view independent */
235                         perp *= sizeEdgeFix / viewportSize;
236                         cornervec[i] *= sizeEdgeFix / viewportSize;
237                         fixvec[i] = fixvecaf[i] = perp;
238
239                         /* Perspective */
240                         if (ProjectionMatrix[3][3] == 0.0) {
241                                 /* vPos[i].z is negative and we don't want
242                                  * our fixvec to be flipped */
243                                 fixvec[i] *= -vPos[i].z;
244                                 fixvecaf[i] *= -vPos[i1].z;
245                                 cornervec[i] *= -vPos[i].z;
246                         }
247                 }
248
249                 /* to not let face color bleed */
250                 faceColor.a = 0.0;
251
252                 /* we don't want other edges : make them far */
253                 eData1 = vec2(1e10);
254                 eData2[0] = vec2(1e10);
255
256                 /* Start with the same last vertex to create a
257                  * degenerate triangle in order to "create"
258                  * a new triangle strip */
259                 for (int i = 2; i < 5; ++i) {
260                         int vbe = (i - 1) % 3;
261                         int vaf = (i + 1) % 3;
262                         int v = i % 3;
263
264                         /* Position of the "hidden" third vertex */
265                         eData2[0] = pos[vbe];
266                         doLoopStrip(v, vec3(fixvec[v], Z_OFFSET));
267
268                         /* Now one triangle only shade one edge
269                          * so we use the edge distance calculated
270                          * in the fragment shader, the third edge;
271                          * we do this because we need flat interp to
272                          * draw a continuous triangle strip */
273                         eData2[1] = pos[vaf];
274                         eData2[2] = pos[v];
275                         flag[0] = (vData[v].x << 8);
276                         flag[1] = (vData[vaf].x << 8);
277                         flag[2] = eflag[vbe];
278                         edgesCrease[2] = ecrease[vbe];
279                         edgesBweight[2] = ebweight[vbe];
280
281                         doLoopStrip(vaf, vec3(fixvecaf[v], Z_OFFSET));
282
283                         /* corner vertices should not draw edges but draw point only */
284                         flag[2] = (vData[vbe].x << 8);
285 #ifdef VERTEX_SELECTION
286                         doLoopStrip(vaf, vec3(cornervec[vaf], Z_OFFSET));
287 #endif
288                 }
289
290                 /* finish the loop strip */
291                 doLoopStrip(2, vec3(fixvec[2], Z_OFFSET));
292 #endif
293         }
294         /* Harder case : compute visible edges vectors */
295         else {
296                 ivec4 vindices = clipPointsIdx[clipCase - 1];
297
298                 vec4 tmp;
299                 tmp = getClipData(pos, vindices.xz);
300                 eData1 = tmp.xy;
301                 eData2[0] = tmp.zw;
302                 tmp = getClipData(pos, vindices.yw);
303                 eData2[1] = tmp.xy;
304                 eData2[2] = tmp.zw;
305
306                 for (int v = 0; v < 3; ++v)
307                         doVertex(v);
308         }
309
310         EndPrimitive();
311 }