]> icculus.org git repositories - divverent/darkplaces.git/blob - r_shadow.c
redesigned surfmesh allocation in brush model loader, in preparation for some more...
[divverent/darkplaces.git] / r_shadow.c
1
2 #include "quakedef.h"
3 #include "r_shadow.h"
4 #include "cl_collision.h"
5 #include "portals.h"
6
7 extern void R_Shadow_EditLights_Init(void);
8
9 #define SHADOWSTAGE_NONE 0
10 #define SHADOWSTAGE_STENCIL 1
11 #define SHADOWSTAGE_LIGHT 2
12 #define SHADOWSTAGE_ERASESTENCIL 3
13
14 int r_shadowstage = SHADOWSTAGE_NONE;
15 int r_shadow_reloadlights = false;
16
17 int r_shadow_lightingmode = 0;
18
19 mempool_t *r_shadow_mempool;
20
21 int maxshadowelements;
22 int *shadowelements;
23 int maxtrianglefacinglight;
24 qbyte *trianglefacinglight;
25 int *trianglefacinglightlist;
26
27 int maxshadowvertices;
28 float *shadowvertex3f;
29 int maxvertexupdate;
30 int *vertexupdate;
31 int vertexupdatenum;
32
33 rtexturepool_t *r_shadow_texturepool;
34 rtexture_t *r_shadow_normalcubetexture;
35 rtexture_t *r_shadow_attenuation2dtexture;
36 rtexture_t *r_shadow_attenuation3dtexture;
37 rtexture_t *r_shadow_blankbumptexture;
38 rtexture_t *r_shadow_blankglosstexture;
39 rtexture_t *r_shadow_blankwhitetexture;
40
41 cvar_t r_shadow_lightattenuationpower = {0, "r_shadow_lightattenuationpower", "0.5"};
42 cvar_t r_shadow_lightattenuationscale = {0, "r_shadow_lightattenuationscale", "1"};
43 cvar_t r_shadow_lightintensityscale = {0, "r_shadow_lightintensityscale", "1"};
44 cvar_t r_shadow_realtime = {0, "r_shadow_realtime", "0"};
45 cvar_t r_shadow_gloss = {0, "r_shadow_gloss", "1"};
46 cvar_t r_shadow_debuglight = {0, "r_shadow_debuglight", "-1"};
47 cvar_t r_shadow_scissor = {0, "r_shadow_scissor", "1"};
48 cvar_t r_shadow_bumpscale_bumpmap = {0, "r_shadow_bumpscale_bumpmap", "4"};
49 cvar_t r_shadow_bumpscale_basetexture = {0, "r_shadow_bumpscale_basetexture", "0"};
50 cvar_t r_shadow_polygonoffset = {0, "r_shadow_polygonoffset", "-1"};
51 cvar_t r_shadow_portallight = {0, "r_shadow_portallight", "1"};
52 cvar_t r_shadow_projectdistance = {0, "r_shadow_projectdistance", "100000"};
53 cvar_t r_shadow_texture3d = {0, "r_shadow_texture3d", "1"};
54
55 int c_rt_lights, c_rt_clears, c_rt_scissored;
56 int c_rt_shadowmeshes, c_rt_shadowtris, c_rt_lightmeshes, c_rt_lighttris;
57 int c_rtcached_shadowmeshes, c_rtcached_shadowtris;
58
59 void R_Shadow_ClearWorldLights(void);
60 void R_Shadow_SaveWorldLights(void);
61 void R_Shadow_LoadWorldLights(void);
62 void R_Shadow_LoadLightsFile(void);
63 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void);
64
65 void r_shadow_start(void)
66 {
67         // allocate vertex processing arrays
68         r_shadow_mempool = Mem_AllocPool("R_Shadow");
69         maxshadowelements = 0;
70         shadowelements = NULL;
71         maxshadowvertices = 0;
72         shadowvertex3f = NULL;
73         maxvertexupdate = 0;
74         vertexupdate = NULL;
75         vertexupdatenum = 0;
76         maxtrianglefacinglight = 0;
77         trianglefacinglight = NULL;
78         trianglefacinglightlist = NULL;
79         r_shadow_normalcubetexture = NULL;
80         r_shadow_attenuation2dtexture = NULL;
81         r_shadow_attenuation3dtexture = NULL;
82         r_shadow_blankbumptexture = NULL;
83         r_shadow_blankglosstexture = NULL;
84         r_shadow_blankwhitetexture = NULL;
85         r_shadow_texturepool = NULL;
86         R_Shadow_ClearWorldLights();
87         r_shadow_reloadlights = true;
88 }
89
90 void r_shadow_shutdown(void)
91 {
92         R_Shadow_ClearWorldLights();
93         r_shadow_reloadlights = true;
94         r_shadow_normalcubetexture = NULL;
95         r_shadow_attenuation2dtexture = NULL;
96         r_shadow_attenuation3dtexture = NULL;
97         r_shadow_blankbumptexture = NULL;
98         r_shadow_blankglosstexture = NULL;
99         r_shadow_blankwhitetexture = NULL;
100         R_FreeTexturePool(&r_shadow_texturepool);
101         maxshadowelements = 0;
102         shadowelements = NULL;
103         maxshadowvertices = 0;
104         shadowvertex3f = NULL;
105         maxvertexupdate = 0;
106         vertexupdate = NULL;
107         vertexupdatenum = 0;
108         maxtrianglefacinglight = 0;
109         trianglefacinglight = NULL;
110         trianglefacinglightlist = NULL;
111         Mem_FreePool(&r_shadow_mempool);
112 }
113
114 void r_shadow_newmap(void)
115 {
116         R_Shadow_ClearWorldLights();
117         r_shadow_reloadlights = true;
118 }
119
120 void R_Shadow_Init(void)
121 {
122         Cvar_RegisterVariable(&r_shadow_lightattenuationpower);
123         Cvar_RegisterVariable(&r_shadow_lightattenuationscale);
124         Cvar_RegisterVariable(&r_shadow_lightintensityscale);
125         Cvar_RegisterVariable(&r_shadow_realtime);
126         Cvar_RegisterVariable(&r_shadow_gloss);
127         Cvar_RegisterVariable(&r_shadow_debuglight);
128         Cvar_RegisterVariable(&r_shadow_scissor);
129         Cvar_RegisterVariable(&r_shadow_bumpscale_bumpmap);
130         Cvar_RegisterVariable(&r_shadow_bumpscale_basetexture);
131         Cvar_RegisterVariable(&r_shadow_polygonoffset);
132         Cvar_RegisterVariable(&r_shadow_portallight);
133         Cvar_RegisterVariable(&r_shadow_projectdistance);
134         Cvar_RegisterVariable(&r_shadow_texture3d);
135         R_Shadow_EditLights_Init();
136         R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
137 }
138
139 int R_Shadow_MakeTriangleShadowFlags_Vertex3f(const int *elements, const float *vertex, int numtris, qbyte *facing, int *list, const float *relativelightorigin)
140 {
141         int i, tris = 0;
142         const float *v0, *v1, *v2;
143         for (i = 0;i < numtris;i++, elements += 3)
144         {
145                 // calculate triangle facing flag
146                 v0 = vertex + elements[0] * 3;
147                 v1 = vertex + elements[1] * 3;
148                 v2 = vertex + elements[2] * 3;
149                 if(PointInfrontOfTriangle(relativelightorigin, v0, v1, v2))
150                 {
151                         facing[i] = true;
152                         list[tris++] = i;
153                 }
154                 else
155                         facing[i] = false;
156         }
157         return tris;
158 }
159
160 int R_Shadow_BuildShadowVolume(const int *elements, const int *neighbors, int numverts, const qbyte *facing, const int *facinglist, int numfacing, int *out, float *vertices, const float *relativelightorigin, float projectdistance)
161 {
162         int i, j, tris, vertexpointeradjust = numverts * 3;
163         const int *e, *n;
164         float *vin, *vout;
165
166         if (maxvertexupdate < numverts)
167         {
168                 maxvertexupdate = numverts;
169                 if (vertexupdate)
170                         Mem_Free(vertexupdate);
171                 vertexupdate = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
172         }
173         vertexupdatenum++;
174
175         // check each frontface for bordering backfaces,
176         // and cast shadow polygons from those edges,
177         // also create front and back caps for shadow volume
178         tris = numfacing * 2;
179         // output front caps
180         for (i = 0;i < numfacing;i++)
181         {
182                 e = elements + facinglist[i] * 3;
183                 out[0] = e[0];
184                 out[1] = e[1];
185                 out[2] = e[2];
186                 out += 3;
187         }
188         // output back caps
189         for (i = 0;i < numfacing;i++)
190         {
191                 e = elements + facinglist[i] * 3;
192                 // generate vertices if needed
193                 for (j = 0;j < 3;j++)
194                 {
195                         if (vertexupdate[e[j]] != vertexupdatenum)
196                         {
197                                 vertexupdate[e[j]] = vertexupdatenum;
198                                 vin = vertices + e[j] * 3;
199                                 vout = vin + vertexpointeradjust;
200                                 vout[0] = relativelightorigin[0] + projectdistance * (vin[0] - relativelightorigin[0]);
201                                 vout[1] = relativelightorigin[1] + projectdistance * (vin[1] - relativelightorigin[1]);
202                                 vout[2] = relativelightorigin[2] + projectdistance * (vin[2] - relativelightorigin[2]);
203                         }
204                 }
205                 out[0] = e[2] + numverts;
206                 out[1] = e[1] + numverts;
207                 out[2] = e[0] + numverts;
208                 out += 3;
209         }
210         // output sides around frontfaces
211         for (i = 0;i < numfacing;i++)
212         {
213                 n = neighbors + facinglist[i] * 3;
214                 // check the edges
215                 if (n[0] < 0 || !facing[n[0]])
216                 {
217                         e = elements + facinglist[i] * 3;
218                         out[0] = e[1];
219                         out[1] = e[0];
220                         out[2] = e[0] + numverts;
221                         out[3] = e[1];
222                         out[4] = e[0] + numverts;
223                         out[5] = e[1] + numverts;
224                         out += 6;
225                         tris += 2;
226                 }
227                 if (n[1] < 0 || !facing[n[1]])
228                 {
229                         e = elements + facinglist[i] * 3;
230                         out[0] = e[2];
231                         out[1] = e[1];
232                         out[2] = e[1] + numverts;
233                         out[3] = e[2];
234                         out[4] = e[1] + numverts;
235                         out[5] = e[2] + numverts;
236                         out += 6;
237                         tris += 2;
238                 }
239                 if (n[2] < 0 || !facing[n[2]])
240                 {
241                         e = elements + facinglist[i] * 3;
242                         out[0] = e[0];
243                         out[1] = e[2];
244                         out[2] = e[2] + numverts;
245                         out[3] = e[0];
246                         out[4] = e[2] + numverts;
247                         out[5] = e[0] + numverts;
248                         out += 6;
249                         tris += 2;
250                 }
251         }
252         return tris;
253 }
254
255 void R_Shadow_ResizeTriangleFacingLight(int numtris)
256 {
257         // make sure trianglefacinglight is big enough for this volume
258         // ameks ru ertaignelaficgnilhg tsib gie ongu hof rhtsiv lomu e
259         // m4k3 5ur3 7r14ng13f4c1n5115h7 15 b15 3n0u5h f0r 7h15 v01um3
260         if (maxtrianglefacinglight < numtris)
261         {
262                 maxtrianglefacinglight = numtris;
263                 if (trianglefacinglight)
264                         Mem_Free(trianglefacinglight);
265                 if (trianglefacinglightlist)
266                         Mem_Free(trianglefacinglightlist);
267                 trianglefacinglight = Mem_Alloc(r_shadow_mempool, maxtrianglefacinglight);
268                 trianglefacinglightlist = Mem_Alloc(r_shadow_mempool, sizeof(int) * maxtrianglefacinglight);
269         }
270 }
271
272 int *R_Shadow_ResizeShadowElements(int numtris)
273 {
274         // make sure shadowelements is big enough for this volume
275         if (maxshadowelements < numtris * 24)
276         {
277                 maxshadowelements = numtris * 24;
278                 if (shadowelements)
279                         Mem_Free(shadowelements);
280                 shadowelements = Mem_Alloc(r_shadow_mempool, maxshadowelements * sizeof(int));
281         }
282         return shadowelements;
283 }
284
285 float *R_Shadow_VertexBuffer(int numvertices)
286 {
287         if (maxshadowvertices < numvertices)
288         {
289                 maxshadowvertices = numvertices;
290                 if (shadowvertex3f)
291                         Mem_Free(shadowvertex3f);
292                 shadowvertex3f = Mem_Alloc(r_shadow_mempool, maxshadowvertices * sizeof(float[3]));
293         }
294         return shadowvertex3f;
295 }
296
297 void R_Shadow_Volume(int numverts, int numtris, int *elements, int *neighbors, vec3_t relativelightorigin, float lightradius, float projectdistance)
298 {
299         int tris;
300         if (projectdistance < 0.1)
301         {
302                 Con_Printf("R_Shadow_Volume: projectdistance %f\n");
303                 return;
304         }
305         if (!numverts)
306                 return;
307 // terminology:
308 //
309 // frontface:
310 // a triangle facing the light source
311 //
312 // backface:
313 // a triangle not facing the light source
314 //
315 // shadow volume:
316 // an extrusion of the frontfaces, beginning at the original geometry and
317 // ending further from the light source than the original geometry
318 // (presumably at least as far as the light's radius, if the light has a
319 // radius at all), capped at both front and back to avoid any problems
320 //
321 // description:
322 // draws the shadow volumes of the model.
323 // requirements:
324 // vertex locations must already be in varray_vertex3f before use.
325 // varray_vertex3f must have capacity for numverts * 2.
326
327         // make sure trianglefacinglight is big enough for this volume
328         if (maxtrianglefacinglight < numtris)
329                 R_Shadow_ResizeTriangleFacingLight(numtris);
330
331         // make sure shadowelements is big enough for this volume
332         if (maxshadowelements < numtris * 24)
333                 R_Shadow_ResizeShadowElements(numtris);
334
335         // check which triangles are facing the light
336         tris = R_Shadow_MakeTriangleShadowFlags_Vertex3f(elements, varray_vertex3f, numtris, trianglefacinglight, trianglefacinglightlist, relativelightorigin);
337         if (!tris)
338                 return;
339
340         // by clever use of elements we can construct the whole shadow from
341         // the unprojected vertices and the projected vertices
342
343         // output triangle elements and vertices
344         tris = R_Shadow_BuildShadowVolume(elements, neighbors, numverts, trianglefacinglight, trianglefacinglightlist, tris, shadowelements, varray_vertex3f, relativelightorigin, projectdistance);
345         if (!tris)
346                 return;
347
348         if (r_shadowstage == SHADOWSTAGE_STENCIL)
349         {
350                 // increment stencil if backface is behind depthbuffer
351                 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
352                 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
353                 R_Mesh_Draw(numverts * 2, tris, shadowelements);
354                 c_rt_shadowmeshes++;
355                 c_rt_shadowtris += numtris;
356                 // decrement stencil if frontface is behind depthbuffer
357                 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
358                 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
359         }
360         R_Mesh_Draw(numverts * 2, tris, shadowelements);
361         c_rt_shadowmeshes++;
362         c_rt_shadowtris += numtris;
363 }
364
365 void R_Shadow_RenderShadowMeshVolume(shadowmesh_t *firstmesh)
366 {
367         shadowmesh_t *mesh;
368         if (r_shadowstage == SHADOWSTAGE_STENCIL)
369         {
370                 // increment stencil if backface is behind depthbuffer
371                 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
372                 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
373                 for (mesh = firstmesh;mesh;mesh = mesh->next)
374                 {
375                         R_Mesh_GetSpace(mesh->numverts);
376                         R_Mesh_CopyVertex3f(mesh->vertex3f, mesh->numverts);
377                         R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
378                         c_rtcached_shadowmeshes++;
379                         c_rtcached_shadowtris += mesh->numtriangles;
380                 }
381                 // decrement stencil if frontface is behind depthbuffer
382                 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
383                 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
384         }
385         for (mesh = firstmesh;mesh;mesh = mesh->next)
386         {
387                 R_Mesh_GetSpace(mesh->numverts);
388                 R_Mesh_CopyVertex3f(mesh->vertex3f, mesh->numverts);
389                 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
390                 c_rtcached_shadowmeshes++;
391                 c_rtcached_shadowtris += mesh->numtriangles;
392         }
393 }
394
395 float r_shadow_attenpower, r_shadow_attenscale;
396 static void R_Shadow_MakeTextures(void)
397 {
398         int x, y, z, d, side;
399         float v[3], s, t, intensity;
400         qbyte *data;
401         R_FreeTexturePool(&r_shadow_texturepool);
402         r_shadow_texturepool = R_AllocTexturePool();
403         r_shadow_attenpower = r_shadow_lightattenuationpower.value;
404         r_shadow_attenscale = r_shadow_lightattenuationscale.value;
405 #define NORMSIZE 64
406 #define ATTEN2DSIZE 64
407 #define ATTEN3DSIZE 32
408         data = Mem_Alloc(tempmempool, max(6*NORMSIZE*NORMSIZE*4, max(ATTEN3DSIZE*ATTEN3DSIZE*ATTEN3DSIZE*4, ATTEN2DSIZE*ATTEN2DSIZE*4)));
409         data[0] = 128;
410         data[1] = 128;
411         data[2] = 255;
412         data[3] = 255;
413         r_shadow_blankbumptexture = R_LoadTexture2D(r_shadow_texturepool, "blankbump", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
414         data[0] = 255;
415         data[1] = 255;
416         data[2] = 255;
417         data[3] = 255;
418         r_shadow_blankglosstexture = R_LoadTexture2D(r_shadow_texturepool, "blankgloss", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
419         data[0] = 255;
420         data[1] = 255;
421         data[2] = 255;
422         data[3] = 255;
423         r_shadow_blankwhitetexture = R_LoadTexture2D(r_shadow_texturepool, "blankwhite", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
424         if (gl_texturecubemap)
425         {
426                 for (side = 0;side < 6;side++)
427                 {
428                         for (y = 0;y < NORMSIZE;y++)
429                         {
430                                 for (x = 0;x < NORMSIZE;x++)
431                                 {
432                                         s = (x + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
433                                         t = (y + 0.5f) * (2.0f / NORMSIZE) - 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*NORMSIZE+y)*NORMSIZE+x)*4+0] = 128.0f + intensity * v[0];
469                                         data[((side*NORMSIZE+y)*NORMSIZE+x)*4+1] = 128.0f + intensity * v[1];
470                                         data[((side*NORMSIZE+y)*NORMSIZE+x)*4+2] = 128.0f + intensity * v[2];
471                                         data[((side*NORMSIZE+y)*NORMSIZE+x)*4+3] = 255;
472                                 }
473                         }
474                 }
475                 r_shadow_normalcubetexture = R_LoadTextureCubeMap(r_shadow_texturepool, "normalcube", NORMSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP, NULL);
476         }
477         else
478                 r_shadow_normalcubetexture = NULL;
479         for (y = 0;y < ATTEN2DSIZE;y++)
480         {
481                 for (x = 0;x < ATTEN2DSIZE;x++)
482                 {
483                         v[0] = ((x + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
484                         v[1] = ((y + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
485                         v[2] = 0;
486                         intensity = 1.0f - sqrt(DotProduct(v, v));
487                         if (intensity > 0)
488                                 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
489                         d = bound(0, intensity, 255);
490                         data[(y*ATTEN2DSIZE+x)*4+0] = d;
491                         data[(y*ATTEN2DSIZE+x)*4+1] = d;
492                         data[(y*ATTEN2DSIZE+x)*4+2] = d;
493                         data[(y*ATTEN2DSIZE+x)*4+3] = d;
494                 }
495         }
496         r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", ATTEN2DSIZE, ATTEN2DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
497         if (r_shadow_texture3d.integer)
498         {
499                 for (z = 0;z < ATTEN3DSIZE;z++)
500                 {
501                         for (y = 0;y < ATTEN3DSIZE;y++)
502                         {
503                                 for (x = 0;x < ATTEN3DSIZE;x++)
504                                 {
505                                         v[0] = ((x + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
506                                         v[1] = ((y + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
507                                         v[2] = ((z + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
508                                         intensity = 1.0f - sqrt(DotProduct(v, v));
509                                         if (intensity > 0)
510                                                 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
511                                         d = bound(0, intensity, 255);
512                                         data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+0] = d;
513                                         data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+1] = d;
514                                         data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+2] = d;
515                                         data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+3] = d;
516                                 }
517                         }
518                 }
519                 r_shadow_attenuation3dtexture = R_LoadTexture3D(r_shadow_texturepool, "attenuation3d", ATTEN3DSIZE, ATTEN3DSIZE, ATTEN3DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
520         }
521         Mem_Free(data);
522 }
523
524 void R_Shadow_Stage_Begin(void)
525 {
526         rmeshstate_t m;
527
528         if (r_shadow_texture3d.integer && !gl_texture3d)
529                 Cvar_SetValueQuick(&r_shadow_texture3d, 0);
530
531         //cl.worldmodel->numlights = min(cl.worldmodel->numlights, 1);
532         if (!r_shadow_attenuation2dtexture
533          || (!r_shadow_attenuation3dtexture && r_shadow_texture3d.integer)
534          || r_shadow_lightattenuationpower.value != r_shadow_attenpower
535          || r_shadow_lightattenuationscale.value != r_shadow_attenscale)
536                 R_Shadow_MakeTextures();
537         if (r_shadow_reloadlights && cl.worldmodel)
538         {
539                 R_Shadow_ClearWorldLights();
540                 r_shadow_reloadlights = false;
541                 R_Shadow_LoadWorldLights();
542                 if (r_shadow_worldlightchain == NULL)
543                 {
544                         R_Shadow_LoadLightsFile();
545                         if (r_shadow_worldlightchain == NULL)
546                                 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
547                 }
548         }
549
550         memset(&m, 0, sizeof(m));
551         m.blendfunc1 = GL_ONE;
552         m.blendfunc2 = GL_ZERO;
553         R_Mesh_State(&m);
554         GL_Color(0, 0, 0, 1);
555         r_shadowstage = SHADOWSTAGE_NONE;
556
557         c_rt_lights = c_rt_clears = c_rt_scissored = 0;
558         c_rt_shadowmeshes = c_rt_shadowtris = c_rt_lightmeshes = c_rt_lighttris = 0;
559         c_rtcached_shadowmeshes = c_rtcached_shadowtris = 0;
560 }
561
562 void R_Shadow_Stage_ShadowVolumes(void)
563 {
564         rmeshstate_t m;
565         memset(&m, 0, sizeof(m));
566         R_Mesh_TextureState(&m);
567         GL_Color(1, 1, 1, 1);
568         qglColorMask(0, 0, 0, 0);
569         qglDisable(GL_BLEND);
570         qglDepthMask(0);
571         qglDepthFunc(GL_LESS);
572         qglEnable(GL_STENCIL_TEST);
573         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
574         qglStencilFunc(GL_ALWAYS, 128, 0xFF);
575         qglEnable(GL_CULL_FACE);
576         qglEnable(GL_DEPTH_TEST);
577         r_shadowstage = SHADOWSTAGE_STENCIL;
578         qglClear(GL_STENCIL_BUFFER_BIT);
579         c_rt_clears++;
580         // LordHavoc note: many shadow volumes reside entirely inside the world
581         // (that is to say they are entirely bounded by their lit surfaces),
582         // which can be optimized by handling things as an inverted light volume,
583         // with the shadow boundaries of the world being simulated by an altered
584         // (129) bias to stencil clearing on such lights
585         // FIXME: generate inverted light volumes for use as shadow volumes and
586         // optimize for them as noted above
587 }
588
589 void R_Shadow_Stage_LightWithoutShadows(void)
590 {
591         rmeshstate_t m;
592         memset(&m, 0, sizeof(m));
593         R_Mesh_TextureState(&m);
594         qglActiveTexture(GL_TEXTURE0_ARB);
595
596         qglEnable(GL_BLEND);
597         qglBlendFunc(GL_ONE, GL_ONE);
598         GL_Color(1, 1, 1, 1);
599         qglColorMask(1, 1, 1, 1);
600         qglDepthMask(0);
601         qglDepthFunc(GL_EQUAL);
602         qglDisable(GL_STENCIL_TEST);
603         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
604         qglStencilFunc(GL_EQUAL, 128, 0xFF);
605         qglEnable(GL_CULL_FACE);
606         qglEnable(GL_DEPTH_TEST);
607         r_shadowstage = SHADOWSTAGE_LIGHT;
608         c_rt_lights++;
609 }
610
611 void R_Shadow_Stage_LightWithShadows(void)
612 {
613         rmeshstate_t m;
614         memset(&m, 0, sizeof(m));
615         R_Mesh_TextureState(&m);
616         qglActiveTexture(GL_TEXTURE0_ARB);
617
618         qglEnable(GL_BLEND);
619         qglBlendFunc(GL_ONE, GL_ONE);
620         GL_Color(1, 1, 1, 1);
621         qglColorMask(1, 1, 1, 1);
622         qglDepthMask(0);
623         qglDepthFunc(GL_EQUAL);
624         qglEnable(GL_STENCIL_TEST);
625         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
626         // only draw light where this geometry was already rendered AND the
627         // stencil is 128 (values other than this mean shadow)
628         qglStencilFunc(GL_EQUAL, 128, 0xFF);
629         qglEnable(GL_CULL_FACE);
630         qglEnable(GL_DEPTH_TEST);
631         r_shadowstage = SHADOWSTAGE_LIGHT;
632         c_rt_lights++;
633 }
634
635 void R_Shadow_Stage_End(void)
636 {
637         rmeshstate_t m;
638         // attempt to restore state to what Mesh_State thinks it is
639         qglDisable(GL_BLEND);
640         qglBlendFunc(GL_ONE, GL_ZERO);
641         qglDepthMask(1);
642         // now restore the rest of the state to normal
643         GL_Color(1, 1, 1, 1);
644         qglColorMask(1, 1, 1, 1);
645         qglDisable(GL_SCISSOR_TEST);
646         qglDepthFunc(GL_LEQUAL);
647         qglDisable(GL_STENCIL_TEST);
648         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
649         qglStencilFunc(GL_ALWAYS, 128, 0xFF);
650         qglEnable(GL_CULL_FACE);
651         qglEnable(GL_DEPTH_TEST);
652         // force mesh state to reset by using various combinations of features
653         memset(&m, 0, sizeof(m));
654         m.blendfunc1 = GL_SRC_ALPHA;
655         m.blendfunc2 = GL_ONE_MINUS_SRC_ALPHA;
656         R_Mesh_State(&m);
657         m.blendfunc1 = GL_ONE;
658         m.blendfunc2 = GL_ZERO;
659         R_Mesh_State(&m);
660         r_shadowstage = SHADOWSTAGE_NONE;
661 }
662
663 #if 0
664 int R_Shadow_ScissorForBBoxAndSphere(const float *mins, const float *maxs, const float *origin, float radius)
665 {
666         int i, ix1, iy1, ix2, iy2;
667         float x1, y1, x2, y2, x, y;
668         vec3_t smins, smaxs;
669         vec4_t v, v2;
670         if (!r_shadow_scissor.integer)
671                 return false;
672         // if view is inside the box, just say yes it's visible
673         if (r_origin[0] >= mins[0] && r_origin[0] <= maxs[0]
674          && r_origin[1] >= mins[1] && r_origin[1] <= maxs[1]
675          && r_origin[2] >= mins[2] && r_origin[2] <= maxs[2])
676         {
677                 qglDisable(GL_SCISSOR_TEST);
678                 return false;
679         }
680         VectorSubtract(r_origin, origin, v);
681         if (DotProduct(v, v) < radius * radius)
682         {
683                 qglDisable(GL_SCISSOR_TEST);
684                 return false;
685         }
686         // create viewspace bbox
687         for (i = 0;i < 8;i++)
688         {
689                 v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_origin[0];
690                 v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_origin[1];
691                 v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_origin[2];
692                 v2[0] = DotProduct(v, vright);
693                 v2[1] = DotProduct(v, vup);
694                 v2[2] = DotProduct(v, vpn);
695                 if (i)
696                 {
697                         if (smins[0] > v2[0]) smins[0] = v2[0];
698                         if (smaxs[0] < v2[0]) smaxs[0] = v2[0];
699                         if (smins[1] > v2[1]) smins[1] = v2[1];
700                         if (smaxs[1] < v2[1]) smaxs[1] = v2[1];
701                         if (smins[2] > v2[2]) smins[2] = v2[2];
702                         if (smaxs[2] < v2[2]) smaxs[2] = v2[2];
703                 }
704                 else
705                 {
706                         smins[0] = smaxs[0] = v2[0];
707                         smins[1] = smaxs[1] = v2[1];
708                         smins[2] = smaxs[2] = v2[2];
709                 }
710         }
711         // now we have a bbox in viewspace
712         // clip it to the viewspace version of the sphere
713         v[0] = origin[0] - r_origin[0];
714         v[1] = origin[1] - r_origin[1];
715         v[2] = origin[2] - r_origin[2];
716         v2[0] = DotProduct(v, vright);
717         v2[1] = DotProduct(v, vup);
718         v2[2] = DotProduct(v, vpn);
719         if (smins[0] < v2[0] - radius) smins[0] = v2[0] - radius;
720         if (smaxs[0] < v2[0] - radius) smaxs[0] = v2[0] + radius;
721         if (smins[1] < v2[1] - radius) smins[1] = v2[1] - radius;
722         if (smaxs[1] < v2[1] - radius) smaxs[1] = v2[1] + radius;
723         if (smins[2] < v2[2] - radius) smins[2] = v2[2] - radius;
724         if (smaxs[2] < v2[2] - radius) smaxs[2] = v2[2] + radius;
725         // clip it to the view plane
726         if (smins[2] < 1)
727                 smins[2] = 1;
728         // return true if that culled the box
729         if (smins[2] >= smaxs[2])
730                 return true;
731         // ok some of it is infront of the view, transform each corner back to
732         // worldspace and then to screenspace and make screen rect
733         // initialize these variables just to avoid compiler warnings
734         x1 = y1 = x2 = y2 = 0;
735         for (i = 0;i < 8;i++)
736         {
737                 v2[0] = (i & 1) ? smins[0] : smaxs[0];
738                 v2[1] = (i & 2) ? smins[1] : smaxs[1];
739                 v2[2] = (i & 4) ? smins[2] : smaxs[2];
740                 v[0] = v2[0] * vright[0] + v2[1] * vup[0] + v2[2] * vpn[0] + r_origin[0];
741                 v[1] = v2[0] * vright[1] + v2[1] * vup[1] + v2[2] * vpn[1] + r_origin[1];
742                 v[2] = v2[0] * vright[2] + v2[1] * vup[2] + v2[2] * vpn[2] + r_origin[2];
743                 v[3] = 1.0f;
744                 GL_TransformToScreen(v, v2);
745                 //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]);
746                 x = v2[0];
747                 y = v2[1];
748                 if (i)
749                 {
750                         if (x1 > x) x1 = x;
751                         if (x2 < x) x2 = x;
752                         if (y1 > y) y1 = y;
753                         if (y2 < y) y2 = y;
754                 }
755                 else
756                 {
757                         x1 = x2 = x;
758                         y1 = y2 = y;
759                 }
760         }
761         /*
762         // this code doesn't handle boxes with any points behind view properly
763         x1 = 1000;x2 = -1000;
764         y1 = 1000;y2 = -1000;
765         for (i = 0;i < 8;i++)
766         {
767                 v[0] = (i & 1) ? mins[0] : maxs[0];
768                 v[1] = (i & 2) ? mins[1] : maxs[1];
769                 v[2] = (i & 4) ? mins[2] : maxs[2];
770                 v[3] = 1.0f;
771                 GL_TransformToScreen(v, v2);
772                 //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]);
773                 if (v2[2] > 0)
774                 {
775                         x = v2[0];
776                         y = v2[1];
777
778                         if (x1 > x) x1 = x;
779                         if (x2 < x) x2 = x;
780                         if (y1 > y) y1 = y;
781                         if (y2 < y) y2 = y;
782                 }
783         }
784         */
785         ix1 = x1 - 1.0f;
786         iy1 = y1 - 1.0f;
787         ix2 = x2 + 1.0f;
788         iy2 = y2 + 1.0f;
789         //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
790         if (ix1 < r_refdef.x) ix1 = r_refdef.x;
791         if (iy1 < r_refdef.y) iy1 = r_refdef.y;
792         if (ix2 > r_refdef.x + r_refdef.width) ix2 = r_refdef.x + r_refdef.width;
793         if (iy2 > r_refdef.y + r_refdef.height) iy2 = r_refdef.y + r_refdef.height;
794         if (ix2 <= ix1 || iy2 <= iy1)
795                 return true;
796         // set up the scissor rectangle
797         qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
798         qglEnable(GL_SCISSOR_TEST);
799         c_rt_scissored++;
800         return false;
801 }
802 #endif
803
804 int R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
805 {
806         int i, ix1, iy1, ix2, iy2;
807         float x1, y1, x2, y2, x, y, f;
808         vec3_t smins, smaxs;
809         vec4_t v, v2;
810         if (!r_shadow_scissor.integer)
811                 return false;
812         // if view is inside the box, just say yes it's visible
813         if (BoxesOverlap(r_origin, r_origin, mins, maxs))
814         {
815                 qglDisable(GL_SCISSOR_TEST);
816                 return false;
817         }
818         for (i = 0;i < 3;i++)
819         {
820                 if (vpn[i] >= 0)
821                 {
822                         v[i] = mins[i];
823                         v2[i] = maxs[i];
824                 }
825                 else
826                 {
827                         v[i] = maxs[i];
828                         v2[i] = mins[i];
829                 }
830         }
831         f = DotProduct(vpn, r_origin) + 1;
832         if (DotProduct(vpn, v2) <= f)
833         {
834                 // entirely behind nearclip plane
835                 qglDisable(GL_SCISSOR_TEST);
836                 return false;
837         }
838         if (DotProduct(vpn, v) >= f)
839         {
840                 // entirely infront of nearclip plane
841                 x1 = y1 = x2 = y2 = 0;
842                 for (i = 0;i < 8;i++)
843                 {
844                         v[0] = (i & 1) ? mins[0] : maxs[0];
845                         v[1] = (i & 2) ? mins[1] : maxs[1];
846                         v[2] = (i & 4) ? mins[2] : maxs[2];
847                         v[3] = 1.0f;
848                         GL_TransformToScreen(v, v2);
849                         //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]);
850                         x = v2[0];
851                         y = v2[1];
852                         if (i)
853                         {
854                                 if (x1 > x) x1 = x;
855                                 if (x2 < x) x2 = x;
856                                 if (y1 > y) y1 = y;
857                                 if (y2 < y) y2 = y;
858                         }
859                         else
860                         {
861                                 x1 = x2 = x;
862                                 y1 = y2 = y;
863                         }
864                 }
865         }
866         else
867         {
868                 // clipped by nearclip plane
869                 // this is nasty and crude...
870                 // create viewspace bbox
871                 for (i = 0;i < 8;i++)
872                 {
873                         v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_origin[0];
874                         v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_origin[1];
875                         v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_origin[2];
876                         v2[0] = DotProduct(v, vright);
877                         v2[1] = DotProduct(v, vup);
878                         v2[2] = DotProduct(v, vpn);
879                         if (i)
880                         {
881                                 if (smins[0] > v2[0]) smins[0] = v2[0];
882                                 if (smaxs[0] < v2[0]) smaxs[0] = v2[0];
883                                 if (smins[1] > v2[1]) smins[1] = v2[1];
884                                 if (smaxs[1] < v2[1]) smaxs[1] = v2[1];
885                                 if (smins[2] > v2[2]) smins[2] = v2[2];
886                                 if (smaxs[2] < v2[2]) smaxs[2] = v2[2];
887                         }
888                         else
889                         {
890                                 smins[0] = smaxs[0] = v2[0];
891                                 smins[1] = smaxs[1] = v2[1];
892                                 smins[2] = smaxs[2] = v2[2];
893                         }
894                 }
895                 // now we have a bbox in viewspace
896                 // clip it to the view plane
897                 if (smins[2] < 1)
898                         smins[2] = 1;
899                 // return true if that culled the box
900                 if (smins[2] >= smaxs[2])
901                         return true;
902                 // ok some of it is infront of the view, transform each corner back to
903                 // worldspace and then to screenspace and make screen rect
904                 // initialize these variables just to avoid compiler warnings
905                 x1 = y1 = x2 = y2 = 0;
906                 for (i = 0;i < 8;i++)
907                 {
908                         v2[0] = (i & 1) ? smins[0] : smaxs[0];
909                         v2[1] = (i & 2) ? smins[1] : smaxs[1];
910                         v2[2] = (i & 4) ? smins[2] : smaxs[2];
911                         v[0] = v2[0] * vright[0] + v2[1] * vup[0] + v2[2] * vpn[0] + r_origin[0];
912                         v[1] = v2[0] * vright[1] + v2[1] * vup[1] + v2[2] * vpn[1] + r_origin[1];
913                         v[2] = v2[0] * vright[2] + v2[1] * vup[2] + v2[2] * vpn[2] + r_origin[2];
914                         v[3] = 1.0f;
915                         GL_TransformToScreen(v, v2);
916                         //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]);
917                         x = v2[0];
918                         y = v2[1];
919                         if (i)
920                         {
921                                 if (x1 > x) x1 = x;
922                                 if (x2 < x) x2 = x;
923                                 if (y1 > y) y1 = y;
924                                 if (y2 < y) y2 = y;
925                         }
926                         else
927                         {
928                                 x1 = x2 = x;
929                                 y1 = y2 = y;
930                         }
931                 }
932                 /*
933                 // this code doesn't handle boxes with any points behind view properly
934                 x1 = 1000;x2 = -1000;
935                 y1 = 1000;y2 = -1000;
936                 for (i = 0;i < 8;i++)
937                 {
938                         v[0] = (i & 1) ? mins[0] : maxs[0];
939                         v[1] = (i & 2) ? mins[1] : maxs[1];
940                         v[2] = (i & 4) ? mins[2] : maxs[2];
941                         v[3] = 1.0f;
942                         GL_TransformToScreen(v, v2);
943                         //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]);
944                         if (v2[2] > 0)
945                         {
946                                 x = v2[0];
947                                 y = v2[1];
948
949                                 if (x1 > x) x1 = x;
950                                 if (x2 < x) x2 = x;
951                                 if (y1 > y) y1 = y;
952                                 if (y2 < y) y2 = y;
953                         }
954                 }
955                 */
956         }
957         ix1 = x1 - 1.0f;
958         iy1 = y1 - 1.0f;
959         ix2 = x2 + 1.0f;
960         iy2 = y2 + 1.0f;
961         //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
962         if (ix1 < r_refdef.x) ix1 = r_refdef.x;
963         if (iy1 < r_refdef.y) iy1 = r_refdef.y;
964         if (ix2 > r_refdef.x + r_refdef.width) ix2 = r_refdef.x + r_refdef.width;
965         if (iy2 > r_refdef.y + r_refdef.height) iy2 = r_refdef.y + r_refdef.height;
966         if (ix2 <= ix1 || iy2 <= iy1)
967                 return true;
968         // set up the scissor rectangle
969         qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
970         qglEnable(GL_SCISSOR_TEST);
971         c_rt_scissored++;
972         return false;
973 }
974
975 void R_Shadow_VertexLighting(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const float *relativelightorigin, float lightradius)
976 {
977         float *color4f = varray_color4f;
978         float dist, dot, intensity, iradius = 1.0f / lightradius, radius2 = lightradius * lightradius, v[3];
979         for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
980         {
981                 VectorSubtract(vertex3f, relativelightorigin, v);
982                 if ((dot = DotProduct(normal3f, v)) > 0 && (dist = DotProduct(v, v)) < radius2)
983                 {
984                         dist = sqrt(dist);
985                         intensity = pow(1 - (dist * iradius), r_shadow_attenpower) * r_shadow_attenscale * dot / dist;
986                         VectorScale(lightcolor, intensity, color4f);
987                         color4f[3] = 1;
988                 }
989                 else
990                 {
991                         VectorClear(color4f);
992                         color4f[3] = 1;
993                 }
994         }
995 }
996
997 void R_Shadow_VertexLightingWithXYAttenuationTexture(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const float *relativelightorigin, float lightradius, const float *zdir)
998 {
999         float *color4f = varray_color4f;
1000         float dist, dot, intensity, iradius = 1.0f / lightradius, v[3];
1001         for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1002         {
1003                 VectorSubtract(vertex3f, relativelightorigin, v);
1004                 if ((dot = DotProduct(normal3f, v)) > 0 && (dist = fabs(DotProduct(zdir, v))) < lightradius)
1005                 {
1006                         intensity = pow(1 - (dist * iradius), r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(v,v));
1007                         VectorScale(lightcolor, intensity, color4f);
1008                         color4f[3] = 1;
1009                 }
1010                 else
1011                 {
1012                         VectorClear(color4f);
1013                         color4f[3] = 1;
1014                 }
1015         }
1016 }
1017
1018 // FIXME: this should be done in a vertex program when possible
1019 // FIXME: if vertex program not available, this would really benefit from 3DNow! or SSE
1020 void R_Shadow_Transform_Vertex3f_TexCoord3f(float *tc3f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1021 {
1022         do
1023         {
1024                 tc3f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1025                 tc3f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1026                 tc3f[2] = vertex3f[0] * matrix->m[2][0] + vertex3f[1] * matrix->m[2][1] + vertex3f[2] * matrix->m[2][2] + matrix->m[2][3];
1027                 vertex3f += 3;
1028                 tc3f += 3;
1029         }
1030         while (--numverts);
1031 }
1032
1033 void R_Shadow_Transform_Vertex3f_TexCoord2f(float *tc2f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1034 {
1035         do
1036         {
1037                 tc2f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1038                 tc2f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1039                 vertex3f += 3;
1040                 tc2f += 2;
1041         }
1042         while (--numverts);
1043 }
1044
1045 void R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(float *out3f, int numverts, const float *vertex3f, const float *svector3f, const float *tvector3f, const float *normal3f, const vec3_t relativelightorigin)
1046 {
1047         int i;
1048         float lightdir[3];
1049         for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1050         {
1051                 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1052                 // the cubemap normalizes this for us
1053                 out3f[0] = DotProduct(svector3f, lightdir);
1054                 out3f[1] = DotProduct(tvector3f, lightdir);
1055                 out3f[2] = DotProduct(normal3f, lightdir);
1056         }
1057 }
1058
1059 void R_Shadow_GenTexCoords_Specular_NormalCubeMap(float *out3f, int numverts, const float *vertex3f, const float *svector3f, const float *tvector3f, const float *normal3f, const vec3_t relativelightorigin, const vec3_t relativeeyeorigin)
1060 {
1061         int i;
1062         float lightdir[3], eyedir[3], halfdir[3];
1063         for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1064         {
1065                 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1066                 VectorNormalizeFast(lightdir);
1067                 VectorSubtract(vertex3f, relativeeyeorigin, eyedir);
1068                 VectorNormalizeFast(eyedir);
1069                 VectorAdd(lightdir, eyedir, halfdir);
1070                 // the cubemap normalizes this for us
1071                 out3f[0] = DotProduct(svector3f, halfdir);
1072                 out3f[1] = DotProduct(tvector3f, halfdir);
1073                 out3f[2] = DotProduct(normal3f, halfdir);
1074         }
1075 }
1076
1077 void R_Shadow_DiffuseLighting(int numverts, int numtriangles, const int *elements, const float *vertex3f, const float *svector3f, const float *tvector3f, const float *normal3f, const float *texcoord2f, const float *relativelightorigin, float lightradius, const float *lightcolor, const matrix4x4_t *matrix_modeltofilter, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *basetexture, rtexture_t *bumptexture, rtexture_t *lightcubemap)
1078 {
1079         int renders;
1080         float color[3], color2[3];
1081         rmeshstate_t m;
1082         memset(&m, 0, sizeof(m));
1083         if (gl_dot3arb && gl_texturecubemap && gl_combine.integer && gl_stencil)
1084         {
1085                 if (!bumptexture)
1086                         bumptexture = r_shadow_blankbumptexture;
1087                 // colorscale accounts for how much we multiply the brightness during combine
1088                 // mult is how many times the final pass of the lighting will be
1089                 // performed to get more brightness than otherwise possible
1090                 // limit mult to 64 for sanity sake
1091                 if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
1092                 {
1093                         // 3/2 3D combine path (Geforce3, Radeon 8500)
1094                         m.tex[0] = R_GetTexture(bumptexture);
1095                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1096                         m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
1097                         m.texcombinergb[0] = GL_REPLACE;
1098                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1099                         R_Mesh_TextureState(&m);
1100                         qglColorMask(0,0,0,1);
1101                         qglDisable(GL_BLEND);
1102                         GL_Color(1,1,1,1);
1103                         R_Mesh_GetSpace(numverts);
1104                         R_Mesh_CopyVertex3f(vertex3f, numverts);
1105                         R_Mesh_CopyTexCoord2f(0, texcoord2f, numverts);
1106                         R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1107                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1108                         R_Mesh_Draw(numverts, numtriangles, elements);
1109                         c_rt_lightmeshes++;
1110                         c_rt_lighttris += numtriangles;
1111
1112                         m.tex[0] = R_GetTexture(basetexture);
1113                         m.tex[1] = 0;
1114                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1115                         m.tex3d[2] = 0;
1116                         m.texcombinergb[0] = GL_MODULATE;
1117                         m.texcombinergb[1] = GL_MODULATE;
1118                         R_Mesh_TextureState(&m);
1119                         qglColorMask(1,1,1,0);
1120                         qglBlendFunc(GL_DST_ALPHA, GL_ONE);
1121                         qglEnable(GL_BLEND);
1122
1123                         VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1124                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1125                         {
1126                                 color[0] = bound(0, color2[0], 1);
1127                                 color[1] = bound(0, color2[1], 1);
1128                                 color[2] = bound(0, color2[2], 1);
1129                                 GL_Color(color[0], color[1], color[2], 1);
1130                                 R_Mesh_GetSpace(numverts);
1131                                 R_Mesh_CopyVertex3f(vertex3f, numverts);
1132                                 R_Mesh_CopyTexCoord2f(0, texcoord2f, numverts);
1133                                 if (lightcubemap)
1134                                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1135                                 R_Mesh_Draw(numverts, numtriangles, elements);
1136                                 c_rt_lightmeshes++;
1137                                 c_rt_lighttris += numtriangles;
1138                         }
1139                 }
1140                 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap)
1141                 {
1142                         // 1/2/2 3D combine path (original Radeon)
1143                         m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1144                         R_Mesh_TextureState(&m);
1145                         qglColorMask(0,0,0,1);
1146                         qglDisable(GL_BLEND);
1147                         GL_Color(1,1,1,1);
1148                         R_Mesh_GetSpace(numverts);
1149                         R_Mesh_CopyVertex3f(vertex3f, numverts);
1150                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1151                         R_Mesh_Draw(numverts, numtriangles, elements);
1152                         c_rt_lightmeshes++;
1153                         c_rt_lighttris += numtriangles;
1154
1155                         m.tex[0] = R_GetTexture(bumptexture);
1156                         m.tex3d[0] = 0;
1157                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1158                         m.texcombinergb[0] = GL_REPLACE;
1159                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1160                         R_Mesh_TextureState(&m);
1161                         qglBlendFunc(GL_DST_ALPHA, GL_ZERO);
1162                         qglEnable(GL_BLEND);
1163                         R_Mesh_GetSpace(numverts);
1164                         R_Mesh_CopyVertex3f(vertex3f, numverts);
1165                         R_Mesh_CopyTexCoord2f(0, texcoord2f, numverts);
1166                         R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1167                         R_Mesh_Draw(numverts, numtriangles, elements);
1168                         c_rt_lightmeshes++;
1169                         c_rt_lighttris += numtriangles;
1170
1171                         m.tex[0] = R_GetTexture(basetexture);
1172                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1173                         m.texcombinergb[0] = GL_MODULATE;
1174                         m.texcombinergb[1] = GL_MODULATE;
1175                         R_Mesh_TextureState(&m);
1176                         qglColorMask(1,1,1,0);
1177                         qglBlendFunc(GL_DST_ALPHA, GL_ONE);
1178
1179                         VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1180                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1181                         {
1182                                 color[0] = bound(0, color2[0], 1);
1183                                 color[1] = bound(0, color2[1], 1);
1184                                 color[2] = bound(0, color2[2], 1);
1185                                 GL_Color(color[0], color[1], color[2], 1);
1186                                 R_Mesh_GetSpace(numverts);
1187                                 R_Mesh_CopyVertex3f(vertex3f, numverts);
1188                                 R_Mesh_CopyTexCoord2f(0, texcoord2f, numverts);
1189                                 if (lightcubemap)
1190                                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1191                                 R_Mesh_Draw(numverts, numtriangles, elements);
1192                                 c_rt_lightmeshes++;
1193                                 c_rt_lighttris += numtriangles;
1194                         }
1195                 }
1196                 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap)
1197                 {
1198                         // 2/2 3D combine path (original Radeon)
1199                         m.tex[0] = R_GetTexture(bumptexture);
1200                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1201                         m.texcombinergb[0] = GL_REPLACE;
1202                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1203                         R_Mesh_TextureState(&m);
1204                         GL_Color(1,1,1,1);
1205                         qglColorMask(0,0,0,1);
1206                         qglDisable(GL_BLEND);
1207                         R_Mesh_GetSpace(numverts);
1208                         R_Mesh_CopyVertex3f(vertex3f, numverts);
1209                         R_Mesh_CopyTexCoord2f(0, texcoord2f, numverts);
1210                         R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1211                         R_Mesh_Draw(numverts, numtriangles, elements);
1212                         c_rt_lightmeshes++;
1213                         c_rt_lighttris += numtriangles;
1214
1215                         m.tex[0] = R_GetTexture(basetexture);
1216                         m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1217                         m.texcubemap[1] = 0;
1218                         m.texcombinergb[0] = GL_MODULATE;
1219                         m.texcombinergb[1] = GL_MODULATE;
1220                         R_Mesh_TextureState(&m);
1221                         qglColorMask(1,1,1,0);
1222                         qglBlendFunc(GL_DST_ALPHA, GL_ONE);
1223                         qglEnable(GL_BLEND);
1224
1225                         VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1226                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1227                         {
1228                                 color[0] = bound(0, color2[0], 1);
1229                                 color[1] = bound(0, color2[1], 1);
1230                                 color[2] = bound(0, color2[2], 1);
1231                                 GL_Color(color[0], color[1], color[2], 1);
1232                                 R_Mesh_GetSpace(numverts);
1233                                 R_Mesh_CopyVertex3f(vertex3f, numverts);
1234                                 R_Mesh_CopyTexCoord2f(0, texcoord2f, numverts);
1235                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1236                                 R_Mesh_Draw(numverts, numtriangles, elements);
1237                                 c_rt_lightmeshes++;
1238                                 c_rt_lighttris += numtriangles;
1239                         }
1240                 }
1241                 else if (r_textureunits.integer >= 4)
1242                 {
1243                         // 4/2 2D combine path (Geforce3, Radeon 8500)
1244                         m.tex[0] = R_GetTexture(bumptexture);
1245                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1246                         m.texcombinergb[0] = GL_REPLACE;
1247                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1248                         m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
1249                         m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
1250                         R_Mesh_TextureState(&m);
1251                         qglColorMask(0,0,0,1);
1252                         qglDisable(GL_BLEND);
1253                         GL_Color(1,1,1,1);
1254                         R_Mesh_GetSpace(numverts);
1255                         R_Mesh_CopyVertex3f(vertex3f, numverts);
1256                         R_Mesh_CopyTexCoord2f(0, texcoord2f, numverts);
1257                         R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1258                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1259                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[3], numverts, vertex3f, matrix_modeltoattenuationz);
1260                         R_Mesh_Draw(numverts, numtriangles, elements);
1261                         c_rt_lightmeshes++;
1262                         c_rt_lighttris += numtriangles;
1263
1264                         m.tex[0] = R_GetTexture(basetexture);
1265                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1266                         m.texcombinergb[0] = GL_MODULATE;
1267                         m.texcombinergb[1] = GL_MODULATE;
1268                         m.tex[2] = 0;
1269                         m.tex[3] = 0;
1270                         R_Mesh_TextureState(&m);
1271                         qglColorMask(1,1,1,0);
1272                         qglBlendFunc(GL_DST_ALPHA, GL_ONE);
1273                         qglEnable(GL_BLEND);
1274
1275                         VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1276                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1277                         {
1278                                 color[0] = bound(0, color2[0], 1);
1279                                 color[1] = bound(0, color2[1], 1);
1280                                 color[2] = bound(0, color2[2], 1);
1281                                 GL_Color(color[0], color[1], color[2], 1);
1282                                 R_Mesh_GetSpace(numverts);
1283                                 R_Mesh_CopyVertex3f(vertex3f, numverts);
1284                                 R_Mesh_CopyTexCoord2f(0, texcoord2f, numverts);
1285                                 if (lightcubemap)
1286                                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1287                                 R_Mesh_Draw(numverts, numtriangles, elements);
1288                                 c_rt_lightmeshes++;
1289                                 c_rt_lighttris += numtriangles;
1290                         }
1291                 }
1292                 else
1293                 {
1294                         // 2/2/2 2D combine path (any dot3 card)
1295                         m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1296                         m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1297                         R_Mesh_TextureState(&m);
1298                         qglColorMask(0,0,0,1);
1299                         qglDisable(GL_BLEND);
1300                         GL_Color(1,1,1,1);
1301                         R_Mesh_GetSpace(numverts);
1302                         R_Mesh_CopyVertex3f(vertex3f, numverts);
1303                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1304                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1305                         R_Mesh_Draw(numverts, numtriangles, elements);
1306                         c_rt_lightmeshes++;
1307                         c_rt_lighttris += numtriangles;
1308
1309                         m.tex[0] = R_GetTexture(bumptexture);
1310                         m.tex[1] = 0;
1311                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1312                         m.texcombinergb[0] = GL_REPLACE;
1313                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1314                         R_Mesh_TextureState(&m);
1315                         qglBlendFunc(GL_DST_ALPHA, GL_ZERO);
1316                         qglEnable(GL_BLEND);
1317                         R_Mesh_GetSpace(numverts);
1318                         R_Mesh_CopyVertex3f(vertex3f, numverts);
1319                         R_Mesh_CopyTexCoord2f(0, texcoord2f, numverts);
1320                         R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1321                         R_Mesh_Draw(numverts, numtriangles, elements);
1322                         c_rt_lightmeshes++;
1323                         c_rt_lighttris += numtriangles;
1324
1325                         m.tex[0] = R_GetTexture(basetexture);
1326                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1327                         m.texcombinergb[0] = GL_MODULATE;
1328                         m.texcombinergb[1] = GL_MODULATE;
1329                         R_Mesh_TextureState(&m);
1330                         qglColorMask(1,1,1,0);
1331                         qglBlendFunc(GL_DST_ALPHA, GL_ONE);
1332
1333                         VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1334                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1335                         {
1336                                 color[0] = bound(0, color2[0], 1);
1337                                 color[1] = bound(0, color2[1], 1);
1338                                 color[2] = bound(0, color2[2], 1);
1339                                 GL_Color(color[0], color[1], color[2], 1);
1340                                 R_Mesh_GetSpace(numverts);
1341                                 R_Mesh_CopyVertex3f(vertex3f, numverts);
1342                                 R_Mesh_CopyTexCoord2f(0, texcoord2f, numverts);
1343                                 if (lightcubemap)
1344                                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1345                                 R_Mesh_Draw(numverts, numtriangles, elements);
1346                                 c_rt_lightmeshes++;
1347                                 c_rt_lighttris += numtriangles;
1348                         }
1349                 }
1350         }
1351         else
1352         {
1353                 if (r_textureunits.integer >= 2)
1354                 {
1355                         // voodoo2
1356 #if 1
1357                         m.tex[0] = R_GetTexture(basetexture);
1358                         m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1359                         R_Mesh_TextureState(&m);
1360                         qglBlendFunc(GL_SRC_ALPHA, GL_ONE);
1361                         qglEnable(GL_BLEND);
1362 #else
1363                         m.tex[0] = R_GetTexture(basetexture);
1364                         m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1365                         m.blendfunc1 = GL_SRC_ALPHA;
1366                         m.blendfunc2 = GL_ONE;
1367                         R_Mesh_State(&m);
1368 #endif
1369                         VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1370                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1371                         {
1372                                 color[0] = bound(0, color2[0], 1);
1373                                 color[1] = bound(0, color2[1], 1);
1374                                 color[2] = bound(0, color2[2], 1);
1375                                 GL_UseColorArray();
1376                                 R_Mesh_GetSpace(numverts);
1377                                 R_Mesh_CopyVertex3f(vertex3f, numverts);
1378                                 R_Mesh_CopyTexCoord2f(0, texcoord2f, numverts);
1379                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1380                                 R_Shadow_VertexLightingWithXYAttenuationTexture(numverts, vertex3f, normal3f, color2, relativelightorigin, lightradius, matrix_modeltofilter->m[2]);
1381                                 R_Mesh_Draw(numverts, numtriangles, elements);
1382                         }
1383                 }
1384                 else
1385                 {
1386                         // voodoo1
1387 #if 1
1388                         m.tex[0] = R_GetTexture(basetexture);
1389                         R_Mesh_TextureState(&m);
1390                         qglBlendFunc(GL_SRC_ALPHA, GL_ONE);
1391                         qglEnable(GL_BLEND);
1392 #else
1393                         m.tex[0] = R_GetTexture(basetexture);
1394                         m.blendfunc1 = GL_SRC_ALPHA;
1395                         m.blendfunc2 = GL_ONE;
1396                         R_Mesh_State(&m);
1397 #endif
1398                         VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1399                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1400                         {
1401                                 color[0] = bound(0, color2[0], 1);
1402                                 color[1] = bound(0, color2[1], 1);
1403                                 color[2] = bound(0, color2[2], 1);
1404                                 GL_UseColorArray();
1405                                 R_Mesh_GetSpace(numverts);
1406                                 R_Mesh_CopyVertex3f(vertex3f, numverts);
1407                                 R_Mesh_CopyTexCoord2f(0, texcoord2f, numverts);
1408                                 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color);
1409                                 R_Shadow_VertexLighting(numverts, vertex3f, normal3f, color, relativelightorigin, lightradius);
1410                                 R_Mesh_Draw(numverts, numtriangles, elements);
1411                         }
1412                 }
1413         }
1414 }
1415
1416 void R_Shadow_SpecularLighting(int numverts, int numtriangles, const int *elements, const float *vertex3f, const float *svector3f, const float *tvector3f, const float *normal3f, const float *texcoord2f, const float *relativelightorigin, const float *relativeeyeorigin, float lightradius, const float *lightcolor, const matrix4x4_t *matrix_modeltofilter, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *glosstexture, rtexture_t *bumptexture, rtexture_t *lightcubemap)
1417 {
1418         int renders;
1419         float color[3], color2[3];
1420         rmeshstate_t m;
1421         if (!gl_dot3arb || !gl_texturecubemap || !gl_combine.integer || !gl_stencil)
1422                 return;
1423         memset(&m, 0, sizeof(m));
1424         if (!bumptexture)
1425                 bumptexture = r_shadow_blankbumptexture;
1426         if (!glosstexture)
1427                 glosstexture = r_shadow_blankglosstexture;
1428         if (r_shadow_gloss.integer >= 2 || (r_shadow_gloss.integer >= 1 && glosstexture != r_shadow_blankglosstexture))
1429         {
1430                 if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1431                 {
1432                         // 2/0/0/1/2 3D combine blendsquare path
1433                         m.tex[0] = R_GetTexture(bumptexture);
1434                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1435                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1436                         R_Mesh_TextureState(&m);
1437                         qglColorMask(0,0,0,1);
1438                         // this squares the result
1439                         qglEnable(GL_BLEND);
1440                         qglBlendFunc(GL_SRC_ALPHA, GL_ZERO);
1441                         GL_Color(1,1,1,1);
1442                         R_Mesh_GetSpace(numverts);
1443                         R_Mesh_CopyVertex3f(vertex3f, numverts);
1444                         R_Mesh_CopyTexCoord2f(0, texcoord2f, numverts);
1445                         R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1446                         R_Mesh_Draw(numverts, numtriangles, elements);
1447                         c_rt_lightmeshes++;
1448                         c_rt_lighttris += numtriangles;
1449
1450                         m.tex[0] = 0;
1451                         m.texcubemap[1] = 0;
1452                         m.texcombinergb[1] = GL_MODULATE;
1453                         R_Mesh_TextureState(&m);
1454                         // square alpha in framebuffer a few times to make it shiny
1455                         qglBlendFunc(GL_ZERO, GL_DST_ALPHA);
1456                         // these comments are a test run through this math for intensity 0.5
1457                         // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1458                         // 0.25 * 0.25 = 0.0625 (this is another pass)
1459                         // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1460                         for (renders = 0;renders < 2;renders++)
1461                         {
1462                                 R_Mesh_GetSpace(numverts);
1463                                 R_Mesh_CopyVertex3f(vertex3f, numverts);
1464                                 R_Mesh_Draw(numverts, numtriangles, elements);
1465                         }
1466                         c_rt_lightmeshes += 3;
1467                         c_rt_lighttris += numtriangles * 3;
1468
1469                         m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1470                         R_Mesh_TextureState(&m);
1471                         qglBlendFunc(GL_DST_ALPHA, GL_ZERO);
1472                         R_Mesh_GetSpace(numverts);
1473                         R_Mesh_CopyVertex3f(vertex3f, numverts);
1474                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1475                         R_Mesh_Draw(numverts, numtriangles, elements);
1476                         c_rt_lightmeshes++;
1477                         c_rt_lighttris += numtriangles;
1478
1479                         m.tex3d[0] = 0;
1480                         m.tex[0] = R_GetTexture(glosstexture);
1481                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1482                         R_Mesh_TextureState(&m);
1483                         qglColorMask(1,1,1,0);
1484                         qglBlendFunc(GL_DST_ALPHA, GL_ONE);
1485
1486                         VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value * 0.25f, color2);
1487                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1488                         {
1489                                 color[0] = bound(0, color2[0], 1);
1490                                 color[1] = bound(0, color2[1], 1);
1491                                 color[2] = bound(0, color2[2], 1);
1492                                 GL_Color(color[0], color[1], color[2], 1);
1493                                 R_Mesh_GetSpace(numverts);
1494                                 R_Mesh_CopyVertex3f(vertex3f, numverts);
1495                                 R_Mesh_CopyTexCoord2f(0, texcoord2f, numverts);
1496                                 if (lightcubemap)
1497                                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1498                                 R_Mesh_Draw(numverts, numtriangles, elements);
1499                                 c_rt_lightmeshes++;
1500                                 c_rt_lighttris += numtriangles;
1501                         }
1502                 }
1503                 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1504                 {
1505                         // 2/0/0/2 3D combine blendsquare path
1506                         m.tex[0] = R_GetTexture(bumptexture);
1507                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1508                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1509                         R_Mesh_TextureState(&m);
1510                         qglColorMask(0,0,0,1);
1511                         // this squares the result
1512                         qglEnable(GL_BLEND);
1513                         qglBlendFunc(GL_SRC_ALPHA, GL_ZERO);
1514                         GL_Color(1,1,1,1);
1515                         R_Mesh_GetSpace(numverts);
1516                         R_Mesh_CopyVertex3f(vertex3f, numverts);
1517                         R_Mesh_CopyTexCoord2f(0, texcoord2f, numverts);
1518                         R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1519                         R_Mesh_Draw(numverts, numtriangles, elements);
1520                         c_rt_lightmeshes++;
1521                         c_rt_lighttris += numtriangles;
1522
1523                         m.tex[0] = 0;
1524                         m.texcubemap[1] = 0;
1525                         m.texcombinergb[1] = GL_MODULATE;
1526                         R_Mesh_TextureState(&m);
1527                         // square alpha in framebuffer a few times to make it shiny
1528                         qglBlendFunc(GL_ZERO, GL_DST_ALPHA);
1529                         // these comments are a test run through this math for intensity 0.5
1530                         // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1531                         // 0.25 * 0.25 = 0.0625 (this is another pass)
1532                         // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1533                         for (renders = 0;renders < 2;renders++)
1534                         {
1535                                 R_Mesh_GetSpace(numverts);
1536                                 R_Mesh_CopyVertex3f(vertex3f, numverts);
1537                                 R_Mesh_Draw(numverts, numtriangles, elements);
1538                         }
1539                         c_rt_lightmeshes += 3;
1540                         c_rt_lighttris += numtriangles * 3;
1541
1542                         R_Mesh_CopyVertex3f(vertex3f, numverts);
1543                         R_Mesh_CopyTexCoord2f(0, texcoord2f, numverts);
1544                         m.tex[0] = R_GetTexture(glosstexture);
1545                         m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1546                         R_Mesh_TextureState(&m);
1547                         qglColorMask(1,1,1,0);
1548                         qglBlendFunc(GL_DST_ALPHA, GL_ONE);
1549                         c_rt_lightmeshes++;
1550                         c_rt_lighttris += numtriangles;
1551
1552                         VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value * 0.25f, color2);
1553                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1554                         {
1555                                 color[0] = bound(0, color2[0], 1);
1556                                 color[1] = bound(0, color2[1], 1);
1557                                 color[2] = bound(0, color2[2], 1);
1558                                 GL_Color(color[0], color[1], color[2], 1);
1559                                 R_Mesh_GetSpace(numverts);
1560                                 R_Mesh_CopyVertex3f(vertex3f, numverts);
1561                                 R_Mesh_CopyTexCoord2f(0, texcoord2f, numverts);
1562                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1563                                 R_Mesh_Draw(numverts, numtriangles, elements);
1564                                 c_rt_lightmeshes++;
1565                                 c_rt_lighttris += numtriangles;
1566                         }
1567                 }
1568                 else if (r_textureunits.integer >= 2 /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1569                 {
1570                         // 2/0/0/2/2 2D combine blendsquare path
1571                         m.tex[0] = R_GetTexture(bumptexture);
1572                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1573                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1574                         R_Mesh_TextureState(&m);
1575                         qglColorMask(0,0,0,1);
1576                         // this squares the result
1577                         qglEnable(GL_BLEND);
1578                         qglBlendFunc(GL_SRC_ALPHA, GL_ZERO);
1579                         GL_Color(1,1,1,1);
1580                         R_Mesh_GetSpace(numverts);
1581                         R_Mesh_CopyVertex3f(vertex3f, numverts);
1582                         R_Mesh_CopyTexCoord2f(0, texcoord2f, numverts);
1583                         R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1584                         R_Mesh_Draw(numverts, numtriangles, elements);
1585                         c_rt_lightmeshes++;
1586                         c_rt_lighttris += numtriangles;
1587
1588                         m.tex[0] = 0;
1589                         m.texcubemap[1] = 0;
1590                         m.texcombinergb[1] = GL_MODULATE;
1591                         R_Mesh_TextureState(&m);
1592                         // square alpha in framebuffer a few times to make it shiny
1593                         qglBlendFunc(GL_ZERO, GL_DST_ALPHA);
1594                         // these comments are a test run through this math for intensity 0.5
1595                         // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1596                         // 0.25 * 0.25 = 0.0625 (this is another pass)
1597                         // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1598                         for (renders = 0;renders < 2;renders++)
1599                         {
1600                                 R_Mesh_GetSpace(numverts);
1601                                 R_Mesh_CopyVertex3f(vertex3f, numverts);
1602                                 R_Mesh_Draw(numverts, numtriangles, elements);
1603                         }
1604                         c_rt_lightmeshes += 3;
1605                         c_rt_lighttris += numtriangles * 3;
1606
1607                         m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1608                         m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1609                         R_Mesh_TextureState(&m);
1610                         qglBlendFunc(GL_DST_ALPHA, GL_ZERO);
1611                         R_Mesh_GetSpace(numverts);
1612                         R_Mesh_CopyVertex3f(vertex3f, numverts);
1613                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1614                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1615                         R_Mesh_Draw(numverts, numtriangles, elements);
1616                         c_rt_lightmeshes++;
1617                         c_rt_lighttris += numtriangles;
1618
1619                         m.tex[0] = R_GetTexture(glosstexture);
1620                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1621                         R_Mesh_TextureState(&m);
1622                         qglColorMask(1,1,1,0);
1623                         qglBlendFunc(GL_DST_ALPHA, GL_ONE);
1624
1625                         VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value * 0.25f, color2);
1626                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1627                         {
1628                                 color[0] = bound(0, color2[0], 1);
1629                                 color[1] = bound(0, color2[1], 1);
1630                                 color[2] = bound(0, color2[2], 1);
1631                                 GL_Color(color[0], color[1], color[2], 1);
1632                                 R_Mesh_GetSpace(numverts);
1633                                 R_Mesh_CopyVertex3f(vertex3f, numverts);
1634                                 R_Mesh_CopyTexCoord2f(0, texcoord2f, numverts);
1635                                 if (lightcubemap)
1636                                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1637                                 R_Mesh_Draw(numverts, numtriangles, elements);
1638                                 c_rt_lightmeshes++;
1639                                 c_rt_lighttris += numtriangles;
1640                         }
1641                 }
1642         }
1643 }
1644
1645 void R_Shadow_DrawWorldLightShadowVolume(matrix4x4_t *matrix, worldlight_t *light)
1646 {
1647         R_Mesh_Matrix(matrix);
1648         R_Shadow_RenderShadowMeshVolume(light->shadowvolume);
1649 }
1650
1651 cvar_t r_editlights = {0, "r_editlights", "0"};
1652 cvar_t r_editlights_cursordistance = {0, "r_editlights_distance", "1024"};
1653 cvar_t r_editlights_cursorpushback = {0, "r_editlights_pushback", "0"};
1654 cvar_t r_editlights_cursorpushoff = {0, "r_editlights_pushoff", "4"};
1655 cvar_t r_editlights_cursorgrid = {0, "r_editlights_grid", "4"};
1656 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "0.8"};
1657 cvar_t r_editlights_rtlightssizescale = {CVAR_SAVE, "r_editlights_rtlightssizescale", "0.7"};
1658 cvar_t r_editlights_rtlightscolorscale = {CVAR_SAVE, "r_editlights_rtlightscolorscale", "2"};
1659 worldlight_t *r_shadow_worldlightchain;
1660 worldlight_t *r_shadow_selectedlight;
1661 vec3_t r_editlights_cursorlocation;
1662
1663 static int castshadowcount = 1;
1664 void R_Shadow_NewWorldLight(vec3_t origin, float radius, vec3_t color, int style, const char *cubemapname, int castshadow)
1665 {
1666         int i, j, k, l, maxverts = 256, *mark, tris;
1667         float *vertex3f = NULL;
1668         worldlight_t *e;
1669         shadowmesh_t *mesh, *castmesh;
1670         mleaf_t *leaf;
1671         msurface_t *surf;
1672         qbyte *pvs;
1673         surfmesh_t *surfmesh;
1674
1675         if (radius < 15 || DotProduct(color, color) < 0.03)
1676         {
1677                 Con_Printf("R_Shadow_NewWorldLight: refusing to create a light too small/dim\n");
1678                 return;
1679         }
1680
1681         e = Mem_Alloc(r_shadow_mempool, sizeof(worldlight_t));
1682         VectorCopy(origin, e->origin);
1683         VectorCopy(color, e->light);
1684         e->lightradius = radius;
1685         e->style = style;
1686         if (e->style < 0 || e->style >= MAX_LIGHTSTYLES)
1687         {
1688                 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", e->style, MAX_LIGHTSTYLES);
1689                 e->style = 0;
1690         }
1691         e->castshadows = castshadow;
1692
1693         e->cullradius = e->lightradius;
1694         for (k = 0;k < 3;k++)
1695         {
1696                 e->mins[k] = e->origin[k] - e->lightradius;
1697                 e->maxs[k] = e->origin[k] + e->lightradius;
1698         }
1699
1700         e->next = r_shadow_worldlightchain;
1701         r_shadow_worldlightchain = e;
1702         if (cubemapname && cubemapname[0])
1703         {
1704                 e->cubemapname = Mem_Alloc(r_shadow_mempool, strlen(cubemapname) + 1);
1705                 strcpy(e->cubemapname, cubemapname);
1706                 // FIXME: add cubemap loading (and don't load a cubemap twice)
1707         }
1708         if (cl.worldmodel)
1709         {
1710                 castshadowcount++;
1711                 i = Mod_PointContents(e->origin, cl.worldmodel);
1712                 if (r_shadow_portallight.integer && i != CONTENTS_SOLID && i != CONTENTS_SKY)
1713                 {
1714                         qbyte *byteleafpvs;
1715                         qbyte *bytesurfacepvs;
1716
1717                         byteleafpvs = Mem_Alloc(tempmempool, cl.worldmodel->numleafs + 1);
1718                         bytesurfacepvs = Mem_Alloc(tempmempool, cl.worldmodel->numsurfaces);
1719
1720                         Portal_Visibility(cl.worldmodel, e->origin, byteleafpvs, bytesurfacepvs, NULL, 0, true, RadiusFromBoundsAndOrigin(e->mins, e->maxs, e->origin));
1721
1722                         for (i = 0, leaf = cl.worldmodel->leafs + 1;i < cl.worldmodel->numleafs;i++, leaf++)
1723                                 if (byteleafpvs[i+1] && BoxesOverlap(leaf->mins, leaf->maxs, e->mins, e->maxs))
1724                                         leaf->worldnodeframe = castshadowcount;
1725
1726                         for (i = 0, surf = cl.worldmodel->surfaces;i < cl.worldmodel->numsurfaces;i++, surf++)
1727                                 if (bytesurfacepvs[i] && BoxesOverlap(surf->poly_mins, surf->poly_maxs, e->mins, e->maxs))
1728                                         surf->castshadow = castshadowcount;
1729
1730                         Mem_Free(byteleafpvs);
1731                         Mem_Free(bytesurfacepvs);
1732                 }
1733                 else
1734                 {
1735                         leaf = Mod_PointInLeaf(origin, cl.worldmodel);
1736                         pvs = Mod_LeafPVS(leaf, cl.worldmodel);
1737                         for (i = 0, leaf = cl.worldmodel->leafs + 1;i < cl.worldmodel->numleafs;i++, leaf++)
1738                         {
1739                                 if (pvs[i >> 3] & (1 << (i & 7)) && BoxesOverlap(leaf->mins, leaf->maxs, e->mins, e->maxs))
1740                                 {
1741                                         leaf->worldnodeframe = castshadowcount;
1742                                         for (j = 0, mark = leaf->firstmarksurface;j < leaf->nummarksurfaces;j++, mark++)
1743                                         {
1744                                                 surf = cl.worldmodel->surfaces + *mark;
1745                                                 if (surf->castshadow != castshadowcount && BoxesOverlap(surf->poly_mins, surf->poly_maxs, e->mins, e->maxs))
1746                                                         surf->castshadow = castshadowcount;
1747                                         }
1748                                 }
1749                         }
1750                 }
1751
1752                 e->numleafs = 0;
1753                 for (i = 0, leaf = cl.worldmodel->leafs + 1;i < cl.worldmodel->numleafs;i++, leaf++)
1754                         if (leaf->worldnodeframe == castshadowcount)
1755                                 e->numleafs++;
1756                 e->numsurfaces = 0;
1757                 for (i = 0, surf = cl.worldmodel->surfaces + cl.worldmodel->firstmodelsurface;i < cl.worldmodel->nummodelsurfaces;i++, surf++)
1758                         if (surf->castshadow == castshadowcount)
1759                                 e->numsurfaces++;
1760
1761                 if (e->numleafs)
1762                         e->leafs = Mem_Alloc(r_shadow_mempool, e->numleafs * sizeof(mleaf_t *));
1763                 if (e->numsurfaces)
1764                         e->surfaces = Mem_Alloc(r_shadow_mempool, e->numsurfaces * sizeof(msurface_t *));
1765                 e->numleafs = 0;
1766                 for (i = 0, leaf = cl.worldmodel->leafs + 1;i < cl.worldmodel->numleafs;i++, leaf++)
1767                         if (leaf->worldnodeframe == castshadowcount)
1768                                 e->leafs[e->numleafs++] = leaf;
1769                 e->numsurfaces = 0;
1770                 for (i = 0, surf = cl.worldmodel->surfaces + cl.worldmodel->firstmodelsurface;i < cl.worldmodel->nummodelsurfaces;i++, surf++)
1771                         if (surf->castshadow == castshadowcount)
1772                                 e->surfaces[e->numsurfaces++] = surf;
1773
1774                 // find bounding box of lit leafs
1775                 VectorCopy(e->origin, e->mins);
1776                 VectorCopy(e->origin, e->maxs);
1777                 for (j = 0;j < e->numleafs;j++)
1778                 {
1779                         leaf = e->leafs[j];
1780                         for (k = 0;k < 3;k++)
1781                         {
1782                                 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
1783                                 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
1784                         }
1785                 }
1786
1787                 for (k = 0;k < 3;k++)
1788                 {
1789                         if (e->mins[k] < e->origin[k] - e->lightradius) e->mins[k] = e->origin[k] - e->lightradius;
1790                         if (e->maxs[k] > e->origin[k] + e->lightradius) e->maxs[k] = e->origin[k] + e->lightradius;
1791                 }
1792                 e->cullradius = RadiusFromBoundsAndOrigin(e->mins, e->maxs, e->origin);
1793
1794                 if (e->castshadows)
1795                 {
1796                         castshadowcount++;
1797                         for (j = 0;j < e->numsurfaces;j++)
1798                         {
1799                                 surf = e->surfaces[j];
1800                                 if (surf->flags & SURF_SHADOWCAST)
1801                                 {
1802                                         surf->castshadow = castshadowcount;
1803                                         if (maxverts < surf->poly_numverts)
1804                                                 maxverts = surf->poly_numverts;
1805                                 }
1806                         }
1807                         e->shadowvolume = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768);
1808                         // make a mesh to cast a shadow volume from
1809                         castmesh = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768);
1810                         for (j = 0;j < e->numsurfaces;j++)
1811                                 if (e->surfaces[j]->castshadow == castshadowcount)
1812                                         for (surfmesh = e->surfaces[j]->mesh;surfmesh;surfmesh = surfmesh->chain)
1813                                                 Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, surfmesh->numverts, surfmesh->vertex3f, surfmesh->numtriangles, surfmesh->element3i);
1814                         castmesh = Mod_ShadowMesh_Finish(r_shadow_mempool, castmesh);
1815
1816                         // cast shadow volume from castmesh
1817                         for (mesh = castmesh;mesh;mesh = mesh->next)
1818                         {
1819                                 R_Shadow_ResizeTriangleFacingLight(castmesh->numtriangles);
1820                                 R_Shadow_ResizeShadowElements(castmesh->numtriangles);
1821
1822                                 if (maxverts < castmesh->numverts * 2)
1823                                 {
1824                                         maxverts = castmesh->numverts * 2;
1825                                         if (vertex3f)
1826                                                 Mem_Free(vertex3f);
1827                                         vertex3f = NULL;
1828                                 }
1829                                 if (vertex3f == NULL && maxverts > 0)
1830                                         vertex3f = Mem_Alloc(r_shadow_mempool, maxverts * sizeof(float[3]));
1831
1832                                 // now that we have the buffers big enough, construct shadow volume mesh
1833                                 memcpy(vertex3f, castmesh->vertex3f, castmesh->numverts * sizeof(float[3]));
1834                                 tris = R_Shadow_MakeTriangleShadowFlags_Vertex3f(castmesh->element3i, vertex3f, castmesh->numtriangles, trianglefacinglight, trianglefacinglightlist, e->origin);
1835                                 tris = R_Shadow_BuildShadowVolume(castmesh->element3i, castmesh->neighbor3i, castmesh->numverts, trianglefacinglight, trianglefacinglightlist, tris, shadowelements, vertex3f, e->origin, r_shadow_projectdistance.value);
1836                                 // add the constructed shadow volume mesh
1837                                 Mod_ShadowMesh_AddMesh(r_shadow_mempool, e->shadowvolume, castmesh->numverts, vertex3f, tris, shadowelements);
1838                         }
1839                         if (vertex3f)
1840                                 Mem_Free(vertex3f);
1841                         vertex3f = NULL;
1842                         // we're done with castmesh now
1843                         Mod_ShadowMesh_Free(castmesh);
1844                         e->shadowvolume = Mod_ShadowMesh_Finish(r_shadow_mempool, e->shadowvolume);
1845                         for (l = 0, mesh = e->shadowvolume;mesh;mesh = mesh->next)
1846                                 l += mesh->numtriangles;
1847                         Con_Printf("static shadow volume built containing %i triangles\n", l);
1848                 }
1849         }
1850         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);
1851 }
1852
1853 void R_Shadow_FreeWorldLight(worldlight_t *light)
1854 {
1855         worldlight_t **lightpointer;
1856         for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
1857         if (*lightpointer != light)
1858                 Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain\n");
1859         *lightpointer = light->next;
1860         if (light->cubemapname)
1861                 Mem_Free(light->cubemapname);
1862         if (light->shadowvolume)
1863                 Mod_ShadowMesh_Free(light->shadowvolume);
1864         if (light->surfaces)
1865                 Mem_Free(light->surfaces);
1866         if (light->leafs)
1867                 Mem_Free(light->leafs);
1868         Mem_Free(light);
1869 }
1870
1871 void R_Shadow_ClearWorldLights(void)
1872 {
1873         while (r_shadow_worldlightchain)
1874                 R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
1875         r_shadow_selectedlight = NULL;
1876 }
1877
1878 void R_Shadow_SelectLight(worldlight_t *light)
1879 {
1880         if (r_shadow_selectedlight)
1881                 r_shadow_selectedlight->selected = false;
1882         r_shadow_selectedlight = light;
1883         if (r_shadow_selectedlight)
1884                 r_shadow_selectedlight->selected = true;
1885 }
1886
1887
1888 void R_DrawLightSprite(int texnum, const vec3_t origin, vec_t scale, float cr, float cg, float cb, float ca)
1889 {
1890         rmeshstate_t m;
1891         float diff[3];
1892
1893         if (fogenabled)
1894         {
1895                 VectorSubtract(origin, r_origin, diff);
1896                 ca *= 1 - exp(fogdensity/DotProduct(diff,diff));
1897         }
1898
1899         memset(&m, 0, sizeof(m));
1900         m.blendfunc1 = GL_SRC_ALPHA;
1901         m.blendfunc2 = GL_ONE;
1902         m.tex[0] = texnum;
1903         R_Mesh_Matrix(&r_identitymatrix);
1904         R_Mesh_State(&m);
1905
1906         GL_Color(cr * r_colorscale, cg * r_colorscale, cb * r_colorscale, ca);
1907         R_DrawSpriteMesh(origin, vright, vup, scale, -scale, -scale, scale);
1908 }
1909
1910 void R_Shadow_DrawCursorCallback(const void *calldata1, int calldata2)
1911 {
1912         cachepic_t *pic;
1913         pic = Draw_CachePic("gfx/crosshair1.tga");
1914         if (pic)
1915                 R_DrawLightSprite(R_GetTexture(pic->tex), r_editlights_cursorlocation, r_editlights_cursorgrid.value * 0.5f, 1, 1, 1, 0.5);
1916 }
1917
1918 void R_Shadow_DrawLightSpriteCallback(const void *calldata1, int calldata2)
1919 {
1920         float intensity;
1921         const worldlight_t *light;
1922         light = calldata1;
1923         intensity = 0.5;
1924         if (light->selected)
1925                 intensity = 0.75 + 0.25 * sin(realtime * M_PI * 4.0);
1926         if (light->shadowvolume)
1927                 R_DrawLightSprite(calldata2, light->origin, 8, intensity, intensity, intensity, 0.5);
1928         else
1929                 R_DrawLightSprite(calldata2, light->origin, 8, intensity * 0.5, intensity * 0.5, intensity * 0.5, 0.5);
1930 }
1931
1932 void R_Shadow_DrawLightSprites(void)
1933 {
1934         int i, texnums[5];
1935         cachepic_t *pic;
1936         worldlight_t *light;
1937
1938         for (i = 0;i < 5;i++)
1939         {
1940                 pic = Draw_CachePic(va("gfx/crosshair%i.tga", i + 1));
1941                 if (pic)
1942                         texnums[i] = R_GetTexture(pic->tex);
1943                 else
1944                         texnums[i] = 0;
1945         }
1946
1947         for (light = r_shadow_worldlightchain;light;light = light->next)
1948                 R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSpriteCallback, light, texnums[((int) light) % 5]);
1949         R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursorCallback, NULL, 0);
1950 }
1951
1952 void R_Shadow_SelectLightInView(void)
1953 {
1954         float bestrating, rating, temp[3];
1955         worldlight_t *best, *light;
1956         best = NULL;
1957         bestrating = 0;
1958         for (light = r_shadow_worldlightchain;light;light = light->next)
1959         {
1960                 VectorSubtract(light->origin, r_refdef.vieworg, temp);
1961                 rating = (DotProduct(temp, vpn) / sqrt(DotProduct(temp, temp)));
1962                 if (rating >= 0.95)
1963                 {
1964                         rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
1965                         if (bestrating < rating && CL_TraceLine(light->origin, r_refdef.vieworg, NULL, NULL, 0, true, NULL) == 1.0f)
1966                         {
1967                                 bestrating = rating;
1968                                 best = light;
1969                         }
1970                 }
1971         }
1972         R_Shadow_SelectLight(best);
1973 }
1974
1975 void R_Shadow_LoadWorldLights(void)
1976 {
1977         int n, a, style, shadow;
1978         char name[MAX_QPATH], cubemapname[MAX_QPATH], *lightsstring, *s, *t;
1979         float origin[3], radius, color[3];
1980         if (cl.worldmodel == NULL)
1981         {
1982                 Con_Printf("No map loaded.\n");
1983                 return;
1984         }
1985         FS_StripExtension(cl.worldmodel->name, name);
1986         strcat(name, ".rtlights");
1987         lightsstring = FS_LoadFile(name, false);
1988         if (lightsstring)
1989         {
1990                 s = lightsstring;
1991                 n = 0;
1992                 while (*s)
1993                 {
1994                         t = s;
1995                         while (*s && *s != '\n')
1996                                 s++;
1997                         if (!*s)
1998                                 break;
1999                         *s = 0;
2000                         shadow = true;
2001                         // check for modifier flags
2002                         if (*t == '!')
2003                         {
2004                                 shadow = false;
2005                                 t++;
2006                         }
2007                         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);
2008                         if (a < 9)
2009                                 cubemapname[0] = 0;
2010                         *s = '\n';
2011                         if (a < 8)
2012                         {
2013                                 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);
2014                                 break;
2015                         }
2016                         VectorScale(color, r_editlights_rtlightscolorscale.value, color);
2017                         radius *= r_editlights_rtlightssizescale.value;
2018                         R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadow);
2019                         s++;
2020                         n++;
2021                 }
2022                 if (*s)
2023                         Con_Printf("invalid rtlights file \"%s\"\n", name);
2024                 Mem_Free(lightsstring);
2025         }
2026 }
2027
2028 void R_Shadow_SaveWorldLights(void)
2029 {
2030         worldlight_t *light;
2031         int bufchars, bufmaxchars;
2032         char *buf, *oldbuf;
2033         char name[MAX_QPATH];
2034         char line[1024];
2035         if (!r_shadow_worldlightchain)
2036                 return;
2037         if (cl.worldmodel == NULL)
2038         {
2039                 Con_Printf("No map loaded.\n");
2040                 return;
2041         }
2042         FS_StripExtension(cl.worldmodel->name, name);
2043         strcat(name, ".rtlights");
2044         bufchars = bufmaxchars = 0;
2045         buf = NULL;
2046         for (light = r_shadow_worldlightchain;light;light = light->next)
2047         {
2048                 sprintf(line, "%s%g %g %g %g %g %g %g %d %s\n", light->castshadows ? "" : "!", light->origin[0], light->origin[1], light->origin[2], light->lightradius / r_editlights_rtlightssizescale.value, light->light[0] / r_editlights_rtlightscolorscale.value, light->light[1] / r_editlights_rtlightscolorscale.value, light->light[2] / r_editlights_rtlightscolorscale.value, light->style, light->cubemapname ? light->cubemapname : "");
2049                 if (bufchars + strlen(line) > bufmaxchars)
2050                 {
2051                         bufmaxchars = bufchars + strlen(line) + 2048;
2052                         oldbuf = buf;
2053                         buf = Mem_Alloc(r_shadow_mempool, bufmaxchars);
2054                         if (oldbuf)
2055                         {
2056                                 if (bufchars)
2057                                         memcpy(buf, oldbuf, bufchars);
2058                                 Mem_Free(oldbuf);
2059                         }
2060                 }
2061                 if (strlen(line))
2062                 {
2063                         memcpy(buf + bufchars, line, strlen(line));
2064                         bufchars += strlen(line);
2065                 }
2066         }
2067         if (bufchars)
2068                 FS_WriteFile(name, buf, bufchars);
2069         if (buf)
2070                 Mem_Free(buf);
2071 }
2072
2073 void R_Shadow_LoadLightsFile(void)
2074 {
2075         int n, a, style;
2076         char name[MAX_QPATH], *lightsstring, *s, *t;
2077         float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
2078         if (cl.worldmodel == NULL)
2079         {
2080                 Con_Printf("No map loaded.\n");
2081                 return;
2082         }
2083         FS_StripExtension(cl.worldmodel->name, name);
2084         strcat(name, ".lights");
2085         lightsstring = FS_LoadFile(name, false);
2086         if (lightsstring)
2087         {
2088                 s = lightsstring;
2089                 n = 0;
2090                 while (*s)
2091                 {
2092                         t = s;
2093                         while (*s && *s != '\n')
2094                                 s++;
2095                         if (!*s)
2096                                 break;
2097                         *s = 0;
2098                         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);
2099                         *s = '\n';
2100                         if (a < 14)
2101                         {
2102                                 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);
2103                                 break;
2104                         }
2105                         radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
2106                         radius = bound(15, radius, 4096);
2107                         VectorScale(color, (2.0f / (8388608.0f)), color);
2108                         R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2109                         s++;
2110                         n++;
2111                 }
2112                 if (*s)
2113                         Con_Printf("invalid lights file \"%s\"\n", name);
2114                 Mem_Free(lightsstring);
2115         }
2116 }
2117
2118 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
2119 {
2120         int entnum, style, islight;
2121         char key[256], value[1024];
2122         float origin[3], radius, color[3], light, scale, originhack[3], overridecolor[3];
2123         const char *data;
2124
2125         if (cl.worldmodel == NULL)
2126         {
2127                 Con_Printf("No map loaded.\n");
2128                 return;
2129         }
2130         data = cl.worldmodel->entities;
2131         if (!data)
2132                 return;
2133         for (entnum = 0;COM_ParseToken(&data) && com_token[0] == '{';entnum++)
2134         {
2135                 light = 0;
2136                 origin[0] = origin[1] = origin[2] = 0;
2137                 originhack[0] = originhack[1] = originhack[2] = 0;
2138                 color[0] = color[1] = color[2] = 1;
2139                 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
2140                 scale = 1;
2141                 style = 0;
2142                 islight = false;
2143                 while (1)
2144                 {
2145                         if (!COM_ParseToken(&data))
2146                                 break; // error
2147                         if (com_token[0] == '}')
2148                                 break; // end of entity
2149                         if (com_token[0] == '_')
2150                                 strcpy(key, com_token + 1);
2151                         else
2152                                 strcpy(key, com_token);
2153                         while (key[strlen(key)-1] == ' ') // remove trailing spaces
2154                                 key[strlen(key)-1] = 0;
2155                         if (!COM_ParseToken(&data))
2156                                 break; // error
2157                         strcpy(value, com_token);
2158
2159                         // now that we have the key pair worked out...
2160                         if (!strcmp("light", key))
2161                                 light = atof(value);
2162                         else if (!strcmp("origin", key))
2163                                 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
2164                         else if (!strcmp("color", key))
2165                                 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
2166                         else if (!strcmp("wait", key))
2167                                 scale = atof(value);
2168                         else if (!strcmp("classname", key))
2169                         {
2170                                 if (!strncmp(value, "light", 5))
2171                                 {
2172                                         islight = true;
2173                                         if (!strcmp(value, "light_fluoro"))
2174                                         {
2175                                                 originhack[0] = 0;
2176                                                 originhack[1] = 0;
2177                                                 originhack[2] = 0;
2178                                                 overridecolor[0] = 1;
2179                                                 overridecolor[1] = 1;
2180                                                 overridecolor[2] = 1;
2181                                         }
2182                                         if (!strcmp(value, "light_fluorospark"))
2183                                         {
2184                                                 originhack[0] = 0;
2185                                                 originhack[1] = 0;
2186                                                 originhack[2] = 0;
2187                                                 overridecolor[0] = 1;
2188                                                 overridecolor[1] = 1;
2189                                                 overridecolor[2] = 1;
2190                                         }
2191                                         if (!strcmp(value, "light_globe"))
2192                                         {
2193                                                 originhack[0] = 0;
2194                                                 originhack[1] = 0;
2195                                                 originhack[2] = 0;
2196                                                 overridecolor[0] = 1;
2197                                                 overridecolor[1] = 0.8;
2198                                                 overridecolor[2] = 0.4;
2199                                         }
2200                                         if (!strcmp(value, "light_flame_large_yellow"))
2201                                         {
2202                                                 originhack[0] = 0;
2203                                                 originhack[1] = 0;
2204                                                 originhack[2] = 48;
2205                                                 overridecolor[0] = 1;
2206                                                 overridecolor[1] = 0.5;
2207                                                 overridecolor[2] = 0.1;
2208                                         }
2209                                         if (!strcmp(value, "light_flame_small_yellow"))
2210                                         {
2211                                                 originhack[0] = 0;
2212                                                 originhack[1] = 0;
2213                                                 originhack[2] = 40;
2214                                                 overridecolor[0] = 1;
2215                                                 overridecolor[1] = 0.5;
2216                                                 overridecolor[2] = 0.1;
2217                                         }
2218                                         if (!strcmp(value, "light_torch_small_white"))
2219                                         {
2220                                                 originhack[0] = 0;
2221                                                 originhack[1] = 0;
2222                                                 originhack[2] = 40;
2223                                                 overridecolor[0] = 1;
2224                                                 overridecolor[1] = 0.5;
2225                                                 overridecolor[2] = 0.1;
2226                                         }
2227                                         if (!strcmp(value, "light_torch_small_walltorch"))
2228                                         {
2229                                                 originhack[0] = 0;
2230                                                 originhack[1] = 0;
2231                                                 originhack[2] = 40;
2232                                                 overridecolor[0] = 1;
2233                                                 overridecolor[1] = 0.5;
2234                                                 overridecolor[2] = 0.1;
2235                                         }
2236                                 }
2237                         }
2238                         else if (!strcmp("style", key))
2239                                 style = atoi(value);
2240                 }
2241                 if (light <= 0 && islight)
2242                         light = 300;
2243                 radius = min(light * r_editlights_quakelightsizescale.value / scale, 1048576);
2244                 light = sqrt(bound(0, light, 1048576)) * (1.0f / 16.0f);
2245                 if (color[0] == 1 && color[1] == 1 && color[2] == 1)
2246                         VectorCopy(overridecolor, color);
2247                 VectorScale(color, light, color);
2248                 VectorAdd(origin, originhack, origin);
2249                 if (radius >= 15)
2250                         R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2251         }
2252 }
2253
2254
2255 void R_Shadow_SetCursorLocationForView(void)
2256 {
2257         vec_t dist, push, frac;
2258         vec3_t dest, endpos, normal;
2259         VectorMA(r_refdef.vieworg, r_editlights_cursordistance.value, vpn, dest);
2260         frac = CL_TraceLine(r_refdef.vieworg, dest, endpos, normal, 0, true, NULL);
2261         if (frac < 1)
2262         {
2263                 dist = frac * r_editlights_cursordistance.value;
2264                 push = r_editlights_cursorpushback.value;
2265                 if (push > dist)
2266                         push = dist;
2267                 push = -push;
2268                 VectorMA(endpos, push, vpn, endpos);
2269                 VectorMA(endpos, r_editlights_cursorpushoff.value, normal, endpos);
2270         }
2271         r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2272         r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2273         r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2274 }
2275
2276 void R_Shadow_UpdateLightingMode(void)
2277 {
2278         r_shadow_lightingmode = 0;
2279         if (r_shadow_realtime.integer)
2280         {
2281                 if (r_shadow_worldlightchain)
2282                         r_shadow_lightingmode = 2;
2283                 else
2284                         r_shadow_lightingmode = 1;
2285         }
2286 }
2287
2288 void R_Shadow_UpdateWorldLightSelection(void)
2289 {
2290         R_Shadow_SetCursorLocationForView();
2291         if (r_editlights.integer)
2292         {
2293                 R_Shadow_SelectLightInView();
2294                 R_Shadow_DrawLightSprites();
2295         }
2296         else
2297                 R_Shadow_SelectLight(NULL);
2298 }
2299
2300 void R_Shadow_EditLights_Clear_f(void)
2301 {
2302         R_Shadow_ClearWorldLights();
2303 }
2304
2305 void R_Shadow_EditLights_Reload_f(void)
2306 {
2307         r_shadow_reloadlights = true;
2308 }
2309
2310 void R_Shadow_EditLights_Save_f(void)
2311 {
2312         if (cl.worldmodel)
2313                 R_Shadow_SaveWorldLights();
2314 }
2315
2316 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
2317 {
2318         R_Shadow_ClearWorldLights();
2319         R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
2320 }
2321
2322 void R_Shadow_EditLights_ImportLightsFile_f(void)
2323 {
2324         R_Shadow_ClearWorldLights();
2325         R_Shadow_LoadLightsFile();
2326 }
2327
2328 void R_Shadow_EditLights_Spawn_f(void)
2329 {
2330         vec3_t color;
2331         if (!r_editlights.integer)
2332         {
2333                 Con_Printf("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
2334                 return;
2335         }
2336         if (Cmd_Argc() != 1)
2337         {
2338                 Con_Printf("r_editlights_spawn does not take parameters\n");
2339                 return;
2340         }
2341         color[0] = color[1] = color[2] = 1;
2342         R_Shadow_NewWorldLight(r_editlights_cursorlocation, 200, color, 0, NULL, true);
2343 }
2344
2345 void R_Shadow_EditLights_Edit_f(void)
2346 {
2347         vec3_t origin, color;
2348         vec_t radius;
2349         int style, shadows;
2350         char cubemapname[1024];
2351         if (!r_editlights.integer)
2352         {
2353                 Con_Printf("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
2354                 return;
2355         }
2356         if (!r_shadow_selectedlight)
2357         {
2358                 Con_Printf("No selected light.\n");
2359                 return;
2360         }
2361         VectorCopy(r_shadow_selectedlight->origin, origin);
2362         radius = r_shadow_selectedlight->lightradius;
2363         VectorCopy(r_shadow_selectedlight->light, color);
2364         style = r_shadow_selectedlight->style;
2365         if (r_shadow_selectedlight->cubemapname)
2366                 strcpy(cubemapname, r_shadow_selectedlight->cubemapname);
2367         else
2368                 cubemapname[0] = 0;
2369         shadows = r_shadow_selectedlight->castshadows;
2370         if (!strcmp(Cmd_Argv(1), "origin"))
2371         {
2372                 if (Cmd_Argc() != 5)
2373                 {
2374                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2375                         return;
2376                 }
2377                 origin[0] = atof(Cmd_Argv(2));
2378                 origin[1] = atof(Cmd_Argv(3));
2379                 origin[2] = atof(Cmd_Argv(4));
2380         }
2381         else if (!strcmp(Cmd_Argv(1), "originx"))
2382         {
2383                 if (Cmd_Argc() != 3)
2384                 {
2385                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2386                         return;
2387                 }
2388                 origin[0] = atof(Cmd_Argv(2));
2389         }
2390         else if (!strcmp(Cmd_Argv(1), "originy"))
2391         {
2392                 if (Cmd_Argc() != 3)
2393                 {
2394                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2395                         return;
2396                 }
2397                 origin[1] = atof(Cmd_Argv(2));
2398         }
2399         else if (!strcmp(Cmd_Argv(1), "originz"))
2400         {
2401                 if (Cmd_Argc() != 3)
2402                 {
2403                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2404                         return;
2405                 }
2406                 origin[2] = atof(Cmd_Argv(2));
2407         }
2408         else if (!strcmp(Cmd_Argv(1), "move"))
2409         {
2410                 if (Cmd_Argc() != 5)
2411                 {
2412                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2413                         return;
2414                 }
2415                 origin[0] += atof(Cmd_Argv(2));
2416                 origin[1] += atof(Cmd_Argv(3));
2417                 origin[2] += atof(Cmd_Argv(4));
2418         }
2419         else if (!strcmp(Cmd_Argv(1), "movex"))
2420         {
2421                 if (Cmd_Argc() != 3)
2422                 {
2423                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2424                         return;
2425                 }
2426                 origin[0] += atof(Cmd_Argv(2));
2427         }
2428         else if (!strcmp(Cmd_Argv(1), "movey"))
2429         {
2430                 if (Cmd_Argc() != 3)
2431                 {
2432                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2433                         return;
2434                 }
2435                 origin[1] += atof(Cmd_Argv(2));
2436         }
2437         else if (!strcmp(Cmd_Argv(1), "movez"))
2438         {
2439                 if (Cmd_Argc() != 3)
2440                 {
2441                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2442                         return;
2443                 }
2444                 origin[2] += atof(Cmd_Argv(2));
2445         }
2446         else if (!strcmp(Cmd_Argv(1), "color"))
2447         {
2448                 if (Cmd_Argc() != 5)
2449                 {
2450                         Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(0));
2451                         return;
2452                 }
2453                 color[0] = atof(Cmd_Argv(2));
2454                 color[1] = atof(Cmd_Argv(3));
2455                 color[2] = atof(Cmd_Argv(4));
2456         }
2457         else if (!strcmp(Cmd_Argv(1), "radius"))
2458         {
2459                 if (Cmd_Argc() != 3)
2460                 {
2461                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2462                         return;
2463                 }
2464                 radius = atof(Cmd_Argv(2));
2465         }
2466         else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "style"))
2467         {
2468                 if (Cmd_Argc() != 3)
2469                 {
2470                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2471                         return;
2472                 }
2473                 style = atoi(Cmd_Argv(2));
2474         }
2475         else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "cubemap"))
2476         {
2477                 if (Cmd_Argc() > 3)
2478                 {
2479                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2480                         return;
2481                 }
2482                 if (Cmd_Argc() == 3)
2483                         strcpy(cubemapname, Cmd_Argv(2));
2484                 else
2485                         cubemapname[0] = 0;
2486         }
2487         else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "shadows"))
2488         {
2489                 if (Cmd_Argc() != 3)
2490                 {
2491                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2492                         return;
2493                 }
2494                 shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
2495         }
2496         else
2497         {
2498                 Con_Printf("usage: r_editlights_edit [property] [value]\n");
2499                 Con_Printf("Selected light's properties:\n");
2500                 Con_Printf("Origin: %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
2501                 Con_Printf("Radius: %f\n", r_shadow_selectedlight->lightradius);
2502                 Con_Printf("Color: %f %f %f\n", r_shadow_selectedlight->light[0], r_shadow_selectedlight->light[1], r_shadow_selectedlight->light[2]);
2503                 Con_Printf("Style: %i\n", r_shadow_selectedlight->style);
2504                 Con_Printf("Cubemap: %s\n", r_shadow_selectedlight->cubemapname);
2505                 Con_Printf("Shadows: %s\n", r_shadow_selectedlight->castshadows ? "yes" : "no");
2506                 return;
2507         }
2508         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2509         r_shadow_selectedlight = NULL;
2510         R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadows);
2511 }
2512
2513 extern int con_vislines;
2514 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
2515 {
2516         float x, y;
2517         char temp[256];
2518         if (r_shadow_selectedlight == NULL)
2519                 return;
2520         x = 0;
2521         y = con_vislines;
2522         sprintf(temp, "Light properties");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2523         sprintf(temp, "Origin %f %f %f", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2524         sprintf(temp, "Radius %f", r_shadow_selectedlight->lightradius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2525         sprintf(temp, "Color %f %f %f", r_shadow_selectedlight->light[0], r_shadow_selectedlight->light[1], r_shadow_selectedlight->light[2]);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2526         sprintf(temp, "Style %i", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2527         sprintf(temp, "Cubemap %s", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2528         sprintf(temp, "Shadows %s", r_shadow_selectedlight->castshadows ? "yes" : "no");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2529 }
2530
2531 void R_Shadow_EditLights_ToggleShadow_f(void)
2532 {
2533         if (!r_editlights.integer)
2534         {
2535                 Con_Printf("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
2536                 return;
2537         }
2538         if (!r_shadow_selectedlight)
2539         {
2540                 Con_Printf("No selected light.\n");
2541                 return;
2542         }
2543         R_Shadow_NewWorldLight(r_shadow_selectedlight->origin, r_shadow_selectedlight->lightradius, r_shadow_selectedlight->light, r_shadow_selectedlight->style, r_shadow_selectedlight->cubemapname, !r_shadow_selectedlight->castshadows);
2544         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2545         r_shadow_selectedlight = NULL;
2546 }
2547
2548 void R_Shadow_EditLights_Remove_f(void)
2549 {
2550         if (!r_editlights.integer)
2551         {
2552                 Con_Printf("Cannot remove light when not in editing mode.  Set r_editlights to 1.\n");
2553                 return;
2554         }
2555         if (!r_shadow_selectedlight)
2556         {
2557                 Con_Printf("No selected light.\n");
2558                 return;
2559         }
2560         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2561         r_shadow_selectedlight = NULL;
2562 }
2563
2564 void R_Shadow_EditLights_Init(void)
2565 {
2566         Cvar_RegisterVariable(&r_editlights);
2567         Cvar_RegisterVariable(&r_editlights_cursordistance);
2568         Cvar_RegisterVariable(&r_editlights_cursorpushback);
2569         Cvar_RegisterVariable(&r_editlights_cursorpushoff);
2570         Cvar_RegisterVariable(&r_editlights_cursorgrid);
2571         Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
2572         Cvar_RegisterVariable(&r_editlights_rtlightssizescale);
2573         Cvar_RegisterVariable(&r_editlights_rtlightscolorscale);
2574         Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f);
2575         Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f);
2576         Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f);
2577         Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f);
2578         Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f);
2579         Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f);
2580         Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f);
2581         Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f);
2582         Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f);
2583 }