]> icculus.org git repositories - divverent/darkplaces.git/blob - r_shadow.c
bumpscale is now adjustable (r_shadow_bumpscale) for experimenting (it's not saved...
[divverent/darkplaces.git] / r_shadow.c
1
2 #include "quakedef.h"
3 #include "r_shadow.h"
4 #include "cl_collision.h"
5
6 extern void R_Shadow_EditLights_Init(void);
7
8 #define SHADOWSTAGE_NONE 0
9 #define SHADOWSTAGE_STENCIL 1
10 #define SHADOWSTAGE_LIGHT 2
11 #define SHADOWSTAGE_ERASESTENCIL 3
12
13 int r_shadowstage = SHADOWSTAGE_NONE;
14 int r_shadow_reloadlights = false;
15
16 int r_shadow_lightingmode = 0;
17
18 mempool_t *r_shadow_mempool;
19
20 int maxshadowelements;
21 int *shadowelements;
22 int maxtrianglefacinglight;
23 qbyte *trianglefacinglight;
24
25 rtexturepool_t *r_shadow_texturepool;
26 rtexture_t *r_shadow_normalsattenuationtexture;
27 rtexture_t *r_shadow_normalscubetexture;
28 rtexture_t *r_shadow_attenuation2dtexture;
29 rtexture_t *r_shadow_blankbumptexture;
30 rtexture_t *r_shadow_blankglosstexture;
31 rtexture_t *r_shadow_blankwhitetexture;
32
33 cvar_t r_shadow_lightattenuationscale = {0, "r_shadow_lightattenuationscale", "2"};
34 cvar_t r_shadow_lightintensityscale = {0, "r_shadow_lightintensityscale", "1"};
35 cvar_t r_shadow_realtime = {0, "r_shadow_realtime", "0"};
36 cvar_t r_shadow_erasebydrawing = {0, "r_shadow_erasebydrawing", "0"};
37 cvar_t r_shadow_texture3d = {0, "r_shadow_texture3d", "0"};
38 cvar_t r_shadow_gloss = {0, "r_shadow_gloss", "1"};
39 cvar_t r_shadow_debuglight = {0, "r_shadow_debuglight", "-1"};
40 cvar_t r_shadow_scissor = {0, "r_shadow_scissor", "1"};
41 cvar_t r_shadow_bumpscale = {0, "r_shadow_bumpscale", "4"};
42
43 void R_Shadow_ClearWorldLights(void);
44 void R_Shadow_SaveWorldLights(void);
45 void R_Shadow_LoadWorldLights(void);
46 void R_Shadow_LoadLightsFile(void);
47 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void);
48
49 void r_shadow_start(void)
50 {
51         // allocate vertex processing arrays
52         r_shadow_mempool = Mem_AllocPool("R_Shadow");
53         maxshadowelements = 0;
54         shadowelements = NULL;
55         maxtrianglefacinglight = 0;
56         trianglefacinglight = NULL;
57         r_shadow_normalsattenuationtexture = NULL;
58         r_shadow_normalscubetexture = NULL;
59         r_shadow_attenuation2dtexture = NULL;
60         r_shadow_blankbumptexture = NULL;
61         r_shadow_blankglosstexture = NULL;
62         r_shadow_blankwhitetexture = NULL;
63         r_shadow_texturepool = NULL;
64         R_Shadow_ClearWorldLights();
65         r_shadow_reloadlights = true;
66 }
67
68 void r_shadow_shutdown(void)
69 {
70         R_Shadow_ClearWorldLights();
71         r_shadow_reloadlights = true;
72         r_shadow_normalsattenuationtexture = NULL;
73         r_shadow_normalscubetexture = NULL;
74         r_shadow_attenuation2dtexture = NULL;
75         r_shadow_blankbumptexture = NULL;
76         r_shadow_blankglosstexture = NULL;
77         r_shadow_blankwhitetexture = NULL;
78         R_FreeTexturePool(&r_shadow_texturepool);
79         maxshadowelements = 0;
80         shadowelements = NULL;
81         maxtrianglefacinglight = 0;
82         trianglefacinglight = NULL;
83         Mem_FreePool(&r_shadow_mempool);
84 }
85
86 void r_shadow_newmap(void)
87 {
88         R_Shadow_ClearWorldLights();
89         r_shadow_reloadlights = true;
90 }
91
92 void R_Shadow_Init(void)
93 {
94         Cvar_RegisterVariable(&r_shadow_lightattenuationscale);
95         Cvar_RegisterVariable(&r_shadow_lightintensityscale);
96         Cvar_RegisterVariable(&r_shadow_realtime);
97         Cvar_RegisterVariable(&r_shadow_texture3d);
98         Cvar_RegisterVariable(&r_shadow_gloss);
99         Cvar_RegisterVariable(&r_shadow_debuglight);
100         Cvar_RegisterVariable(&r_shadow_erasebydrawing);
101         Cvar_RegisterVariable(&r_shadow_scissor);
102         Cvar_RegisterVariable(&r_shadow_bumpscale);
103         R_Shadow_EditLights_Init();
104         R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
105 }
106
107 void R_Shadow_ProjectVertices(const float *in, float *out, int numverts, const float *relativelightorigin, float projectdistance)
108 {
109         int i;
110         for (i = 0;i < numverts;i++, in += 4, out += 4)
111         {
112 #if 1
113                 out[0] = in[0] + 1000000.0f * (in[0] - relativelightorigin[0]);
114                 out[1] = in[1] + 1000000.0f * (in[1] - relativelightorigin[1]);
115                 out[2] = in[2] + 1000000.0f * (in[2] - relativelightorigin[2]);
116 #elif 0
117                 VectorSubtract(in, relativelightorigin, temp);
118                 f = lightradius / sqrt(DotProduct(temp,temp));
119                 if (f < 1)
120                         f = 1;
121                 VectorMA(relativelightorigin, f, temp, out);
122 #else
123                 VectorSubtract(in, relativelightorigin, temp);
124                 f = projectdistance / sqrt(DotProduct(temp,temp));
125                 VectorMA(in, f, temp, out);
126 #endif
127         }
128 }
129
130 void R_Shadow_MakeTriangleShadowFlags(const int *elements, const float *vertex, int numtris, qbyte *trianglefacinglight, const float *relativelightorigin, float lightradius)
131 {
132         int i;
133         const float *v0, *v1, *v2;
134         for (i = 0;i < numtris;i++, elements += 3)
135         {
136                 // calculate triangle facing flag
137                 v0 = vertex + elements[0] * 4;
138                 v1 = vertex + elements[1] * 4;
139                 v2 = vertex + elements[2] * 4;
140                 // we do not need to normalize the surface normal because both sides
141                 // of the comparison use it, therefore they are both multiplied the
142                 // same amount...  furthermore the subtract can be done on the
143                 // vectors, saving a little bit of math in the dotproducts
144 #if 0
145                 // fast version
146                 // subtracts v1 from v0 and v2, combined into a crossproduct,
147                 // combined with a dotproduct of the light location relative to the
148                 // first point of the triangle (any point works, since the triangle
149                 // is obviously flat), and finally a comparison to determine if the
150                 // light is infront of the triangle (the goal of this statement)
151                 trianglefacinglight[i] =
152                    (relativelightorigin[0] - v0[0]) * ((v0[1] - v1[1]) * (v2[2] - v1[2]) - (v0[2] - v1[2]) * (v2[1] - v1[1]))
153                  + (relativelightorigin[1] - v0[1]) * ((v0[2] - v1[2]) * (v2[0] - v1[0]) - (v0[0] - v1[0]) * (v2[2] - v1[2]))
154                  + (relativelightorigin[2] - v0[2]) * ((v0[0] - v1[0]) * (v2[1] - v1[1]) - (v0[1] - v1[1]) * (v2[0] - v1[0])) > 0;
155 #else
156                 // readable version
157                 {
158                 float dir0[3], dir1[3], temp[3], f;
159
160                 // calculate two mostly perpendicular edge directions
161                 VectorSubtract(v0, v1, dir0);
162                 VectorSubtract(v2, v1, dir1);
163
164                 // we have two edge directions, we can calculate a third vector from
165                 // them, which is the direction of the surface normal (it's magnitude
166                 // is not 1 however)
167                 CrossProduct(dir0, dir1, temp);
168
169                 // this is entirely unnecessary, but kept for clarity
170                 //VectorNormalize(temp);
171
172                 // compare distance of light along normal, with distance of any point
173                 // of the triangle along the same normal (the triangle is planar,
174                 // I.E. flat, so all points give the same answer)
175                 // the normal is not normalized because it is used on both sides of
176                 // the comparison, so it's magnitude does not matter
177                 //trianglefacinglight[i] = DotProduct(relativelightorigin, temp) >= DotProduct(v0, temp);
178                 f = DotProduct(relativelightorigin, temp) - DotProduct(v0, temp);
179                 trianglefacinglight[i] = f > 0 && f < lightradius * sqrt(DotProduct(temp, temp));
180                 }
181 #endif
182         }
183 }
184
185 int R_Shadow_BuildShadowVolumeTriangles(const int *elements, const int *neighbors, int numtris, int numverts, const qbyte *trianglefacinglight, int *out)
186 {
187         int i, tris;
188         // check each frontface for bordering backfaces,
189         // and cast shadow polygons from those edges,
190         // also create front and back caps for shadow volume
191         tris = 0;
192         for (i = 0;i < numtris;i++, elements += 3, neighbors += 3)
193         {
194                 if (trianglefacinglight[i])
195                 {
196                         // triangle is frontface and therefore casts shadow,
197                         // output front and back caps for shadow volume
198                         // front cap
199                         out[0] = elements[0];
200                         out[1] = elements[1];
201                         out[2] = elements[2];
202                         // rear cap (with flipped winding order)
203                         out[3] = elements[0] + numverts;
204                         out[4] = elements[2] + numverts;
205                         out[5] = elements[1] + numverts;
206                         out += 6;
207                         tris += 2;
208                         // check the edges
209                         if (neighbors[0] < 0 || !trianglefacinglight[neighbors[0]])
210                         {
211                                 out[0] = elements[1];
212                                 out[1] = elements[0];
213                                 out[2] = elements[0] + numverts;
214                                 out[3] = elements[1];
215                                 out[4] = elements[0] + numverts;
216                                 out[5] = elements[1] + numverts;
217                                 out += 6;
218                                 tris += 2;
219                         }
220                         if (neighbors[1] < 0 || !trianglefacinglight[neighbors[1]])
221                         {
222                                 out[0] = elements[2];
223                                 out[1] = elements[1];
224                                 out[2] = elements[1] + numverts;
225                                 out[3] = elements[2];
226                                 out[4] = elements[1] + numverts;
227                                 out[5] = elements[2] + numverts;
228                                 out += 6;
229                                 tris += 2;
230                         }
231                         if (neighbors[2] < 0 || !trianglefacinglight[neighbors[2]])
232                         {
233                                 out[0] = elements[0];
234                                 out[1] = elements[2];
235                                 out[2] = elements[2] + numverts;
236                                 out[3] = elements[0];
237                                 out[4] = elements[2] + numverts;
238                                 out[5] = elements[0] + numverts;
239                                 out += 6;
240                                 tris += 2;
241                         }
242                 }
243         }
244         return tris;
245 }
246
247 void R_Shadow_ResizeTriangleFacingLight(int numtris)
248 {
249         // make sure trianglefacinglight is big enough for this volume
250         if (maxtrianglefacinglight < numtris)
251         {
252                 maxtrianglefacinglight = numtris;
253                 if (trianglefacinglight)
254                         Mem_Free(trianglefacinglight);
255                 trianglefacinglight = Mem_Alloc(r_shadow_mempool, maxtrianglefacinglight);
256         }
257 }
258
259 void R_Shadow_ResizeShadowElements(int numtris)
260 {
261         // make sure shadowelements is big enough for this volume
262         if (maxshadowelements < numtris * 24)
263         {
264                 maxshadowelements = numtris * 24;
265                 if (shadowelements)
266                         Mem_Free(shadowelements);
267                 shadowelements = Mem_Alloc(r_shadow_mempool, maxshadowelements * sizeof(int));
268         }
269 }
270
271 void R_Shadow_Volume(int numverts, int numtris, int *elements, int *neighbors, vec3_t relativelightorigin, float lightradius, float projectdistance)
272 {
273         int tris;
274         if (projectdistance < 0.1)
275         {
276                 Con_Printf("R_Shadow_Volume: projectdistance %f\n");
277                 return;
278         }
279 // terminology:
280 //
281 // frontface:
282 // a triangle facing the light source
283 //
284 // backface:
285 // a triangle not facing the light source
286 //
287 // shadow volume:
288 // an extrusion of the frontfaces, beginning at the original geometry and
289 // ending further from the light source than the original geometry
290 // (presumably at least as far as the light's radius, if the light has a
291 // radius at all), capped at both front and back to avoid any problems
292 //
293 // description:
294 // draws the shadow volumes of the model.
295 // requirements:
296 // vertex locations must already be in varray_vertex before use.
297 // varray_vertex must have capacity for numverts * 2.
298
299         // make sure trianglefacinglight is big enough for this volume
300         if (maxtrianglefacinglight < numtris)
301                 R_Shadow_ResizeTriangleFacingLight(numtris);
302
303         // make sure shadowelements is big enough for this volume
304         if (maxshadowelements < numtris * 24)
305                 R_Shadow_ResizeShadowElements(numtris);
306
307         // generate projected vertices
308         // by clever use of elements we'll construct the whole shadow from
309         // the unprojected vertices and these projected vertices
310         R_Shadow_ProjectVertices(varray_vertex, varray_vertex + numverts * 4, numverts, relativelightorigin, projectdistance);
311
312         // check which triangles are facing the light
313         R_Shadow_MakeTriangleShadowFlags(elements, varray_vertex, numtris, trianglefacinglight, relativelightorigin, lightradius);
314
315         // output triangle elements
316         tris = R_Shadow_BuildShadowVolumeTriangles(elements, neighbors, numtris, numverts, trianglefacinglight, shadowelements);
317         R_Shadow_RenderVolume(numverts * 2, tris, shadowelements);
318 }
319
320 void R_Shadow_RenderVolume(int numverts, int numtris, int *elements)
321 {
322         if (!numverts || !numtris)
323                 return;
324         if (r_shadowstage == SHADOWSTAGE_STENCIL)
325         {
326                 // increment stencil if backface is behind depthbuffer
327                 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
328                 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
329                 R_Mesh_Draw(numverts, numtris, elements);
330                 // decrement stencil if frontface is behind depthbuffer
331                 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
332                 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
333         }
334         R_Mesh_Draw(numverts, numtris, elements);
335 }
336
337 void R_Shadow_RenderShadowMeshVolume(shadowmesh_t *firstmesh)
338 {
339         shadowmesh_t *mesh;
340         if (r_shadowstage == SHADOWSTAGE_STENCIL)
341         {
342                 // increment stencil if backface is behind depthbuffer
343                 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
344                 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
345                 for (mesh = firstmesh;mesh;mesh = mesh->next)
346                 {
347                         R_Mesh_ResizeCheck(mesh->numverts);
348                         memcpy(varray_vertex, mesh->verts, mesh->numverts * sizeof(float[4]));
349                         R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->elements);
350                 }
351                 // decrement stencil if frontface is behind depthbuffer
352                 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
353                 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
354         }
355         for (mesh = firstmesh;mesh;mesh = mesh->next)
356         {
357                 R_Mesh_ResizeCheck(mesh->numverts);
358                 memcpy(varray_vertex, mesh->verts, mesh->numverts * sizeof(float[4]));
359                 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->elements);
360         }
361 }
362
363 float r_shadow_atten1;
364 #define ATTEN3DSIZE 64
365 static void R_Shadow_Make3DTextures(void)
366 {
367         int x, y, z;
368         float v[3], intensity, ilen, bordercolor[4];
369         qbyte *data;
370         if (r_shadow_texture3d.integer != 1 || !gl_texture3d)
371                 return;
372         data = Mem_Alloc(tempmempool, ATTEN3DSIZE * ATTEN3DSIZE * ATTEN3DSIZE * 4);
373         for (z = 0;z < ATTEN3DSIZE;z++)
374         {
375                 for (y = 0;y < ATTEN3DSIZE;y++)
376                 {
377                         for (x = 0;x < ATTEN3DSIZE;x++)
378                         {
379                                 v[0] = (x + 0.5f) * (2.0f / (float) ATTEN3DSIZE) - 1.0f;
380                                 v[1] = (y + 0.5f) * (2.0f / (float) ATTEN3DSIZE) - 1.0f;
381                                 v[2] = (z + 0.5f) * (2.0f / (float) ATTEN3DSIZE) - 1.0f;
382                                 intensity = 1.0f - sqrt(DotProduct(v, v));
383                                 if (intensity > 0)
384                                         intensity *= intensity;
385                                 ilen = 127.0f * bound(0, intensity * r_shadow_atten1, 1) / sqrt(DotProduct(v, v));
386                                 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+0] = 128.0f + ilen * v[0];
387                                 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+1] = 128.0f + ilen * v[1];
388                                 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+2] = 128.0f + ilen * v[2];
389                                 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+3] = 255;
390                         }
391                 }
392         }
393         r_shadow_normalsattenuationtexture = R_LoadTexture3D(r_shadow_texturepool, "normalsattenuation", ATTEN3DSIZE, ATTEN3DSIZE, ATTEN3DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALWAYSPRECACHE, NULL);
394         bordercolor[0] = 0.5f;
395         bordercolor[1] = 0.5f;
396         bordercolor[2] = 0.5f;
397         bordercolor[3] = 1.0f;
398         qglTexParameterfv(GL_TEXTURE_3D, GL_TEXTURE_BORDER_COLOR, bordercolor);
399         Mem_Free(data);
400 }
401
402 static void R_Shadow_MakeTextures(void)
403 {
404         int x, y, d, side;
405         float v[3], s, t, intensity;
406         qbyte *data;
407         data = Mem_Alloc(tempmempool, 6*128*128*4);
408         R_FreeTexturePool(&r_shadow_texturepool);
409         r_shadow_texturepool = R_AllocTexturePool();
410         r_shadow_atten1 = r_shadow_lightattenuationscale.value;
411         data[0] = 128;
412         data[1] = 128;
413         data[2] = 255;
414         data[3] = 255;
415         r_shadow_blankbumptexture = R_LoadTexture2D(r_shadow_texturepool, "blankbump", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
416         data[0] = 255;
417         data[1] = 255;
418         data[2] = 255;
419         data[3] = 255;
420         r_shadow_blankglosstexture = R_LoadTexture2D(r_shadow_texturepool, "blankgloss", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
421         data[0] = 255;
422         data[1] = 255;
423         data[2] = 255;
424         data[3] = 255;
425         r_shadow_blankwhitetexture = R_LoadTexture2D(r_shadow_texturepool, "blankwhite", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
426         for (side = 0;side < 6;side++)
427         {
428                 for (y = 0;y < 128;y++)
429                 {
430                         for (x = 0;x < 128;x++)
431                         {
432                                 s = (x + 0.5f) * (2.0f / 128.0f) - 1.0f;
433                                 t = (y + 0.5f) * (2.0f / 128.0f) - 1.0f;
434                                 switch(side)
435                                 {
436                                 case 0:
437                                         v[0] = 1;
438                                         v[1] = -t;
439                                         v[2] = -s;
440                                         break;
441                                 case 1:
442                                         v[0] = -1;
443                                         v[1] = -t;
444                                         v[2] = s;
445                                         break;
446                                 case 2:
447                                         v[0] = s;
448                                         v[1] = 1;
449                                         v[2] = t;
450                                         break;
451                                 case 3:
452                                         v[0] = s;
453                                         v[1] = -1;
454                                         v[2] = -t;
455                                         break;
456                                 case 4:
457                                         v[0] = s;
458                                         v[1] = -t;
459                                         v[2] = 1;
460                                         break;
461                                 case 5:
462                                         v[0] = -s;
463                                         v[1] = -t;
464                                         v[2] = -1;
465                                         break;
466                                 }
467                                 intensity = 127.0f / sqrt(DotProduct(v, v));
468                                 data[((side*128+y)*128+x)*4+0] = 128.0f + intensity * v[0];
469                                 data[((side*128+y)*128+x)*4+1] = 128.0f + intensity * v[1];
470                                 data[((side*128+y)*128+x)*4+2] = 128.0f + intensity * v[2];
471                                 data[((side*128+y)*128+x)*4+3] = 255;
472                         }
473                 }
474         }
475         r_shadow_normalscubetexture = R_LoadTextureCubeMap(r_shadow_texturepool, "normalscube", 128, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP, NULL);
476         for (y = 0;y < 128;y++)
477         {
478                 for (x = 0;x < 128;x++)
479                 {
480                         v[0] = (x + 0.5f) * (2.0f / 128.0f) - 1.0f;
481                         v[1] = (y + 0.5f) * (2.0f / 128.0f) - 1.0f;
482                         v[2] = 0;
483                         intensity = 1.0f - sqrt(DotProduct(v, v));
484                         if (intensity > 0)
485                                 intensity *= intensity;
486                         intensity = bound(0, intensity * r_shadow_atten1 * 256.0f, 255.0f);
487                         d = bound(0, intensity, 255);
488                         data[((0*128+y)*128+x)*4+0] = d;
489                         data[((0*128+y)*128+x)*4+1] = d;
490                         data[((0*128+y)*128+x)*4+2] = d;
491                         data[((0*128+y)*128+x)*4+3] = d;
492                 }
493         }
494         r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", 128, 128, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA | TEXF_MIPMAP, NULL);
495         Mem_Free(data);
496         R_Shadow_Make3DTextures();
497 }
498
499 void R_Shadow_Stage_Begin(void)
500 {
501         rmeshstate_t m;
502
503         if (r_shadow_texture3d.integer == 1 && !gl_texture3d)
504         {
505                 Con_Printf("3D texture support not detected, falling back on slower 2D + 1D + normalization lighting\n");
506                 Cvar_SetValueQuick(&r_shadow_texture3d, 0);
507         }
508         //cl.worldmodel->numlights = min(cl.worldmodel->numlights, 1);
509         if (!r_shadow_attenuation2dtexture
510          || (r_shadow_texture3d.integer == 1 && !r_shadow_normalsattenuationtexture)
511          || r_shadow_lightattenuationscale.value != r_shadow_atten1)
512                 R_Shadow_MakeTextures();
513         if (r_shadow_reloadlights && cl.worldmodel)
514         {
515                 r_shadow_reloadlights = false;
516                 R_Shadow_LoadWorldLights();
517                 if (r_shadow_worldlightchain == NULL)
518                 {
519                         R_Shadow_LoadLightsFile();
520                         if (r_shadow_worldlightchain == NULL)
521                                 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
522                 }
523         }
524
525         memset(&m, 0, sizeof(m));
526         m.blendfunc1 = GL_ONE;
527         m.blendfunc2 = GL_ZERO;
528         R_Mesh_State(&m);
529         GL_Color(0, 0, 0, 1);
530         r_shadowstage = SHADOWSTAGE_NONE;
531 }
532
533 void R_Shadow_Stage_ShadowVolumes(void)
534 {
535         rmeshstate_t m;
536         memset(&m, 0, sizeof(m));
537         R_Mesh_TextureState(&m);
538         GL_Color(1, 1, 1, 1);
539         qglColorMask(0, 0, 0, 0);
540         qglDisable(GL_BLEND);
541         qglDepthMask(0);
542         qglDepthFunc(GL_LESS);
543         qglEnable(GL_STENCIL_TEST);
544         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
545         qglStencilFunc(GL_ALWAYS, 0, 0xFF);
546         qglEnable(GL_CULL_FACE);
547         qglEnable(GL_DEPTH_TEST);
548         r_shadowstage = SHADOWSTAGE_STENCIL;
549         if (!r_shadow_erasebydrawing.integer)
550                 qglClear(GL_STENCIL_BUFFER_BIT);
551 }
552
553 void R_Shadow_Stage_Light(void)
554 {
555         rmeshstate_t m;
556         memset(&m, 0, sizeof(m));
557         R_Mesh_TextureState(&m);
558         qglActiveTexture(GL_TEXTURE0_ARB);
559
560         qglEnable(GL_BLEND);
561         qglBlendFunc(GL_ONE, GL_ONE);
562         GL_Color(1, 1, 1, 1);
563         qglColorMask(1, 1, 1, 1);
564         qglDepthMask(0);
565         qglDepthFunc(GL_EQUAL);
566         qglEnable(GL_STENCIL_TEST);
567         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
568         // only draw light where this geometry was already rendered AND the
569         // stencil is 0 (non-zero means shadow)
570         qglStencilFunc(GL_EQUAL, 0, 0xFF);
571         qglEnable(GL_CULL_FACE);
572         qglEnable(GL_DEPTH_TEST);
573         r_shadowstage = SHADOWSTAGE_LIGHT;
574 }
575
576 int R_Shadow_Stage_EraseShadowVolumes(void)
577 {
578         if (r_shadow_erasebydrawing.integer)
579         {
580                 rmeshstate_t m;
581                 memset(&m, 0, sizeof(m));
582                 R_Mesh_TextureState(&m);
583                 GL_Color(1, 1, 1, 1);
584                 qglColorMask(0, 0, 0, 0);
585                 qglDisable(GL_BLEND);
586                 qglDepthMask(0);
587                 qglDepthFunc(GL_LESS);
588                 qglEnable(GL_STENCIL_TEST);
589                 qglStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);
590                 qglStencilFunc(GL_ALWAYS, 0, 0xFF);
591                 qglDisable(GL_CULL_FACE);
592                 qglDisable(GL_DEPTH_TEST);
593                 r_shadowstage = SHADOWSTAGE_ERASESTENCIL;
594                 return true;
595         }
596         else
597                 return false;
598 }
599
600 void R_Shadow_Stage_End(void)
601 {
602         rmeshstate_t m;
603         // attempt to restore state to what Mesh_State thinks it is
604         qglDisable(GL_BLEND);
605         qglBlendFunc(GL_ONE, GL_ZERO);
606         qglDepthMask(1);
607         // now restore the rest of the state to normal
608         GL_Color(1, 1, 1, 1);
609         qglColorMask(1, 1, 1, 1);
610         qglDisable(GL_SCISSOR_TEST);
611         qglDepthFunc(GL_LEQUAL);
612         qglDisable(GL_STENCIL_TEST);
613         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
614         qglStencilFunc(GL_ALWAYS, 0, 0xFF);
615         qglEnable(GL_CULL_FACE);
616         qglEnable(GL_DEPTH_TEST);
617         // force mesh state to reset by using various combinations of features
618         memset(&m, 0, sizeof(m));
619         m.blendfunc1 = GL_SRC_ALPHA;
620         m.blendfunc2 = GL_ONE_MINUS_SRC_ALPHA;
621         R_Mesh_State(&m);
622         m.blendfunc1 = GL_ONE;
623         m.blendfunc2 = GL_ZERO;
624         R_Mesh_State(&m);
625         r_shadowstage = SHADOWSTAGE_NONE;
626 }
627
628 int R_Shadow_ScissorForBBoxAndSphere(const float *mins, const float *maxs, const float *origin, float radius)
629 {
630         int i, ix1, iy1, ix2, iy2;
631         float x1, y1, x2, y2, x, y;
632         vec3_t smins, smaxs;
633         vec4_t v, v2;
634         if (!r_shadow_scissor.integer)
635                 return false;
636         // if view is inside the box, just say yes it's visible
637         if (r_origin[0] >= mins[0] && r_origin[0] <= maxs[0]
638          && r_origin[1] >= mins[1] && r_origin[1] <= maxs[1]
639          && r_origin[2] >= mins[2] && r_origin[2] <= maxs[2])
640         {
641                 qglDisable(GL_SCISSOR_TEST);
642                 return false;
643         }
644         VectorSubtract(r_origin, origin, v);
645         if (DotProduct(v, v) < radius * radius)
646         {
647                 qglDisable(GL_SCISSOR_TEST);
648                 return false;
649         }
650         // create viewspace bbox
651         for (i = 0;i < 8;i++)
652         {
653                 v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_origin[0];
654                 v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_origin[1];
655                 v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_origin[2];
656                 v2[0] = DotProduct(v, vright);
657                 v2[1] = DotProduct(v, vup);
658                 v2[2] = DotProduct(v, vpn);
659                 if (i)
660                 {
661                         if (smins[0] > v2[0]) smins[0] = v2[0];
662                         if (smaxs[0] < v2[0]) smaxs[0] = v2[0];
663                         if (smins[1] > v2[1]) smins[1] = v2[1];
664                         if (smaxs[1] < v2[1]) smaxs[1] = v2[1];
665                         if (smins[2] > v2[2]) smins[2] = v2[2];
666                         if (smaxs[2] < v2[2]) smaxs[2] = v2[2];
667                 }
668                 else
669                 {
670                         smins[0] = smaxs[0] = v2[0];
671                         smins[1] = smaxs[1] = v2[1];
672                         smins[2] = smaxs[2] = v2[2];
673                 }
674         }
675         // now we have a bbox in viewspace
676         // clip it to the viewspace version of the sphere
677         v[0] = origin[0] - r_origin[0];
678         v[1] = origin[1] - r_origin[1];
679         v[2] = origin[2] - r_origin[2];
680         v2[0] = DotProduct(v, vright);
681         v2[1] = DotProduct(v, vup);
682         v2[2] = DotProduct(v, vpn);
683         if (smins[0] < v2[0] - radius) smins[0] = v2[0] - radius;
684         if (smaxs[0] < v2[0] - radius) smaxs[0] = v2[0] + radius;
685         if (smins[1] < v2[1] - radius) smins[1] = v2[1] - radius;
686         if (smaxs[1] < v2[1] - radius) smaxs[1] = v2[1] + radius;
687         if (smins[2] < v2[2] - radius) smins[2] = v2[2] - radius;
688         if (smaxs[2] < v2[2] - radius) smaxs[2] = v2[2] + radius;
689         // clip it to the view plane
690         if (smins[2] < 1)
691                 smins[2] = 1;
692         // return true if that culled the box
693         if (smins[2] >= smaxs[2])
694                 return true;
695         // ok some of it is infront of the view, transform each corner back to
696         // worldspace and then to screenspace and make screen rect
697         for (i = 0;i < 8;i++)
698         {
699                 v2[0] = (i & 1) ? smins[0] : smaxs[0];
700                 v2[1] = (i & 2) ? smins[1] : smaxs[1];
701                 v2[2] = (i & 4) ? smins[2] : smaxs[2];
702                 v[0] = v2[0] * vright[0] + v2[1] * vup[0] + v2[2] * vpn[0] + r_origin[0];
703                 v[1] = v2[0] * vright[1] + v2[1] * vup[1] + v2[2] * vpn[1] + r_origin[1];
704                 v[2] = v2[0] * vright[2] + v2[1] * vup[2] + v2[2] * vpn[2] + r_origin[2];
705                 v[3] = 1.0f;
706                 GL_TransformToScreen(v, v2);
707                 //Con_Printf("%.3f %.3f %.3f %.3f transformed to %.3f %.3f %.3f %.3f\n", v[0], v[1], v[2], v[3], v2[0], v2[1], v2[2], v2[3]);
708                 x = v2[0];
709                 y = v2[1];
710                 if (i)
711                 {
712                         if (x1 > x) x1 = x;
713                         if (x2 < x) x2 = x;
714                         if (y1 > y) y1 = y;
715                         if (y2 < y) y2 = y;
716                 }
717                 else
718                 {
719                         x1 = x2 = x;
720                         y1 = y2 = y;
721                 }
722         }
723         /*
724         // this code doesn't handle boxes with any points behind view properly
725         x1 = 1000;x2 = -1000;
726         y1 = 1000;y2 = -1000;
727         for (i = 0;i < 8;i++)
728         {
729                 v[0] = (i & 1) ? mins[0] : maxs[0];
730                 v[1] = (i & 2) ? mins[1] : maxs[1];
731                 v[2] = (i & 4) ? mins[2] : maxs[2];
732                 v[3] = 1.0f;
733                 GL_TransformToScreen(v, v2);
734                 //Con_Printf("%.3f %.3f %.3f %.3f transformed to %.3f %.3f %.3f %.3f\n", v[0], v[1], v[2], v[3], v2[0], v2[1], v2[2], v2[3]);
735                 if (v2[2] > 0)
736                 {
737                         x = v2[0];
738                         y = v2[1];
739
740                         if (x1 > x) x1 = x;
741                         if (x2 < x) x2 = x;
742                         if (y1 > y) y1 = y;
743                         if (y2 < y) y2 = y;
744                 }
745         }
746         */
747         ix1 = x1 - 1.0f;
748         iy1 = y1 - 1.0f;
749         ix2 = x2 + 1.0f;
750         iy2 = y2 + 1.0f;
751         //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
752         if (ix1 < r_refdef.x) ix1 = r_refdef.x;
753         if (iy1 < r_refdef.y) iy1 = r_refdef.y;
754         if (ix2 > r_refdef.x + r_refdef.width) ix2 = r_refdef.x + r_refdef.width;
755         if (iy2 > r_refdef.y + r_refdef.height) iy2 = r_refdef.y + r_refdef.height;
756         if (ix2 <= ix1 || iy2 <= iy1)
757                 return true;
758         // set up the scissor rectangle
759         qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
760         qglEnable(GL_SCISSOR_TEST);
761         return false;
762 }
763
764 void R_Shadow_GenTexCoords_Attenuation2D1D(float *out2d, float *out1d, int numverts, const float *vertex, const float *svectors, const float *tvectors, const float *normals, const vec3_t relativelightorigin, float lightradius)
765 {
766         int i;
767         float lightvec[3], iradius;
768         iradius = 0.5f / lightradius;
769         for (i = 0;i < numverts;i++, vertex += 4, svectors += 4, tvectors += 4, normals += 4, out2d += 4, out1d += 4)
770         {
771                 VectorSubtract(vertex, relativelightorigin, lightvec);
772                 out2d[0] = 0.5f + DotProduct(svectors, lightvec) * iradius;
773                 out2d[1] = 0.5f + DotProduct(tvectors, lightvec) * iradius;
774                 out2d[2] = 0;
775                 out1d[0] = 0.5f + DotProduct(normals, lightvec) * iradius;
776                 out1d[1] = 0.5f;
777                 out1d[2] = 0;
778         }
779 }
780
781 void R_Shadow_GenTexCoords_Diffuse_Attenuation3D(float *out, int numverts, const float *vertex, const float *svectors, const float *tvectors, const float *normals, const vec3_t relativelightorigin, float lightradius)
782 {
783         int i;
784         float lightvec[3], iradius;
785         iradius = 0.5f / lightradius;
786         for (i = 0;i < numverts;i++, vertex += 4, svectors += 4, tvectors += 4, normals += 4, out += 4)
787         {
788                 VectorSubtract(vertex, relativelightorigin, lightvec);
789                 out[0] = 0.5f + DotProduct(svectors, lightvec) * iradius;
790                 out[1] = 0.5f + DotProduct(tvectors, lightvec) * iradius;
791                 out[2] = 0.5f + DotProduct(normals, lightvec) * iradius;
792         }
793 }
794
795 void R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(float *out, int numverts, const float *vertex, const float *svectors, const float *tvectors, const float *normals, const vec3_t relativelightorigin)
796 {
797         int i;
798         float lightdir[3];
799         for (i = 0;i < numverts;i++, vertex += 4, svectors += 4, tvectors += 4, normals += 4, out += 4)
800         {
801                 VectorSubtract(vertex, relativelightorigin, lightdir);
802                 // the cubemap normalizes this for us
803                 out[0] = DotProduct(svectors, lightdir);
804                 out[1] = DotProduct(tvectors, lightdir);
805                 out[2] = DotProduct(normals, lightdir);
806         }
807 }
808
809 void R_Shadow_GenTexCoords_Specular_Attenuation3D(float *out, int numverts, const float *vertex, const float *svectors, const float *tvectors, const float *normals, const vec3_t relativelightorigin, const vec3_t relativeeyeorigin, float lightradius)
810 {
811         int i;
812         float lightdir[3], eyedir[3], halfdir[3], lightdirlen, iradius;
813         iradius = 0.5f / lightradius;
814         for (i = 0;i < numverts;i++, vertex += 4, svectors += 4, tvectors += 4, normals += 4, out += 4)
815         {
816                 VectorSubtract(vertex, relativelightorigin, lightdir);
817                 // this is used later to make the attenuation correct
818                 lightdirlen = sqrt(DotProduct(lightdir, lightdir)) * iradius;
819                 VectorNormalizeFast(lightdir);
820                 VectorSubtract(vertex, relativeeyeorigin, eyedir);
821                 VectorNormalizeFast(eyedir);
822                 VectorAdd(lightdir, eyedir, halfdir);
823                 VectorNormalizeFast(halfdir);
824                 out[0] = 0.5f + DotProduct(svectors, halfdir) * lightdirlen;
825                 out[1] = 0.5f + DotProduct(tvectors, halfdir) * lightdirlen;
826                 out[2] = 0.5f + DotProduct(normals, halfdir) * lightdirlen;
827         }
828 }
829
830 void R_Shadow_GenTexCoords_Specular_NormalCubeMap(float *out, int numverts, const float *vertex, const float *svectors, const float *tvectors, const float *normals, const vec3_t relativelightorigin, const vec3_t relativeeyeorigin)
831 {
832         int i;
833         float lightdir[3], eyedir[3], halfdir[3];
834         for (i = 0;i < numverts;i++, vertex += 4, svectors += 4, tvectors += 4, normals += 4, out += 4)
835         {
836                 VectorSubtract(vertex, relativelightorigin, lightdir);
837                 VectorNormalizeFast(lightdir);
838                 VectorSubtract(vertex, relativeeyeorigin, eyedir);
839                 VectorNormalizeFast(eyedir);
840                 VectorAdd(lightdir, eyedir, halfdir);
841                 // the cubemap normalizes this for us
842                 out[0] = DotProduct(svectors, halfdir);
843                 out[1] = DotProduct(tvectors, halfdir);
844                 out[2] = DotProduct(normals, halfdir);
845         }
846 }
847
848 void R_Shadow_GenTexCoords_LightCubeMap(float *out, int numverts, const float *vertex, const vec3_t relativelightorigin)
849 {
850         int i;
851         // FIXME: this needs to be written
852         // this code assumes the vertices are in worldspace (a false assumption)
853         for (i = 0;i < numverts;i++, vertex += 4, out += 4)
854                 VectorSubtract(vertex, relativelightorigin, out);
855 }
856
857 void R_Shadow_DiffuseLighting(int numverts, int numtriangles, const int *elements, const float *svectors, const float *tvectors, const float *normals, const float *texcoords, const float *relativelightorigin, float lightradius, const float *lightcolor, rtexture_t *basetexture, rtexture_t *bumptexture, rtexture_t *lightcubemap)
858 {
859         int renders, mult;
860         float scale, colorscale;
861         rmeshstate_t m;
862         memset(&m, 0, sizeof(m));
863         if (!bumptexture)
864                 bumptexture = r_shadow_blankbumptexture;
865         // colorscale accounts for how much we multiply the brightness during combine
866         // mult is how many times the final pass of the lighting will be
867         // performed to get more brightness than otherwise possible
868         // limit mult to 64 for sanity sake
869         if (r_shadow_texture3d.integer)
870         {
871                 if (r_textureunits.integer >= 4 && !lightcubemap)
872                 {
873                         // 4 texture 3D combine path, one pass, no light cubemap support
874                         m.tex[0] = R_GetTexture(bumptexture);
875                         m.tex3d[1] = R_GetTexture(r_shadow_normalsattenuationtexture);
876                         m.tex[2] = R_GetTexture(basetexture);
877                         m.tex[3] = R_GetTexture(r_shadow_blankwhitetexture);
878                         m.texcombinergb[0] = GL_REPLACE;
879                         m.texcombinergb[1] = GL_DOT3_RGB_ARB;
880                         m.texcombinergb[2] = GL_MODULATE;
881                         m.texcombinergb[3] = GL_MODULATE;
882                         m.texrgbscale[1] = 1;
883                         m.texrgbscale[3] = 4;
884                         R_Mesh_TextureState(&m);
885                         memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
886                         memcpy(varray_texcoord[2], texcoords, numverts * sizeof(float[4]));
887                         R_Shadow_GenTexCoords_Diffuse_Attenuation3D(varray_texcoord[1], numverts, varray_vertex, svectors, tvectors, normals, relativelightorigin, lightradius);
888                         qglActiveTexture(GL_TEXTURE3_ARB);
889                         qglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PRIMARY_COLOR_ARB);
890                         colorscale = r_colorscale * 0.25f * r_shadow_lightintensityscale.value;
891                         for (mult = 1, scale = ixtable[mult];mult < 64 && (lightcolor[0] * scale * colorscale > 1 || lightcolor[1] * scale * colorscale > 1 || lightcolor[2] * scale * colorscale > 1);mult++, scale = ixtable[mult]);
892                         colorscale *= scale;
893                         GL_Color(lightcolor[0] * colorscale, lightcolor[1] * colorscale, lightcolor[2] * colorscale, 1);
894                         for (renders = 0;renders < mult;renders++)
895                                 R_Mesh_Draw(numverts, numtriangles, elements);
896                         qglActiveTexture(GL_TEXTURE3_ARB);
897                         qglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
898                 }
899                 else
900                 {
901                         // 2 texture no3D combine path, two pass
902                         m.tex[0] = R_GetTexture(bumptexture);
903                         m.tex3d[1] = R_GetTexture(r_shadow_normalsattenuationtexture);
904                         m.texcombinergb[0] = GL_REPLACE;
905                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
906                         m.texalphascale[1] = 1;
907                         R_Mesh_TextureState(&m);
908                         qglColorMask(0,0,0,1);
909                         qglDisable(GL_BLEND);
910                         GL_Color(1,1,1,1);
911                         memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
912                         R_Shadow_GenTexCoords_Diffuse_Attenuation3D(varray_texcoord[1], numverts, varray_vertex, svectors, tvectors, normals, relativelightorigin, lightradius);
913                         R_Mesh_Draw(numverts, numtriangles, elements);
914
915                         m.tex[0] = R_GetTexture(basetexture);
916                         m.tex3d[1] = 0;
917                         m.texcubemap[1] = R_GetTexture(lightcubemap);
918                         m.texcombinergb[0] = GL_MODULATE;
919                         m.texcombinergb[1] = GL_MODULATE;
920                         m.texrgbscale[1] = 1;
921                         m.texalphascale[1] = 1;
922                         R_Mesh_TextureState(&m);
923                         qglColorMask(1,1,1,1);
924                         qglBlendFunc(GL_DST_ALPHA, GL_ONE);
925                         qglEnable(GL_BLEND);
926                         if (lightcubemap)
927                                 R_Shadow_GenTexCoords_LightCubeMap(varray_texcoord[1], numverts, varray_vertex, relativelightorigin);
928
929                         colorscale = r_colorscale * 1.0f * r_shadow_lightintensityscale.value;
930                         for (mult = 1, scale = ixtable[mult];mult < 64 && (lightcolor[0] * scale * colorscale > 1 || lightcolor[1] * scale * colorscale > 1 || lightcolor[2] * scale * colorscale > 1);mult++, scale = ixtable[mult]);
931                         colorscale *= scale;
932                         GL_Color(lightcolor[0] * colorscale, lightcolor[1] * colorscale, lightcolor[2] * colorscale, 1);
933                         for (renders = 0;renders < mult;renders++)
934                                 R_Mesh_Draw(numverts, numtriangles, elements);
935                 }
936         }
937         else if (r_textureunits.integer >= 4)
938         {
939                 // 4 texture no3D combine path, two pass
940                 m.tex[0] = R_GetTexture(bumptexture);
941                 m.texcubemap[1] = R_GetTexture(r_shadow_normalscubetexture);
942                 m.texcombinergb[0] = GL_REPLACE;
943                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
944                 m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
945                 m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
946                 R_Mesh_TextureState(&m);
947                 qglColorMask(0,0,0,1);
948                 qglDisable(GL_BLEND);
949                 GL_Color(1,1,1,1);
950                 memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
951                 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord[1], numverts, varray_vertex, svectors, tvectors, normals, relativelightorigin);
952                 R_Shadow_GenTexCoords_Attenuation2D1D(varray_texcoord[2], varray_texcoord[3], numverts, varray_vertex, svectors, tvectors, normals, relativelightorigin, lightradius);
953                 R_Mesh_Draw(numverts, numtriangles, elements);
954
955                 m.tex[0] = R_GetTexture(basetexture);
956                 m.texcubemap[1] = R_GetTexture(lightcubemap);
957                 m.texcombinergb[0] = GL_MODULATE;
958                 m.texcombinergb[1] = GL_MODULATE;
959                 m.tex[2] = 0;
960                 m.tex[3] = 0;
961                 R_Mesh_TextureState(&m);
962                 qglColorMask(1,1,1,1);
963                 qglBlendFunc(GL_DST_ALPHA, GL_ONE);
964                 qglEnable(GL_BLEND);
965                 if (lightcubemap)
966                         R_Shadow_GenTexCoords_LightCubeMap(varray_texcoord[1], numverts, varray_vertex, relativelightorigin);
967
968                 colorscale = r_colorscale * 1.0f * r_shadow_lightintensityscale.value;
969                 for (mult = 1, scale = ixtable[mult];mult < 64 && (lightcolor[0] * scale * colorscale > 1 || lightcolor[1] * scale * colorscale > 1 || lightcolor[2] * scale * colorscale > 1);mult++, scale = ixtable[mult]);
970                 colorscale *= scale;
971                 GL_Color(lightcolor[0] * colorscale, lightcolor[1] * colorscale, lightcolor[2] * colorscale, 1);
972                 for (renders = 0;renders < mult;renders++)
973                         R_Mesh_Draw(numverts, numtriangles, elements);
974         }
975         else
976         {
977                 // 2 texture no3D combine path, three pass
978                 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
979                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
980                 R_Mesh_TextureState(&m);
981                 qglColorMask(0,0,0,1);
982                 qglDisable(GL_BLEND);
983                 GL_Color(1,1,1,1);
984                 R_Shadow_GenTexCoords_Attenuation2D1D(varray_texcoord[0], varray_texcoord[1], numverts, varray_vertex, svectors, tvectors, normals, relativelightorigin, lightradius);
985                 R_Mesh_Draw(numverts, numtriangles, elements);
986
987                 m.tex[0] = R_GetTexture(bumptexture);
988                 m.tex[1] = 0;
989                 m.texcubemap[1] = R_GetTexture(r_shadow_normalscubetexture);
990                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
991                 R_Mesh_TextureState(&m);
992                 qglBlendFunc(GL_DST_ALPHA, GL_ZERO);
993                 qglEnable(GL_BLEND);
994                 memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
995                 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord[1], numverts, varray_vertex, svectors, tvectors, normals, relativelightorigin);
996                 R_Mesh_Draw(numverts, numtriangles, elements);
997
998                 m.tex[0] = R_GetTexture(basetexture);
999                 m.texcubemap[1] = R_GetTexture(lightcubemap);
1000                 m.texcombinergb[1] = GL_MODULATE;
1001                 R_Mesh_TextureState(&m);
1002                 qglColorMask(1,1,1,1);
1003                 qglBlendFunc(GL_DST_ALPHA, GL_ONE);
1004                 if (lightcubemap)
1005                         R_Shadow_GenTexCoords_LightCubeMap(varray_texcoord[1], numverts, varray_vertex, relativelightorigin);
1006
1007                 colorscale = r_colorscale * 1.0f * r_shadow_lightintensityscale.value;
1008                 for (mult = 1, scale = ixtable[mult];mult < 64 && (lightcolor[0] * scale * colorscale > 1 || lightcolor[1] * scale * colorscale > 1 || lightcolor[2] * scale * colorscale > 1);mult++, scale = ixtable[mult]);
1009                 colorscale *= scale;
1010                 GL_Color(lightcolor[0] * colorscale, lightcolor[1] * colorscale, lightcolor[2] * colorscale, 1);
1011                 for (renders = 0;renders < mult;renders++)
1012                         R_Mesh_Draw(numverts, numtriangles, elements);
1013         }
1014 }
1015
1016 void R_Shadow_SpecularLighting(int numverts, int numtriangles, const int *elements, const float *svectors, const float *tvectors, const float *normals, const float *texcoords, const float *relativelightorigin, const float *relativeeyeorigin, float lightradius, const float *lightcolor, rtexture_t *glosstexture, rtexture_t *bumptexture, rtexture_t *lightcubemap)
1017 {
1018         int renders, mult;
1019         float scale, colorscale;
1020         rmeshstate_t m;
1021         memset(&m, 0, sizeof(m));
1022         if (!bumptexture)
1023                 bumptexture = r_shadow_blankbumptexture;
1024         if (!glosstexture)
1025                 glosstexture = r_shadow_blankglosstexture;
1026         if (r_shadow_gloss.integer >= 2 || (r_shadow_gloss.integer >= 1 && glosstexture != r_shadow_blankglosstexture))
1027         {
1028                 // 2 texture no3D combine path, five pass
1029                 memset(&m, 0, sizeof(m));
1030
1031                 m.tex[0] = R_GetTexture(bumptexture);
1032                 m.texcubemap[1] = R_GetTexture(r_shadow_normalscubetexture);
1033                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1034                 R_Mesh_TextureState(&m);
1035                 qglColorMask(0,0,0,1);
1036                 qglDisable(GL_BLEND);
1037                 GL_Color(1,1,1,1);
1038                 memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
1039                 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord[1], numverts, varray_vertex, svectors, tvectors, normals, relativelightorigin, relativeeyeorigin);
1040                 R_Mesh_Draw(numverts, numtriangles, elements);
1041
1042                 m.tex[0] = 0;
1043                 m.texcubemap[1] = 0;
1044                 m.texcombinergb[1] = GL_MODULATE;
1045                 R_Mesh_TextureState(&m);
1046                 // square alpha in framebuffer a few times to make it shiny
1047                 qglBlendFunc(GL_ZERO, GL_DST_ALPHA);
1048                 qglEnable(GL_BLEND);
1049                 // these comments are a test run through this math for intensity 0.5
1050                 // 0.5 * 0.5 = 0.25
1051                 R_Mesh_Draw(numverts, numtriangles, elements);
1052                 // 0.25 * 0.25 = 0.0625
1053                 R_Mesh_Draw(numverts, numtriangles, elements);
1054                 // 0.0625 * 0.0625 = 0.00390625
1055                 R_Mesh_Draw(numverts, numtriangles, elements);
1056
1057                 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1058                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1059                 R_Mesh_TextureState(&m);
1060                 qglBlendFunc(GL_DST_ALPHA, GL_ZERO);
1061                 R_Shadow_GenTexCoords_Attenuation2D1D(varray_texcoord[0], varray_texcoord[1], numverts, varray_vertex, svectors, tvectors, normals, relativelightorigin, lightradius);
1062                 R_Mesh_Draw(numverts, numtriangles, elements);
1063
1064                 m.tex[0] = R_GetTexture(glosstexture);
1065                 m.texcubemap[1] = R_GetTexture(lightcubemap);
1066                 R_Mesh_TextureState(&m);
1067                 qglColorMask(1,1,1,1);
1068                 qglBlendFunc(GL_DST_ALPHA, GL_ONE);
1069                 memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
1070                 if (lightcubemap)
1071                         R_Shadow_GenTexCoords_LightCubeMap(varray_texcoord[1], numverts, varray_vertex, relativelightorigin);
1072
1073                 // the 0.25f makes specular lighting much dimmer than diffuse (intentionally)
1074                 colorscale = r_colorscale * 0.25f * r_shadow_lightintensityscale.value;
1075                 for (mult = 1, scale = ixtable[mult];mult < 64 && (lightcolor[0] * scale * colorscale > 1 || lightcolor[1] * scale * colorscale > 1 || lightcolor[2] * scale * colorscale > 1);mult++, scale = ixtable[mult]);
1076                 colorscale *= scale;
1077                 GL_Color(lightcolor[0] * colorscale, lightcolor[1] * colorscale, lightcolor[2] * colorscale, 1);
1078                 for (renders = 0;renders < mult;renders++)
1079                         R_Mesh_Draw(numverts, numtriangles, elements);
1080         }
1081 }
1082
1083 #define PRECOMPUTEDSHADOWVOLUMES 1
1084 void R_Shadow_DrawWorldLightShadowVolume(matrix4x4_t *matrix, worldlight_t *light)
1085 {
1086 #if PRECOMPUTEDSHADOWVOLUMES
1087         R_Mesh_Matrix(matrix);
1088         R_Shadow_RenderShadowMeshVolume(light->shadowvolume);
1089 #else
1090         shadowmesh_t *mesh;
1091         R_Mesh_Matrix(matrix);
1092         for (mesh = light->shadowvolume;mesh;mesh = mesh->next)
1093         {
1094                 R_Mesh_ResizeCheck(mesh->numverts * 2);
1095                 memcpy(varray_vertex, mesh->verts, mesh->numverts * sizeof(float[4]));
1096                 R_Shadow_Volume(mesh->numverts, mesh->numtriangles, varray_vertex, mesh->elements, mesh->neighbors, light->origin, light->lightradius, light->lightradius);
1097         }
1098 #endif
1099 }
1100
1101 cvar_t r_editlights = {0, "r_editlights", "0"};
1102 cvar_t r_editlights_cursordistance = {0, "r_editlights_distance", "1024"};
1103 cvar_t r_editlights_cursorpushback = {0, "r_editlights_pushback", "0"};
1104 cvar_t r_editlights_cursorpushoff = {0, "r_editlights_pushoff", "4"};
1105 cvar_t r_editlights_cursorgrid = {0, "r_editlights_grid", "4"};
1106 worldlight_t *r_shadow_worldlightchain;
1107 worldlight_t *r_shadow_selectedlight;
1108 vec3_t r_editlights_cursorlocation;
1109
1110 static int castshadowcount = 1;
1111 void R_Shadow_NewWorldLight(vec3_t origin, float radius, vec3_t color, int style, const char *cubemapname)
1112 {
1113         int i, j, k, l, maxverts, *mark;
1114         float *verts, *v, *v0, *v1, f, projectdistance, temp[3], temp2[3], temp3[3], radius2;
1115         worldlight_t *e;
1116         shadowmesh_t *mesh;
1117         mleaf_t *leaf;
1118         msurface_t *surf;
1119         qbyte *pvs;
1120
1121         e = Mem_Alloc(r_shadow_mempool, sizeof(worldlight_t));
1122         VectorCopy(origin, e->origin);
1123         VectorCopy(color, e->light);
1124         e->lightradius = radius;
1125         VectorCopy(origin, e->mins);
1126         VectorCopy(origin, e->maxs);
1127         e->cullradius = 0;
1128         e->style = style;
1129         e->next = r_shadow_worldlightchain;
1130         r_shadow_worldlightchain = e;
1131         if (cubemapname)
1132         {
1133                 e->cubemapname = Mem_Alloc(r_shadow_mempool, strlen(cubemapname) + 1);
1134                 strcpy(e->cubemapname, cubemapname);
1135                 // FIXME: add cubemap loading (and don't load a cubemap twice)
1136         }
1137         if (cl.worldmodel)
1138         {
1139                 castshadowcount++;
1140                 leaf = Mod_PointInLeaf(origin, cl.worldmodel);
1141                 pvs = Mod_LeafPVS(leaf, cl.worldmodel);
1142                 for (i = 0, leaf = cl.worldmodel->leafs + 1;i < cl.worldmodel->numleafs;i++, leaf++)
1143                 {
1144                         if (pvs[i >> 3] & (1 << (i & 7)))
1145                         {
1146                                 VectorCopy(origin, temp);
1147                                 if (temp[0] < leaf->mins[0]) temp[0] = leaf->mins[0];
1148                                 if (temp[0] > leaf->maxs[0]) temp[0] = leaf->maxs[0];
1149                                 if (temp[1] < leaf->mins[1]) temp[1] = leaf->mins[1];
1150                                 if (temp[1] > leaf->maxs[1]) temp[1] = leaf->maxs[1];
1151                                 if (temp[2] < leaf->mins[2]) temp[2] = leaf->mins[2];
1152                                 if (temp[2] > leaf->maxs[2]) temp[2] = leaf->maxs[2];
1153                                 VectorSubtract(temp, origin, temp);
1154                                 if (DotProduct(temp, temp) < e->lightradius * e->lightradius)
1155                                 {
1156                                         leaf->worldnodeframe = castshadowcount;
1157                                         for (j = 0, mark = leaf->firstmarksurface;j < leaf->nummarksurfaces;j++, mark++)
1158                                         {
1159                                                 surf = cl.worldmodel->surfaces + *mark;
1160                                                 if (surf->castshadow != castshadowcount)
1161                                                 {
1162                                                         f = DotProduct(e->origin, surf->plane->normal) - surf->plane->dist;
1163                                                         if (surf->flags & SURF_PLANEBACK)
1164                                                                 f = -f;
1165                                                         if (f > 0 && f < e->lightradius)
1166                                                         {
1167                                                                 VectorSubtract(e->origin, surf->poly_center, temp);
1168                                                                 if (DotProduct(temp, temp) - surf->poly_radius2 < e->lightradius * e->lightradius)
1169                                                                         surf->castshadow = castshadowcount;
1170                                                         }
1171                                                 }
1172                                         }
1173                                 }
1174                         }
1175                 }
1176
1177                 e->numleafs = 0;
1178                 for (i = 0, leaf = cl.worldmodel->leafs + 1;i < cl.worldmodel->numleafs;i++, leaf++)
1179                         if (leaf->worldnodeframe == castshadowcount)
1180                                 e->numleafs++;
1181                 e->numsurfaces = 0;
1182                 for (i = 0, surf = cl.worldmodel->surfaces + cl.worldmodel->firstmodelsurface;i < cl.worldmodel->nummodelsurfaces;i++, surf++)
1183                         if (surf->castshadow == castshadowcount)
1184                                 e->numsurfaces++;
1185
1186                 if (e->numleafs)
1187                         e->leafs = Mem_Alloc(r_shadow_mempool, e->numleafs * sizeof(mleaf_t *));
1188                 if (e->numsurfaces)
1189                         e->surfaces = Mem_Alloc(r_shadow_mempool, e->numsurfaces * sizeof(msurface_t *));
1190                 e->numleafs = 0;
1191                 for (i = 0, leaf = cl.worldmodel->leafs + 1;i < cl.worldmodel->numleafs;i++, leaf++)
1192                         if (leaf->worldnodeframe == castshadowcount)
1193                                 e->leafs[e->numleafs++] = leaf;
1194                 e->numsurfaces = 0;
1195                 for (i = 0, surf = cl.worldmodel->surfaces + cl.worldmodel->firstmodelsurface;i < cl.worldmodel->nummodelsurfaces;i++, surf++)
1196                         if (surf->castshadow == castshadowcount)
1197                                 e->surfaces[e->numsurfaces++] = surf;
1198                 // find bounding box and sphere of lit surfaces
1199                 // (these will be used for creating a shape to clip the light)
1200                 radius2 = 0;
1201                 VectorCopy(e->origin, e->mins);
1202                 VectorCopy(e->origin, e->maxs);
1203                 for (j = 0;j < e->numsurfaces;j++)
1204                 {
1205                         surf = e->surfaces[j];
1206                         for (k = 0, v = surf->poly_verts;k < surf->poly_numverts;k++, v += 3)
1207                         {
1208                                 if (e->mins[0] > v[0]) e->mins[0] = v[0];if (e->maxs[0] < v[0]) e->maxs[0] = v[0];
1209                                 if (e->mins[1] > v[1]) e->mins[1] = v[1];if (e->maxs[1] < v[1]) e->maxs[1] = v[1];
1210                                 if (e->mins[2] > v[2]) e->mins[2] = v[2];if (e->maxs[2] < v[2]) e->maxs[2] = v[2];
1211                                 VectorSubtract(v, e->origin, temp);
1212                                 f = DotProduct(temp, temp);
1213                                 if (radius2 < f)
1214                                         radius2 = f;
1215                         }
1216                 }
1217                 e->cullradius = sqrt(radius2);
1218                 if (e->cullradius > e->lightradius)
1219                         e->cullradius = e->lightradius;
1220                 if (e->mins[0] < e->origin[0] - e->lightradius) e->mins[0] = e->origin[0] - e->lightradius;
1221                 if (e->maxs[0] > e->origin[0] + e->lightradius) e->maxs[0] = e->origin[0] + e->lightradius;
1222                 if (e->mins[1] < e->origin[1] - e->lightradius) e->mins[1] = e->origin[1] - e->lightradius;
1223                 if (e->maxs[1] > e->origin[1] + e->lightradius) e->maxs[1] = e->origin[1] + e->lightradius;
1224                 if (e->mins[2] < e->origin[2] - e->lightradius) e->mins[2] = e->origin[2] - e->lightradius;
1225                 if (e->maxs[2] > e->origin[2] + e->lightradius) e->maxs[2] = e->origin[2] + e->lightradius;
1226                 Con_Printf("%f %f %f, %f %f %f, %f, %f, %d, %d\n", e->mins[0], e->mins[1], e->mins[2], e->maxs[0], e->maxs[1], e->maxs[2], e->cullradius, e->lightradius, e->numleafs, e->numsurfaces);
1227                 // clip shadow volumes against eachother to remove unnecessary
1228                 // polygons (and sections of polygons)
1229                 maxverts = 256;
1230                 verts = NULL;
1231                 castshadowcount++;
1232                 for (j = 0;j < e->numsurfaces;j++)
1233                 {
1234                         surf = e->surfaces[j];
1235                         if (surf->flags & SURF_SHADOWCAST)
1236                         {
1237                                 surf->castshadow = castshadowcount;
1238                                 if (maxverts < surf->poly_numverts)
1239                                         maxverts = surf->poly_numverts;
1240                         }
1241                 }
1242                 e->shadowvolume = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768);
1243 #if !PRECOMPUTEDSHADOWVOLUMES
1244                 // make a mesh to cast a shadow volume from
1245                 for (j = 0;j < e->numsurfaces;j++)
1246                         if (e->surfaces[j]->castshadow == castshadowcount)
1247                                 Mod_ShadowMesh_AddPolygon(r_shadow_mempool, e->shadowvolume, e->surfaces[j]->poly_numverts, e->surfaces[j]->poly_verts);
1248 #else
1249 #if 1
1250                 {
1251                 int tris;
1252                 shadowmesh_t *castmesh, *mesh;
1253                 surfmesh_t *surfmesh;
1254                 // make a mesh to cast a shadow volume from
1255                 castmesh = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768);
1256                 for (j = 0;j < e->numsurfaces;j++)
1257                         if (e->surfaces[j]->castshadow == castshadowcount)
1258                                 for (surfmesh = e->surfaces[j]->mesh;surfmesh;surfmesh = surfmesh->chain)
1259                                         Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, surfmesh->numverts, surfmesh->verts, surfmesh->numtriangles, surfmesh->index);
1260                 castmesh = Mod_ShadowMesh_Finish(r_shadow_mempool, castmesh);
1261
1262                 // cast shadow volume from castmesh
1263                 for (mesh = castmesh;mesh;mesh = mesh->next)
1264                 {
1265                         R_Shadow_ResizeTriangleFacingLight(castmesh->numtriangles);
1266                         R_Shadow_ResizeShadowElements(castmesh->numtriangles);
1267
1268                         if (maxverts < castmesh->numverts * 2)
1269                         {
1270                                 maxverts = castmesh->numverts * 2;
1271                                 if (verts)
1272                                         Mem_Free(verts);
1273                                 verts = NULL;
1274                         }
1275                         if (verts == NULL && maxverts > 0)
1276                                 verts = Mem_Alloc(r_shadow_mempool, maxverts * sizeof(float[4]));
1277
1278                         // now that we have the buffers big enough, construct shadow volume mesh
1279                         memcpy(verts, castmesh->verts, castmesh->numverts * sizeof(float[4]));
1280                         R_Shadow_ProjectVertices(verts, verts + castmesh->numverts * 4, castmesh->numverts, e->origin, e->lightradius);
1281                         R_Shadow_MakeTriangleShadowFlags(castmesh->elements, verts, castmesh->numtriangles, trianglefacinglight, e->origin, e->lightradius);
1282                         tris = R_Shadow_BuildShadowVolumeTriangles(castmesh->elements, castmesh->neighbors, castmesh->numtriangles, castmesh->numverts, trianglefacinglight, shadowelements);
1283                         // add the constructed shadow volume mesh
1284                         Mod_ShadowMesh_AddMesh(r_shadow_mempool, e->shadowvolume, castmesh->numverts, verts, tris, shadowelements);
1285                 }
1286                 // we're done with castmesh now
1287                 Mod_ShadowMesh_Free(castmesh);
1288                 }
1289 #else
1290                 // make a shadow volume mesh
1291                 if (verts == NULL && maxverts > 0)
1292                         verts = Mem_Alloc(r_shadow_mempool, maxverts * sizeof(float[4]));
1293                 for (j = 0;j < e->numsurfaces;j++)
1294                 {
1295                         surf = e->surfaces[j];
1296                         if (surf->castshadow != castshadowcount)
1297                                 continue;
1298                         projectdistance = 1000000.0f;//e->lightradius;
1299                         // copy the original polygon, for the front cap of the volume
1300                         for (k = 0, v0 = surf->poly_verts, v1 = verts;k < surf->poly_numverts;k++, v0 += 3, v1 += 3)
1301                                 VectorCopy(v0, v1);
1302                         Mod_ShadowMesh_AddPolygon(r_shadow_mempool, e->shadowvolume, surf->poly_numverts, verts);
1303                         // project the original polygon, reversed, for the back cap of the volume
1304                         for (k = 0, v0 = surf->poly_verts + (surf->poly_numverts - 1) * 3, v1 = verts;k < surf->poly_numverts;k++, v0 -= 3, v1 += 3)
1305                         {
1306                                 VectorSubtract(v0, e->origin, temp);
1307                                 //VectorNormalize(temp);
1308                                 VectorMA(v0, projectdistance, temp, v1);
1309                         }
1310                         Mod_ShadowMesh_AddPolygon(r_shadow_mempool, e->shadowvolume, surf->poly_numverts, verts);
1311                         // project the shadow volume sides
1312                         for (l = surf->poly_numverts - 1, k = 0, v0 = surf->poly_verts + (surf->poly_numverts - 1) * 3, v1 = surf->poly_verts;k < surf->poly_numverts;l = k, k++, v0 = v1, v1 += 3)
1313                         {
1314                                 if (surf->neighborsurfaces == NULL || surf->neighborsurfaces[l] == NULL || surf->neighborsurfaces[l]->castshadow != castshadowcount)
1315                                 {
1316                                         VectorCopy(v1, &verts[0]);
1317                                         VectorCopy(v0, &verts[3]);
1318                                         VectorCopy(v0, &verts[6]);
1319                                         VectorCopy(v1, &verts[9]);
1320                                         VectorSubtract(&verts[6], e->origin, temp);
1321                                         //VectorNormalize(temp);
1322                                         VectorMA(&verts[6], projectdistance, temp, &verts[6]);
1323                                         VectorSubtract(&verts[9], e->origin, temp);
1324                                         //VectorNormalize(temp);
1325                                         VectorMA(&verts[9], projectdistance, temp, &verts[9]);
1326
1327 #if 0
1328                                         VectorSubtract(&verts[0], &verts[3], temp);
1329                                         VectorSubtract(&verts[6], &verts[3], temp2);
1330                                         CrossProduct(temp, temp2, temp3);
1331                                         VectorNormalize(temp3);
1332                                         if (DotProduct(surf->poly_center, temp3) > DotProduct(&verts[0], temp3))
1333                                         {
1334                                                 VectorCopy(v0, &verts[0]);
1335                                                 VectorCopy(v1, &verts[3]);
1336                                                 VectorCopy(v1, &verts[6]);
1337                                                 VectorCopy(v0, &verts[9]);
1338                                                 VectorSubtract(&verts[6], e->origin, temp);
1339                                                 //VectorNormalize(temp);
1340                                                 VectorMA(&verts[6], projectdistance, temp, &verts[6]);
1341                                                 VectorSubtract(&verts[9], e->origin, temp);
1342                                                 //VectorNormalize(temp);
1343                                                 VectorMA(&verts[9], projectdistance, temp, &verts[9]);
1344                                                 Con_Printf("flipped shadow volume edge %8p %i\n", surf, l);
1345                                         }
1346 #endif
1347
1348                                         Mod_ShadowMesh_AddPolygon(r_shadow_mempool, e->shadowvolume, 4, verts);
1349                                 }
1350                         }
1351                 }
1352 #endif
1353 #endif
1354                 e->shadowvolume = Mod_ShadowMesh_Finish(r_shadow_mempool, e->shadowvolume);
1355                 for (l = 0, mesh = e->shadowvolume;mesh;mesh = mesh->next)
1356                         l += mesh->numtriangles;
1357                 Con_Printf("static shadow volume built containing %i triangles\n", l);
1358         }
1359 }
1360
1361 void R_Shadow_FreeWorldLight(worldlight_t *light)
1362 {
1363         worldlight_t **lightpointer;
1364         for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
1365         if (*lightpointer != light)
1366                 Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain\n");
1367         *lightpointer = light->next;
1368         if (light->cubemapname)
1369                 Mem_Free(light->cubemapname);
1370         if (light->shadowvolume)
1371                 Mod_ShadowMesh_Free(light->shadowvolume);
1372         if (light->surfaces)
1373                 Mem_Free(light->surfaces);
1374         if (light->leafs)
1375                 Mem_Free(light->leafs);
1376         Mem_Free(light);
1377 }
1378
1379 void R_Shadow_ClearWorldLights(void)
1380 {
1381         while (r_shadow_worldlightchain)
1382                 R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
1383         r_shadow_selectedlight = NULL;
1384 }
1385
1386 void R_Shadow_SelectLight(worldlight_t *light)
1387 {
1388         if (r_shadow_selectedlight)
1389                 r_shadow_selectedlight->selected = false;
1390         r_shadow_selectedlight = light;
1391         if (r_shadow_selectedlight)
1392                 r_shadow_selectedlight->selected = true;
1393 }
1394
1395 void R_Shadow_FreeSelectedWorldLight(void)
1396 {
1397         if (r_shadow_selectedlight)
1398         {
1399                 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
1400                 r_shadow_selectedlight = NULL;
1401         }
1402 }
1403
1404 void R_Shadow_SelectLightInView(void)
1405 {
1406         float bestrating, rating, temp[3], dist;
1407         worldlight_t *best, *light;
1408         best = NULL;
1409         bestrating = 1e30;
1410         for (light = r_shadow_worldlightchain;light;light = light->next)
1411         {
1412                 VectorSubtract(light->origin, r_refdef.vieworg, temp);
1413                 dist = sqrt(DotProduct(temp, temp));
1414                 if (DotProduct(temp, vpn) >= 0.97 * dist && bestrating > dist && CL_TraceLine(light->origin, r_refdef.vieworg, NULL, NULL, 0, true, NULL) == 1.0f)
1415                 {
1416                         bestrating = dist;
1417                         best = light;
1418                 }
1419         }
1420         R_Shadow_SelectLight(best);
1421 }
1422
1423 void R_Shadow_LoadWorldLights(void)
1424 {
1425         int n, a, style;
1426         char name[MAX_QPATH], cubemapname[MAX_QPATH], *lightsstring, *s, *t;
1427         float origin[3], radius, color[3];
1428         COM_StripExtension(cl.worldmodel->name, name);
1429         strcat(name, ".rtlights");
1430         lightsstring = COM_LoadFile(name, false);
1431         if (lightsstring)
1432         {
1433                 s = lightsstring;
1434                 n = 0;
1435                 while (*s)
1436                 {
1437                         t = s;
1438                         while (*s && *s != '\n')
1439                                 s++;
1440                         if (!*s)
1441                                 break;
1442                         *s = 0;
1443                         a = sscanf(t, "%f %f %f %f %f %f %f %d %s", &origin[0], &origin[1], &origin[2], &radius, &color[0], &color[1], &color[2], &style, &cubemapname);
1444                         if (a < 9)
1445                                 cubemapname[0] = 0;
1446                         *s = '\n';
1447                         if (a < 8)
1448                         {
1449                                 Con_Printf("found %d parameters on line %i, should be 8 or 9 parameters (origin[0] origin[1] origin[2] radius color[0] color[1] color[2] style cubemapname)\n", a, n + 1);
1450                                 break;
1451                         }
1452                         R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname);
1453                         s++;
1454                         n++;
1455                 }
1456                 if (*s)
1457                         Con_Printf("invalid rtlights file \"%s\"\n", name);
1458                 Mem_Free(lightsstring);
1459         }
1460 }
1461
1462 void R_Shadow_SaveWorldLights(void)
1463 {
1464         worldlight_t *light;
1465         int bufchars, bufmaxchars;
1466         char *buf, *oldbuf;
1467         char name[MAX_QPATH];
1468         char line[1024];
1469         if (!r_shadow_worldlightchain)
1470                 return;
1471         COM_StripExtension(cl.worldmodel->name, name);
1472         strcat(name, ".rtlights");
1473         bufchars = bufmaxchars = 0;
1474         buf = NULL;
1475         for (light = r_shadow_worldlightchain;light;light = light->next)
1476         {
1477                 sprintf(line, "%g %g %g %g %g %g %g %d %s\n", light->origin[0], light->origin[1], light->origin[2], light->lightradius, light->light[0], light->light[1], light->light[2], light->style, light->cubemapname ? light->cubemapname : "");
1478                 if (bufchars + strlen(line) > bufmaxchars)
1479                 {
1480                         bufmaxchars = bufchars + strlen(line) + 2048;
1481                         oldbuf = buf;
1482                         buf = Mem_Alloc(r_shadow_mempool, bufmaxchars);
1483                         if (oldbuf)
1484                         {
1485                                 if (bufchars)
1486                                         memcpy(buf, oldbuf, bufchars);
1487                                 Mem_Free(oldbuf);
1488                         }
1489                 }
1490                 if (strlen(line))
1491                 {
1492                         memcpy(buf + bufchars, line, strlen(line));
1493                         bufchars += strlen(line);
1494                 }
1495         }
1496         if (bufchars)
1497                 COM_WriteFile(name, buf, bufchars);
1498         if (buf)
1499                 Mem_Free(buf);
1500 }
1501
1502 void R_Shadow_LoadLightsFile(void)
1503 {
1504         int n, a, style;
1505         char name[MAX_QPATH], cubemapname[MAX_QPATH], *lightsstring, *s, *t;
1506         float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
1507         COM_StripExtension(cl.worldmodel->name, name);
1508         strcat(name, ".lights");
1509         lightsstring = COM_LoadFile(name, false);
1510         if (lightsstring)
1511         {
1512                 s = lightsstring;
1513                 n = 0;
1514                 while (*s)
1515                 {
1516                         t = s;
1517                         while (*s && *s != '\n')
1518                                 s++;
1519                         if (!*s)
1520                                 break;
1521                         *s = 0;
1522                         a = sscanf(t, "%f %f %f %f %f %f %f %f %f %f %f %f %f %d", &origin[0], &origin[1], &origin[2], &falloff, &color[0], &color[1], &color[2], &subtract, &spotdir[0], &spotdir[1], &spotdir[2], &spotcone, &distbias, &style);
1523                         *s = '\n';
1524                         if (a < 14)
1525                         {
1526                                 Con_Printf("invalid lights file, found %d parameters on line %i, should be 14 parameters (origin[0] origin[1] origin[2] falloff light[0] light[1] light[2] subtract spotdir[0] spotdir[1] spotdir[2] spotcone distancebias style)\n", a, n + 1);
1527                                 break;
1528                         }
1529                         radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
1530                         radius = bound(15, radius, 4096);
1531                         VectorScale(color, (1.0f / (8388608.0f)), color);
1532                         R_Shadow_NewWorldLight(origin, radius, color, style, NULL);
1533                         s++;
1534                         n++;
1535                 }
1536                 if (*s)
1537                         Con_Printf("invalid lights file \"%s\"\n", name);
1538                 Mem_Free(lightsstring);
1539         }
1540 }
1541
1542 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
1543 {
1544         int entnum, style, islight;
1545         char key[256], value[1024];
1546         float origin[3], radius, color[3], light, scale, originhack[3];
1547         const char *data;
1548
1549         data = cl.worldmodel->entities;
1550         if (!data)
1551                 return;
1552         for (entnum = 0;COM_ParseToken(&data) && com_token[0] == '{';entnum++)
1553         {
1554                 light = 0;
1555                 origin[0] = origin[1] = origin[2] = 0;
1556                 originhack[0] = originhack[1] = originhack[2] = 0;
1557                 color[0] = color[1] = color[2] = 1;
1558                 scale = 1;
1559                 style = 0;
1560                 islight = false;
1561                 while (1)
1562                 {
1563                         if (!COM_ParseToken(&data))
1564                                 break; // error
1565                         if (com_token[0] == '}')
1566                                 break; // end of entity
1567                         if (com_token[0] == '_')
1568                                 strcpy(key, com_token + 1);
1569                         else
1570                                 strcpy(key, com_token);
1571                         while (key[strlen(key)-1] == ' ') // remove trailing spaces
1572                                 key[strlen(key)-1] = 0;
1573                         if (!COM_ParseToken(&data))
1574                                 break; // error
1575                         strcpy(value, com_token);
1576
1577                         // now that we have the key pair worked out...
1578                         if (!strcmp("light", key))
1579                                 light = atof(value);
1580                         else if (!strcmp("origin", key))
1581                                 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
1582                         else if (!strcmp("color", key))
1583                                 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
1584                         else if (!strcmp("wait", key))
1585                                 scale = atof(value);
1586                         else if (!strcmp("classname", key))
1587                         {
1588                                 if (!strncmp(value, "light", 5))
1589                                 {
1590                                         islight = true;
1591                                         if (!strcmp(value, "light_flame_large_yellow"))
1592                                         {
1593                                                 originhack[0] = 0;
1594                                                 originhack[1] = 0;
1595                                                 originhack[2] = 40;
1596                                         }
1597                                         if (!strcmp(value, "light_flame_small_yellow"))
1598                                         {
1599                                                 originhack[0] = 0;
1600                                                 originhack[1] = 0;
1601                                                 originhack[2] = 40;
1602                                         }
1603                                         if (!strcmp(value, "light_torch_small_white"))
1604                                         {
1605                                                 originhack[0] = 0;
1606                                                 originhack[1] = 0;
1607                                                 originhack[2] = 40;
1608                                         }
1609                                         if (!strcmp(value, "light_torch_small_walltorch"))
1610                                         {
1611                                                 originhack[0] = 0;
1612                                                 originhack[1] = 0;
1613                                                 originhack[2] = 40;
1614                                         }
1615                                 }
1616                         }
1617                         else if (!strcmp("style", key))
1618                                 style = atoi(value);
1619                 }
1620                 if (light <= 0 && islight)
1621                         light = 300;
1622                 radius = bound(0, light / scale, 1048576) + 15.0f;
1623                 light = bound(0, light, 1048576) * (1.0f / 256.0f);
1624                 VectorScale(color, light, color);
1625                 VectorAdd(origin, originhack, origin);
1626                 if (radius >= 15)
1627                         R_Shadow_NewWorldLight(origin, radius, color, style, NULL);
1628         }
1629 }
1630
1631
1632 void R_Shadow_SetCursorLocationForView(void)
1633 {
1634         vec_t dist, push, frac;
1635         vec3_t dest, endpos, normal;
1636         VectorMA(r_refdef.vieworg, r_editlights_cursordistance.value, vpn, dest);
1637         frac = CL_TraceLine(r_refdef.vieworg, dest, endpos, normal, 0, true, NULL);
1638         if (frac < 1)
1639         {
1640                 dist = frac * r_editlights_cursordistance.value;
1641                 push = r_editlights_cursorpushback.value;
1642                 if (push > dist)
1643                         push = dist;
1644                 push = -push;
1645                 VectorMA(endpos, push, vpn, endpos);
1646                 VectorMA(endpos, r_editlights_cursorpushoff.value, normal, endpos);
1647         }
1648         r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
1649         r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
1650         r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
1651 }
1652
1653 extern void R_DrawCrosshairSprite(rtexture_t *texture, vec3_t origin, vec_t scale, float cr, float cg, float cb, float ca);
1654 void R_Shadow_DrawCursorCallback(const void *calldata1, int calldata2)
1655 {
1656         cachepic_t *pic;
1657         pic = Draw_CachePic("gfx/crosshair1.tga");
1658         if (pic)
1659                 R_DrawCrosshairSprite(pic->tex, r_editlights_cursorlocation, r_editlights_cursorgrid.value * 0.5f, 1, 1, 1, 1);
1660 }
1661
1662 void R_Shadow_DrawCursor(void)
1663 {
1664         R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursorCallback, NULL, 0);
1665 }
1666
1667 void R_Shadow_UpdateLightingMode(void)
1668 {
1669         r_shadow_lightingmode = 0;
1670         if (r_shadow_realtime.integer)
1671         {
1672                 if (r_shadow_worldlightchain)
1673                         r_shadow_lightingmode = 2;
1674                 else
1675                         r_shadow_lightingmode = 1;
1676         }
1677 }
1678
1679 void R_Shadow_UpdateWorldLightSelection(void)
1680 {
1681         if (r_editlights.integer)
1682         {
1683                 R_Shadow_SelectLightInView();
1684                 R_Shadow_SetCursorLocationForView();
1685                 R_Shadow_DrawCursor();
1686         }
1687         else
1688                 R_Shadow_SelectLight(NULL);
1689 }
1690
1691 void R_Shadow_EditLights_Clear_f(void)
1692 {
1693         R_Shadow_ClearWorldLights();
1694 }
1695
1696 void R_Shadow_EditLights_Reload_f(void)
1697 {
1698         if (cl.worldmodel)
1699         {
1700                 R_Shadow_ClearWorldLights();
1701                 R_Shadow_LoadWorldLights();
1702         }
1703 }
1704
1705 void R_Shadow_EditLights_Save_f(void)
1706 {
1707         if (cl.worldmodel)
1708                 R_Shadow_SaveWorldLights();
1709 }
1710
1711 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
1712 {
1713         R_Shadow_ClearWorldLights();
1714         R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
1715 }
1716
1717 void R_Shadow_EditLights_ImportLightsFile_f(void)
1718 {
1719         R_Shadow_ClearWorldLights();
1720         R_Shadow_LoadLightsFile();
1721 }
1722
1723 void R_Shadow_EditLights_Spawn_f(void)
1724 {
1725         vec3_t origin, color;
1726         vec_t radius;
1727         int style;
1728         const char *cubemapname;
1729         if (!r_editlights.integer)
1730         {
1731                 Con_Printf("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
1732                 return;
1733         }
1734         if (Cmd_Argc() <= 7)
1735         {
1736                 radius = 200;
1737                 color[0] = color[1] = color[2] = 1;
1738                 style = 0;
1739                 cubemapname = NULL;
1740                 if (Cmd_Argc() >= 2)
1741                 {
1742                         radius = atof(Cmd_Argv(1));
1743                         if (Cmd_Argc() >= 3)
1744                         {
1745                                 color[0] = atof(Cmd_Argv(2));
1746                                 color[1] = color[0];
1747                                 color[2] = color[0];
1748                                 if (Cmd_Argc() >= 5)
1749                                 {
1750                                         color[1] = atof(Cmd_Argv(3));
1751                                         color[2] = atof(Cmd_Argv(4));
1752                                         if (Cmd_Argc() >= 6)
1753                                         {
1754                                                 style = atoi(Cmd_Argv(5));
1755                                                 if (Cmd_Argc() >= 7)
1756                                                         cubemapname = Cmd_Argv(6);
1757                                         }
1758                                 }
1759                         }
1760                 }
1761                 if (cubemapname && !cubemapname[0])
1762                         cubemapname = NULL;
1763                 if (radius >= 16 && color[0] >= 0 && color[1] >= 0 && color[2] >= 0 && style >= 0 && style < 256 && (color[0] >= 0.1 || color[1] >= 0.1 || color[2] >= 0.1))
1764                 {
1765                         VectorCopy(r_editlights_cursorlocation, origin);
1766                         R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname);
1767                         return;
1768                 }
1769         }
1770         Con_Printf("usage: r_editlights_spawn radius red green blue [style [cubemap]]\n");
1771 }
1772
1773 void R_Shadow_EditLights_Edit_f(void)
1774 {
1775         vec3_t origin, color;
1776         vec_t radius;
1777         int style;
1778         const char *cubemapname;
1779         if (!r_editlights.integer)
1780         {
1781                 Con_Printf("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
1782                 return;
1783         }
1784         if (!r_shadow_selectedlight)
1785         {
1786                 Con_Printf("No selected light.\n");
1787                 return;
1788         }
1789         if (Cmd_Argc() <= 7)
1790         {
1791                 radius = 200;
1792                 color[0] = color[1] = color[2] = 1;
1793                 style = 0;
1794                 cubemapname = NULL;
1795                 if (Cmd_Argc() >= 2)
1796                 {
1797                         radius = atof(Cmd_Argv(1));
1798                         if (Cmd_Argc() >= 3)
1799                         {
1800                                 color[0] = atof(Cmd_Argv(2));
1801                                 color[1] = color[0];
1802                                 color[2] = color[0];
1803                                 if (Cmd_Argc() >= 5)
1804                                 {
1805                                         color[1] = atof(Cmd_Argv(3));
1806                                         color[2] = atof(Cmd_Argv(4));
1807                                         if (Cmd_Argc() >= 6)
1808                                         {
1809                                                 style = atoi(Cmd_Argv(5));
1810                                                 if (Cmd_Argc() >= 7)
1811                                                         cubemapname = Cmd_Argv(6);
1812                                         }
1813                                 }
1814                         }
1815                 }
1816                 if (cubemapname && !cubemapname[0])
1817                         cubemapname = NULL;
1818                 if (radius >= 16 && color[0] >= 0 && color[1] >= 0 && color[2] >= 0 && style >= 0 && style < 256 && (color[0] >= 0.1 || color[1] >= 0.1 || color[2] >= 0.1))
1819                 {
1820                         VectorCopy(r_shadow_selectedlight->origin, origin);
1821                         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
1822                         r_shadow_selectedlight = NULL;
1823                         R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname);
1824                         return;
1825                 }
1826         }
1827         Con_Printf("usage: r_editlights_edit radius red green blue [style [cubemap]]\n");
1828 }
1829
1830 void R_Shadow_EditLights_Remove_f(void)
1831 {
1832         if (!r_editlights.integer)
1833         {
1834                 Con_Printf("Cannot remove light when not in editing mode.  Set r_editlights to 1.\n");
1835                 return;
1836         }
1837         if (!r_shadow_selectedlight)
1838         {
1839                 Con_Printf("No selected light.\n");
1840                 return;
1841         }
1842         R_Shadow_FreeSelectedWorldLight();
1843 }
1844
1845 void R_Shadow_EditLights_Init(void)
1846 {
1847         Cvar_RegisterVariable(&r_editlights);
1848         Cvar_RegisterVariable(&r_editlights_cursordistance);
1849         Cvar_RegisterVariable(&r_editlights_cursorpushback);
1850         Cvar_RegisterVariable(&r_editlights_cursorpushoff);
1851         Cvar_RegisterVariable(&r_editlights_cursorgrid);
1852         Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f);
1853         Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f);
1854         Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f);
1855         Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f);
1856         Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f);
1857         Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f);
1858         Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f);
1859         Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f);
1860 }