]> icculus.org git repositories - divverent/darkplaces.git/blob - r_shadow.c
bmodel shadow volumes
[divverent/darkplaces.git] / r_shadow.c
1
2 #include "quakedef.h"
3
4 mempool_t *r_shadow_mempool;
5
6 int maxshadowelements;
7 int *shadowelements;
8 int maxtrianglefacinglight;
9 qbyte *trianglefacinglight;
10
11 void r_shadow_start(void)
12 {
13         // allocate vertex processing arrays
14         r_shadow_mempool = Mem_AllocPool("R_Shadow");
15         maxshadowelements = 0;
16         shadowelements = NULL;
17         maxtrianglefacinglight = 0;
18         trianglefacinglight = NULL;
19 }
20
21 void r_shadow_shutdown(void)
22 {
23         maxshadowelements = 0;
24         shadowelements = NULL;
25         maxtrianglefacinglight = 0;
26         trianglefacinglight = NULL;
27         Mem_FreePool(&r_shadow_mempool);
28 }
29
30 void r_shadow_newmap(void)
31 {
32 }
33
34 void R_Shadow_Init(void)
35 {
36         R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
37 }
38
39 void R_Shadow_Volume(int numverts, int numtris, int *elements, int *neighbors, vec3_t relativelightorigin, float projectdistance, int visiblevolume)
40 {
41         int i, *e, *n, *out, tris;
42         float *v0, *v1, *v2, dir0[3], dir1[3], temp[3], f;
43 // terminology:
44 //
45 // frontface:
46 // a triangle facing the light source
47 //
48 // backface:
49 // a triangle not facing the light source
50 //
51 // shadow volume:
52 // an extrusion of the backfaces, beginning at the original geometry and
53 // ending further from the light source than the original geometry
54 // (presumably at least as far as the light's radius, if the light has a
55 // radius at all), capped at both front and back to avoid any problems
56 //
57 // description:
58 // draws the shadow volumes of the model.
59 // requirements:
60 // vertex loations must already be in varray_vertex before use.
61 // varray_vertex must have capacity for numverts * 2.
62
63         // make sure trianglefacinglight is big enough for this volume
64         if (maxtrianglefacinglight < numtris)
65         {
66                 maxtrianglefacinglight = numtris;
67                 if (trianglefacinglight)
68                         Mem_Free(trianglefacinglight);
69                 trianglefacinglight = Mem_Alloc(r_shadow_mempool, maxtrianglefacinglight);
70         }
71
72         // make sure shadowelements is big enough for this volume
73         if (maxshadowelements < numtris * 24)
74         {
75                 maxshadowelements = numtris * 24;
76                 if (shadowelements)
77                         Mem_Free(shadowelements);
78                 shadowelements = Mem_Alloc(r_shadow_mempool, maxshadowelements * sizeof(int));
79         }
80
81         // make projected vertices
82         // by clever use of elements we'll construct the whole shadow from
83         // the unprojected vertices and these projected vertices
84         for (i = 0, v0 = varray_vertex, v1 = varray_vertex + numverts * 4;i < numverts;i++, v0 += 4, v1 += 4)
85         {
86                 VectorSubtract(v0, relativelightorigin, temp);
87                 f = projectdistance / sqrt(DotProduct(temp,temp));
88                 VectorMA(v0, f, temp, v1);
89         }
90
91         // check which triangles are facing the light
92         for (i = 0, e = elements;i < numtris;i++, e += 3)
93         {
94                 // calculate surface plane
95                 v0 = varray_vertex + e[0] * 4;
96                 v1 = varray_vertex + e[1] * 4;
97                 v2 = varray_vertex + e[2] * 4;
98                 VectorSubtract(v0, v1, dir0);
99                 VectorSubtract(v2, v1, dir1);
100                 CrossProduct(dir0, dir1, temp);
101                 // we do not need to normalize the surface normal because both sides
102                 // of the comparison use it, therefore they are both multiplied the
103                 // same amount...
104                 trianglefacinglight[i] = DotProduct(relativelightorigin, temp) >= DotProduct(v0, temp);
105         }
106
107         // output triangle elements
108         out = shadowelements;
109         tris = 0;
110
111         // check each backface for bordering frontfaces,
112         // and cast shadow polygons from those edges,
113         // also create front and back caps for shadow volume
114         for (i = 0, e = elements, n = neighbors;i < numtris;i++, e += 3, n += 3)
115         {
116                 if (!trianglefacinglight[i])
117                 {
118                         // triangle is backface and therefore casts shadow,
119                         // output front and back caps for shadow volume
120                         // front cap (with flipped winding order)
121                         out[0] = e[0];
122                         out[1] = e[2];
123                         out[2] = e[1];
124                         // rear cap
125                         out[3] = e[0] + numverts;
126                         out[4] = e[1] + numverts;
127                         out[5] = e[2] + numverts;
128                         out += 6;
129                         tris += 2;
130                         // check the edges
131                         if (n[0] < 0 || trianglefacinglight[n[0]])
132                         {
133                                 out[0] = e[0];
134                                 out[1] = e[1];
135                                 out[2] = e[1] + numverts;
136                                 out[3] = e[0];
137                                 out[4] = e[1] + numverts;
138                                 out[5] = e[0] + numverts;
139                                 out += 6;
140                                 tris += 2;
141                         }
142                         if (n[1] < 0 || trianglefacinglight[n[1]])
143                         {
144                                 out[0] = e[1];
145                                 out[1] = e[2];
146                                 out[2] = e[2] + numverts;
147                                 out[3] = e[1];
148                                 out[4] = e[2] + numverts;
149                                 out[5] = e[1] + numverts;
150                                 out += 6;
151                                 tris += 2;
152                         }
153                         if (n[2] < 0 || trianglefacinglight[n[2]])
154                         {
155                                 out[0] = e[2];
156                                 out[1] = e[0];
157                                 out[2] = e[0] + numverts;
158                                 out[3] = e[2];
159                                 out[4] = e[0] + numverts;
160                                 out[5] = e[2] + numverts;
161                                 out += 6;
162                                 tris += 2;
163                         }
164                 }
165         }
166         // draw the volume
167         if (visiblevolume)
168         {
169                 qglDisable(GL_CULL_FACE);
170                 R_Mesh_Draw(numverts * 2, tris, shadowelements);
171                 qglEnable(GL_CULL_FACE);
172         }
173         else
174         {
175                 qglColorMask(0,0,0,0);
176                 qglEnable(GL_STENCIL_TEST);
177                 // increment stencil if backface is behind depthbuffer
178                 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
179                 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
180                 R_Mesh_Draw(numverts * 2, tris, shadowelements);
181                 // decrement stencil if frontface is infront of depthbuffer
182                 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
183                 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
184                 R_Mesh_Draw(numverts * 2, tris, shadowelements);
185                 // restore to normal quake rendering
186                 qglDisable(GL_STENCIL_TEST);
187                 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
188                 qglColorMask(1,1,1,1);
189         }
190 }
191
192 void R_Shadow_VertexLight(int numverts, float *normals, vec3_t relativelightorigin, float lightradius2, float lightdistbias, float lightsubtract, float *lightcolor)
193 {
194         int i;
195         float *n, *v, *c, f, dist, temp[3];
196         // calculate vertex colors
197         for (i = 0, v = varray_vertex, c = varray_color, n = normals;i < numverts;i++, v += 4, c += 4, n += 3)
198         {
199                 VectorSubtract(relativelightorigin, v, temp);
200                 c[0] = 0;
201                 c[1] = 0;
202                 c[2] = 0;
203                 c[3] = 1;
204                 f = DotProduct(n, temp);
205                 if (f > 0)
206                 {
207                         dist = DotProduct(temp, temp);
208                         if (dist < lightradius2)
209                         {
210                                 f = ((1.0f / (dist + lightdistbias)) - lightsubtract) * (f / sqrt(dist));
211                                 c[0] = f * lightcolor[0];
212                                 c[1] = f * lightcolor[1];
213                                 c[2] = f * lightcolor[2];
214                         }
215                 }
216         }
217 }
218
219 void R_Shadow_RenderLightThroughStencil(int numverts, int numtris, int *elements, vec3_t relativelightorigin, float *normals)
220 {
221         // only draw light where this geometry was already rendered AND the
222         // stencil is 0 (non-zero means shadow)
223         qglDepthFunc(GL_EQUAL);
224         qglEnable(GL_STENCIL_TEST);
225         qglStencilFunc(GL_EQUAL, 0, 0xFF);
226         R_Mesh_Draw(numverts, numtris, elements);
227         qglDisable(GL_STENCIL_TEST);
228         qglDepthFunc(GL_LEQUAL);
229 }
230
231 void R_Shadow_ClearStencil(void)
232 {
233         qglClearStencil(0);
234         qglClear(GL_STENCIL_BUFFER_BIT);
235 }