]> icculus.org git repositories - divverent/darkplaces.git/blob - r_shadow.c
3D attenuation texture works now
[divverent/darkplaces.git] / r_shadow.c
1
2 #include "quakedef.h"
3 #include "r_shadow.h"
4
5 mempool_t *r_shadow_mempool;
6
7 int maxshadowelements;
8 int *shadowelements;
9 int maxtrianglefacinglight;
10 qbyte *trianglefacinglight;
11
12 rtexturepool_t *r_shadow_texturepool;
13 rtexture_t *r_shadow_attenuationtexture;
14
15 cvar_t r_shadow1 = {0, "r_shadow1", "16"};
16 cvar_t r_shadow2 = {0, "r_shadow2", "2"};
17 cvar_t r_shadow3 = {0, "r_shadow3", "65536"};
18 cvar_t r_shadow4 = {0, "r_shadow4", "1"};
19 cvar_t r_shadow5 = {0, "r_shadow5", "0.05"};
20 cvar_t r_shadow6 = {0, "r_shadow6", "1"};
21
22 void r_shadow_start(void)
23 {
24         // allocate vertex processing arrays
25         r_shadow_mempool = Mem_AllocPool("R_Shadow");
26         maxshadowelements = 0;
27         shadowelements = NULL;
28         maxtrianglefacinglight = 0;
29         trianglefacinglight = NULL;
30         r_shadow_attenuationtexture = NULL;
31         r_shadow_texturepool = NULL;
32 }
33
34 void r_shadow_shutdown(void)
35 {
36         r_shadow_attenuationtexture = NULL;
37         R_FreeTexturePool(&r_shadow_texturepool);
38         maxshadowelements = 0;
39         shadowelements = NULL;
40         maxtrianglefacinglight = 0;
41         trianglefacinglight = NULL;
42         Mem_FreePool(&r_shadow_mempool);
43 }
44
45 void r_shadow_newmap(void)
46 {
47 }
48
49 void R_Shadow_Init(void)
50 {
51         Cvar_RegisterVariable(&r_shadow1);
52         Cvar_RegisterVariable(&r_shadow2);
53         Cvar_RegisterVariable(&r_shadow3);
54         Cvar_RegisterVariable(&r_shadow4);
55         Cvar_RegisterVariable(&r_shadow5);
56         Cvar_RegisterVariable(&r_shadow6);
57         R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
58 }
59
60 void R_Shadow_Volume(int numverts, int numtris, float *vertex, int *elements, int *neighbors, vec3_t relativelightorigin, float lightradius, float projectdistance, int visiblevolume)
61 {
62         int i, *e, *n, *out, tris;
63         float *v0, *v1, *v2, temp[3], f;
64         if (projectdistance < 0.1)
65         {
66                 Con_Printf("R_Shadow_Volume: projectdistance %f\n");
67                 return;
68         }
69 // terminology:
70 //
71 // frontface:
72 // a triangle facing the light source
73 //
74 // backface:
75 // a triangle not facing the light source
76 //
77 // shadow volume:
78 // an extrusion of the backfaces, beginning at the original geometry and
79 // ending further from the light source than the original geometry
80 // (presumably at least as far as the light's radius, if the light has a
81 // radius at all), capped at both front and back to avoid any problems
82 //
83 // description:
84 // draws the shadow volumes of the model.
85 // requirements:
86 // vertex loations must already be in vertex before use.
87 // vertex must have capacity for numverts * 2.
88
89         // make sure trianglefacinglight is big enough for this volume
90         if (maxtrianglefacinglight < numtris)
91         {
92                 maxtrianglefacinglight = numtris;
93                 if (trianglefacinglight)
94                         Mem_Free(trianglefacinglight);
95                 trianglefacinglight = Mem_Alloc(r_shadow_mempool, maxtrianglefacinglight);
96         }
97
98         // make sure shadowelements is big enough for this volume
99         if (maxshadowelements < numtris * 24)
100         {
101                 maxshadowelements = numtris * 24;
102                 if (shadowelements)
103                         Mem_Free(shadowelements);
104                 shadowelements = Mem_Alloc(r_shadow_mempool, maxshadowelements * sizeof(int));
105         }
106
107         // make projected vertices
108         // by clever use of elements we'll construct the whole shadow from
109         // the unprojected vertices and these projected vertices
110         for (i = 0, v0 = vertex, v1 = vertex + numverts * 4;i < numverts;i++, v0 += 4, v1 += 4)
111         {
112                 VectorSubtract(v0, relativelightorigin, temp);
113 #if 0
114                 f = lightradius / sqrt(DotProduct(temp,temp));
115                 if (f < 1)
116                         f = 1;
117                 VectorMA(relativelightorigin, f, temp, v1);
118 #else
119                 f = projectdistance / sqrt(DotProduct(temp,temp));
120                 VectorMA(v0, f, temp, v1);
121 #endif
122         }
123
124         // check which triangles are facing the light
125         for (i = 0, e = elements;i < numtris;i++, e += 3)
126         {
127                 // calculate triangle facing flag
128                 v0 = vertex + e[0] * 4;
129                 v1 = vertex + e[1] * 4;
130                 v2 = vertex + e[2] * 4;
131                 // we do not need to normalize the surface normal because both sides
132                 // of the comparison use it, therefore they are both multiplied the
133                 // same amount...  furthermore the subtract can be done on the
134                 // vectors, saving a little bit of math in the dotproducts
135 #if 1
136                 // fast version
137                 // subtracts v1 from v0 and v2, combined into a crossproduct,
138                 // combined with a dotproduct of the light location relative to the
139                 // first point of the triangle (any point works, since the triangle
140                 // is obviously flat), and finally a comparison to determine if the
141                 // light is infront of the triangle (the goal of this statement)
142                 trianglefacinglight[i] =
143                    (relativelightorigin[0] - v0[0]) * ((v0[1] - v1[1]) * (v2[2] - v1[2]) - (v0[2] - v1[2]) * (v2[1] - v1[1]))
144                  + (relativelightorigin[1] - v0[1]) * ((v0[2] - v1[2]) * (v2[0] - v1[0]) - (v0[0] - v1[0]) * (v2[2] - v1[2]))
145                  + (relativelightorigin[2] - v0[2]) * ((v0[0] - v1[0]) * (v2[1] - v1[1]) - (v0[1] - v1[1]) * (v2[0] - v1[0])) > 0;
146 #else
147                 // readable version
148                 {
149                 float dir0[3], dir1[3];
150
151                 // calculate two mostly perpendicular edge directions
152                 VectorSubtract(v0, v1, dir0);
153                 VectorSubtract(v2, v1, dir1);
154
155                 // we have two edge directions, we can calculate a third vector from
156                 // them, which is the direction of the surface normal (it's magnitude
157                 // is not 1 however)
158                 CrossProduct(dir0, dir1, temp);
159
160                 // this is entirely unnecessary, but kept for clarity
161                 //VectorNormalize(temp);
162
163                 // compare distance of light along normal, with distance of any point
164                 // of the triangle along the same normal (the triangle is planar,
165                 // I.E. flat, so all points give the same answer)
166                 // the normal is not normalized because it is used on both sides of
167                 // the comparison, so it's magnitude does not matter
168                 trianglefacinglight[i] = DotProduct(relativelightorigin, temp) >= DotProduct(v0, temp);
169 #endif
170         }
171
172         // output triangle elements
173         out = shadowelements;
174         tris = 0;
175
176         // check each backface for bordering frontfaces,
177         // and cast shadow polygons from those edges,
178         // also create front and back caps for shadow volume
179         for (i = 0, e = elements, n = neighbors;i < numtris;i++, e += 3, n += 3)
180         {
181                 if (!trianglefacinglight[i])
182                 {
183                         // triangle is backface and therefore casts shadow,
184                         // output front and back caps for shadow volume
185 #if 1
186                         // front cap (with flipped winding order)
187                         out[0] = e[0];
188                         out[1] = e[2];
189                         out[2] = e[1];
190                         // rear cap
191                         out[3] = e[0] + numverts;
192                         out[4] = e[1] + numverts;
193                         out[5] = e[2] + numverts;
194                         out += 6;
195                         tris += 2;
196 #else
197                         // rear cap
198                         out[0] = e[0] + numverts;
199                         out[1] = e[1] + numverts;
200                         out[2] = e[2] + numverts;
201                         out += 3;
202                         tris += 1;
203 #endif
204                         // check the edges
205                         if (n[0] < 0 || trianglefacinglight[n[0]])
206                         {
207                                 out[0] = e[0];
208                                 out[1] = e[1];
209                                 out[2] = e[1] + numverts;
210                                 out[3] = e[0];
211                                 out[4] = e[1] + numverts;
212                                 out[5] = e[0] + numverts;
213                                 out += 6;
214                                 tris += 2;
215                         }
216                         if (n[1] < 0 || trianglefacinglight[n[1]])
217                         {
218                                 out[0] = e[1];
219                                 out[1] = e[2];
220                                 out[2] = e[2] + numverts;
221                                 out[3] = e[1];
222                                 out[4] = e[2] + numverts;
223                                 out[5] = e[1] + numverts;
224                                 out += 6;
225                                 tris += 2;
226                         }
227                         if (n[2] < 0 || trianglefacinglight[n[2]])
228                         {
229                                 out[0] = e[2];
230                                 out[1] = e[0];
231                                 out[2] = e[0] + numverts;
232                                 out[3] = e[2];
233                                 out[4] = e[0] + numverts;
234                                 out[5] = e[2] + numverts;
235                                 out += 6;
236                                 tris += 2;
237                         }
238                 }
239         }
240         R_Shadow_RenderVolume(numverts * 2, tris, shadowelements, visiblevolume);
241 }
242
243 void R_Shadow_RenderVolume(int numverts, int numtris, int *elements, int visiblevolume)
244 {
245         // draw the volume
246         if (visiblevolume)
247         {
248                 qglDisable(GL_CULL_FACE);
249                 R_Mesh_Draw(numverts, numtris, elements);
250                 qglEnable(GL_CULL_FACE);
251         }
252         else
253         {
254                 // increment stencil if backface is behind depthbuffer
255                 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
256                 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
257                 R_Mesh_Draw(numverts, numtris, elements);
258                 // decrement stencil if frontface is behind depthbuffer
259                 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
260                 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
261                 R_Mesh_Draw(numverts, numtris, elements);
262         }
263 }
264
265 float r_shadow_atten1, r_shadow_atten2, r_shadow_atten5;
266 static void R_Shadow_MakeTextures(void)
267 {
268         int x, y, z, d;
269         float v[3];
270         qbyte data[32][32][32][4];
271         R_FreeTexturePool(&r_shadow_texturepool);
272         r_shadow_texturepool = R_AllocTexturePool();
273         r_shadow_atten1 = r_shadow1.value;
274         r_shadow_atten2 = r_shadow2.value;
275         r_shadow_atten5 = r_shadow5.value;
276         for (z = 0;z < 32;z++)
277         {
278                 for (y = 0;y < 32;y++)
279                 {
280                         for (x = 0;x < 32;x++)
281                         {
282                                 v[0] = (x / 32.0f) - 0.5f;
283                                 v[1] = (y / 32.0f) - 0.5f;
284                                 v[2] = (z / 32.0f) - 0.5f;
285                                 d = (int) (((r_shadow_atten1 / (DotProduct(v, v)+r_shadow_atten5)) - (r_shadow_atten1 * r_shadow_atten2)));
286                                 d = bound(0, d, 255);
287                                 data[z][y][x][0] = data[z][y][x][1] = data[z][y][x][2] = data[z][y][x][3] = d;
288                         }
289                 }
290         }
291         r_shadow_attenuationtexture = R_LoadTexture3D(r_shadow_texturepool, "attenuation", 32, 32, 32, &data[0][0][0][0], TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_ALPHA);
292         qglTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP);
293         qglTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP);
294         qglTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP);
295 }
296
297 void R_Shadow_Stage_Depth(void)
298 {
299         rmeshstate_t m;
300
301         //cl.worldmodel->numlights = min(cl.worldmodel->numlights, 1);
302         if (!r_shadow_attenuationtexture || r_shadow1.value != r_shadow_atten1 || r_shadow2.value != r_shadow_atten2 || r_shadow5.value != r_shadow_atten5)
303                 R_Shadow_MakeTextures();
304
305         memset(&m, 0, sizeof(m));
306         m.blendfunc1 = GL_ONE;
307         m.blendfunc2 = GL_ZERO;
308         R_Mesh_State(&m);
309         GL_Color(0, 0, 0, 1);
310 }
311
312 void R_Shadow_Stage_ShadowVolumes(void)
313 {
314         rmeshstate_t m;
315         memset(&m, 0, sizeof(m));
316         R_Mesh_TextureState(&m);
317         GL_Color(1, 1, 1, 1);
318         qglColorMask(0, 0, 0, 0);
319         qglDisable(GL_BLEND);
320         qglDepthMask(0);
321         qglDepthFunc(GL_LEQUAL);
322         qglClearStencil(0);
323         qglClear(GL_STENCIL_BUFFER_BIT);
324         qglEnable(GL_STENCIL_TEST);
325         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
326         qglStencilFunc(GL_ALWAYS, 0, 0xFF);
327 }
328
329 void R_Shadow_Stage_Light(void)
330 {
331         rmeshstate_t m;
332         memset(&m, 0, sizeof(m));
333         if (r_shadow6.integer)
334                 m.tex3d[0] = R_GetTexture(r_shadow_attenuationtexture);
335         R_Mesh_TextureState(&m);
336         qglEnable(GL_BLEND);
337         qglBlendFunc(GL_ONE, GL_ONE);
338         GL_Color(1, 1, 1, 1);
339         qglColorMask(1, 1, 1, 1);
340         qglDepthMask(0);
341         qglDepthFunc(GL_EQUAL);
342         qglEnable(GL_STENCIL_TEST);
343         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
344         // only draw light where this geometry was already rendered AND the
345         // stencil is 0 (non-zero means shadow)
346         qglStencilFunc(GL_EQUAL, 0, 0xFF);
347 }
348
349 void R_Shadow_Stage_Textures(void)
350 {
351         rmeshstate_t m;
352         // attempt to restore state to what Mesh_State thinks it is
353         qglDisable(GL_BLEND);
354         qglBlendFunc(GL_ONE, GL_ZERO);
355         qglDepthMask(1);
356
357         // now change to a more useful state
358         memset(&m, 0, sizeof(m));
359         m.blendfunc1 = GL_DST_COLOR;
360         m.blendfunc2 = GL_SRC_COLOR;
361         R_Mesh_State(&m);
362
363         // now hack some more
364         GL_Color(1, 1, 1, 1);
365         qglColorMask(1, 1, 1, 1);
366         qglDepthFunc(GL_EQUAL);
367         qglEnable(GL_STENCIL_TEST);
368         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
369         // only draw in lit areas
370         qglStencilFunc(GL_EQUAL, 0, 0xFF);
371 }
372
373 void R_Shadow_Stage_End(void)
374 {
375         rmeshstate_t m;
376         GL_Color(1, 1, 1, 1);
377         qglColorMask(1, 1, 1, 1);
378         qglDepthFunc(GL_LEQUAL);
379         qglDisable(GL_STENCIL_TEST);
380         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
381         qglStencilFunc(GL_ALWAYS, 0, 0xFF);
382
383         // now change to a more useful state
384         memset(&m, 0, sizeof(m));
385         m.blendfunc1 = GL_ONE;
386         m.blendfunc2 = GL_ZERO;
387         R_Mesh_State(&m);
388 }
389
390 void R_Shadow_Light(int numverts, float *normals, vec3_t relativelightorigin, float lightradius, float lightdistbias, float lightsubtract, float *lightcolor)
391 {
392         if (!r_shadow6.integer)
393         {
394                 int i;
395                 float *n, *v, *c, f, dist, temp[3], light[3], lightradius2;
396                 VectorCopy(lightcolor, light);
397                 lightradius2 = lightradius * lightradius;
398                 for (i = 0, v = varray_vertex, c = varray_color, n = normals;i < numverts;i++, v += 4, c += 4, n += 3)
399                 {
400                         VectorSubtract(relativelightorigin, v, temp);
401                         c[0] = 0;
402                         c[1] = 0;
403                         c[2] = 0;
404                         c[3] = 1;
405                         f = DotProduct(n, temp);
406                         if (f > 0)
407                         {
408                                 dist = DotProduct(temp, temp);
409                                 if (dist < lightradius2)
410                                 {
411                                         f = ((1.0f / (dist + lightdistbias)) - lightsubtract) * (f / sqrt(dist));
412                                         c[0] = f * light[0];
413                                         c[1] = f * light[1];
414                                         c[2] = f * light[2];
415                                 }
416                         }
417                 }
418         }
419         else
420         {
421                 int i;
422                 float *n, *v, *c, *t, f, temp[3], light[3], iradius, attentexbase[3];
423                 VectorScale(lightcolor, (1.0f / r_shadow3.value), light);
424                 iradius = 0.5f / lightradius;
425                 attentexbase[0] = 0.5f;
426                 attentexbase[1] = 0.5f;
427                 attentexbase[2] = 0.5f;
428                 for (i = 0, v = varray_vertex, c = varray_color, n = normals, t = varray_texcoord[0];i < numverts;i++, v += 4, c += 4, n += 3, t += 4)
429                 {
430                         VectorSubtract(v, relativelightorigin, temp);
431                         VectorMA(attentexbase, iradius, temp, t);
432                         c[0] = 0;
433                         c[1] = 0;
434                         c[2] = 0;
435                         c[3] = 1;
436                         f = DotProduct(n, temp);
437                         if (f < 0)
438                         {
439                                 f /= -sqrt(DotProduct(temp, temp));
440                                 c[0] = f * light[0];
441                                 c[1] = f * light[1];
442                                 c[2] = f * light[2];
443                         }
444                 }
445         }
446 }