1 /*
2 Copyright (C) 2001-2002 Charles Hollemeersch
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19 PENTA: the whole file is freakin penta...
20
21 Analog with aliasinstant
22
23 */
24
25 #include "quakedef.h"
26
27
28 #define NUM_BRUSH_LIGHT_INSTANTS 32
29
30 brushlightinstant_t BLightInstantCache[NUM_BRUSH_LIGHT_INSTANTS];
31
32 #define DIST_DELTA 0.1
33 #define ANG_DELTA 0.5
34 #define RADIUS_DELTA 0.1
35
36 int brushCacheRequests, brushFullCacheHits, brushPartialCacheHits;
37
38 /*
39 R_AllocateBrushLightInstant
40 */
41
R_AllocateBrushLightInstant(entity_t * e)42 brushlightinstant_t *R_AllocateBrushLightInstant(entity_t *e) {
43
44 int i, oldest, oindex;
45
46 //try to reclaim an instant that was previously used for this entity and this light
47 for (i=0; i<NUM_BRUSH_LIGHT_INSTANTS; i++) {
48 if ((BLightInstantCache[i].lastent == e) && (BLightInstantCache[i].lastlight == currentshadowlight)) {
49 return &BLightInstantCache[i];
50 }
51 }
52
53 // Con_Printf("legal new\n");
54 //none found, bacause we don't want to destroy the reclaiming of other ents
55 //we use the oldest used instant
56 oldest = r_framecount;
57 oindex = -1;
58 for (i=0; i<NUM_BRUSH_LIGHT_INSTANTS; i++) {
59 if (BLightInstantCache[i].lockframe < oldest) {
60 oldest = BLightInstantCache[i].lockframe;
61 oindex = i;
62 }
63 }
64
65 if (oindex == -1) {
66 //find an instant of an other light
67 for (i=0; i<NUM_BRUSH_LIGHT_INSTANTS; i++) {
68 if (BLightInstantCache[i].lastlight != currentshadowlight)
69 return &BLightInstantCache[i];
70 }
71 }
72
73 return &BLightInstantCache[oindex];
74 }
75
76
R_ClearBrushInstantCaches()77 void R_ClearBrushInstantCaches() {
78
79 int i;
80
81 for (i=0; i<NUM_BRUSH_LIGHT_INSTANTS; i++) {
82 BLightInstantCache[i].lockframe = 0;
83 BLightInstantCache[i].lastent = NULL;
84 }
85 }
86
R_SetupBrushObjectSpace(entity_t * e,brushlightinstant_t * linstant)87 void R_SetupBrushObjectSpace(entity_t *e, brushlightinstant_t *linstant) {
88
89 matrix_4x4 transf;
90 float org[4], res[4];
91
92 //Put light & view origin into object space
93
94 e->angles[0] = -e->angles[0];//stupid quake bug (ID invented it dunno what it doesn messus up angles)
95 R_WorldToObjectMatrix(e, transf);
96 e->angles[0] = -e->angles[0];//stupid quake bug
97
98 org[0] = currentshadowlight->origin[0];
99 org[1] = currentshadowlight->origin[1];
100 org[2] = currentshadowlight->origin[2];
101 org[3] = 1;
102 Mat_Mul_1x4_4x4(org,transf,res);
103 linstant->lightpos[0] = res[0];
104 linstant->lightpos[1] = res[1];
105 linstant->lightpos[2] = res[2];
106
107 org[0] = r_refdef.vieworg[0];
108 org[1] = r_refdef.vieworg[1];
109 org[2] = r_refdef.vieworg[2];
110 org[3] = 1;
111 Mat_Mul_1x4_4x4(org,transf,res);
112 linstant->vieworg[0] = res[0];
113 linstant->vieworg[1] = res[1];
114 linstant->vieworg[2] = res[2];
115 }
116
117 int atest;
118
119 /*
120 I get the creeps of this if I use the dist of aliasinstant it totaly fucks up
121 the whole instant thing (not only brushes)
122 */
bdist(vec3_t v1,vec3_t v2)123 float bdist(vec3_t v1, vec3_t v2) {
124 vec3_t diff;
125 VectorSubtract(v1,v2,diff);
126 return Length(diff);
127 }
128
CheckBrushLightUpdate(entity_t * e,brushlightinstant_t * ins)129 qboolean CheckBrushLightUpdate(entity_t *e, brushlightinstant_t *ins) {
130
131 /*
132 if (ins->lastlight != currentshadowlight) Con_Printf("light\n");
133 if (dist(ins->lastlorg,currentshadowlight->origin) >= DIST_DELTA) Con_Printf("lightorig\n");
134 if (dist(ins->lasteorg,e->origin) >= DIST_DELTA) Con_Printf("entorig\n");
135 if (dist(ins->lasteangles,e->angles) >= ANG_DELTA) Con_Printf("entangle\n");
136 if (abs(ins->lastlradius - currentshadowlight->radius) > RADIUS_DELTA) Con_Printf("lightrad\n");
137 if (ins->lastshadowonly != ins->shadowonly) Con_Printf("shadowonly\n");
138 */
139
140 if (sh_nocache.value) return true;
141
142 // return true;
143
144 if ((ins->lastent == e) &&
145 (ins->lastlight == currentshadowlight) &&
146 (bdist(ins->lastlorg,currentshadowlight->origin) < DIST_DELTA) &&
147 (bdist(ins->lasteorg,e->origin) < DIST_DELTA) &&
148 (bdist(ins->lasteangles,e->angles) < ANG_DELTA) &&
149 (fabs(ins->lastlradius - currentshadowlight->radius) <= RADIUS_DELTA) &&
150 (ins->lastshadowonly == ins->shadowonly) &&
151 (ins->lockframe >= r_framecount-10))//XYW Don't reuse if it has been unused for a long time
152
153 {
154 atest = atest+1;
155 return false;
156 } else {
157 atest = atest-1;
158 return true;
159 }
160 }
161
CheckBrushHalfAngle(brushlightinstant_t * ins)162 qboolean CheckBrushHalfAngle(brushlightinstant_t *ins)
163 {
164 if (bdist(ins->lastvorg,r_refdef.vieworg) < 0.5)
165 {
166 atest = atest+1;
167 return false;
168 } else {
169 atest = atest-1;
170 return true;
171 }
172 }
173
174 /*
175 NOTE this is not the same as R_MarkShadowSurf
176 */
R_IsVisibleSurf(msurface_t * surf,brushlightinstant_t * ins)177 qboolean R_IsVisibleSurf(msurface_t *surf, brushlightinstant_t *ins)
178 {
179 mplane_t *plane;
180 float dist;
181 glpoly_t *poly;
182
183 //we don't cast shadows with water
184 if (( surf->flags & SURF_DRAWTURB ) || ( surf->flags & SURF_DRAWSKY )) {
185 return false;
186 }
187
188 plane = surf->plane;
189
190 poly = surf->polys;
191
192 if (poly->lightTimestamp == r_lightTimestamp) {
193 //this happens with non unique brushmodels like ammoboxes that are already stamped from
194 //an previous entity with the same polygons
195 poly->lightTimestamp = 0;
196 }
197
198 switch (plane->type)
199 {
200 case PLANE_X:
201 dist = ins->lightpos[0] - plane->dist;
202 break;
203 case PLANE_Y:
204 dist = ins->lightpos[1] - plane->dist;
205 break;
206 case PLANE_Z:
207 dist = ins->lightpos[2] - plane->dist;
208 break;
209 default:
210 dist = DotProduct (ins->lightpos, plane->normal) - plane->dist;
211 break;
212 }
213
214 //the normals are flipped when surf_planeback is 1
215 if (((surf->flags & SURF_PLANEBACK) && (dist > 0)) ||
216 (!(surf->flags & SURF_PLANEBACK) && (dist < 0)))
217 {
218 return false;
219 }
220
221 //the normals are flipped when surf_planeback is 1
222 if ( abs(dist) > currentshadowlight->radius)
223 {
224 return false;
225 }
226
227 poly->lightTimestamp = r_lightTimestamp;
228
229 return true;
230 }
231
R_CalcBrushVolumeVerts(entity_t * e,brushlightinstant_t * ins)232 void R_CalcBrushVolumeVerts(entity_t *e, brushlightinstant_t *ins) {
233
234 model_t *model = e->model;
235 msurface_t *surf;
236 glpoly_t *poly;
237 int extrvertcount, visneighcount;
238 int i, j;
239 vec3_t v1;
240 vec3_t *v2;
241 float scale;
242 qboolean shadow;
243
244 //mark visibility of polygons
245 surf = &model->surfaces[model->firstmodelsurface];
246 for (i=0; i<model->nummodelsurfaces; i++, surf++)
247 {
248 ins->polygonVis[i] = R_IsVisibleSurf(surf, ins);
249 }
250
251 if (!currentshadowlight->castShadow) return;
252
253 extrvertcount = 0;
254 visneighcount = 0;
255
256 surf = &model->surfaces[model->firstmodelsurface];
257 for (i=0; i<model->nummodelsurfaces; i++, surf++)
258 {
259 if (!ins->polygonVis[i]) continue;
260
261 poly = surf->polys;
262 //extrude vertices and copy to buffer
263 for (j=0 ; j<surf->numedges ; j++)
264 {
265 //v2 = (vec3_t *)&poly->verts[j];
266 v2 = (vec3_t *)(&globalVertexTable[poly->firstvertex+j]);
267 VectorSubtract ( (*v2) ,ins->lightpos, v1);
268 scale = Length (v1);
269
270 if (sh_visiblevolumes.value)
271 VectorScale (v1, (1/scale)*50, v1);
272 else
273 VectorScale (v1, (1/scale)*currentshadowlight->radius*2, v1);
274
275 VectorAdd (v1, (*v2), ins->extvertices[extrvertcount+j]);
276 }
277
278 //save visibility info of neighbours
279 for (j=0 ; j<surf->numedges ; j++)
280 {
281 shadow = false;
282 if (poly->neighbours[j] != NULL) {
283 if ( poly->neighbours[j]->lightTimestamp != poly->lightTimestamp) {
284 shadow = true;
285 }
286 } else {
287 shadow = true;
288 }
289
290 ins->neighbourVis[extrvertcount+j] = shadow;
291 }
292 extrvertcount+=surf->numedges;
293 }
294 }
295
R_CalcBrushAttenCoords(entity_t * e,brushlightinstant_t * ins)296 void R_CalcBrushAttenCoords(entity_t *e, brushlightinstant_t *ins) {
297 msurface_t *psurf;
298 mplane_t *splitplane;
299 int i, j;
300 vec3_t s, t, nearToVert, nearPt;
301 glpoly_t *poly;
302 int usedcolorscales, usedattencoords;
303 float dist, scale, colorscale;
304 float *v;
305
306 psurf = &e->model->surfaces[e->model->firstmodelsurface];
307
308 usedcolorscales = 0;
309 usedattencoords = 0;
310 for (i=0 ; i<e->model->nummodelsurfaces ; i++, psurf++)
311 {
312 if (!ins->polygonVis[i])
313 continue;
314
315 poly = psurf->polys;
316
317 VectorCopy(psurf->texinfo->vecs[0],s);
318 VectorCopy(psurf->texinfo->vecs[1],t);
319
320 splitplane = psurf->plane;
321
322 switch (splitplane->type)
323 {
324 case PLANE_X:
325 dist = ins->lightpos[0] - splitplane->dist;
326 break;
327 case PLANE_Y:
328 dist = ins->lightpos[1] - splitplane->dist;
329 break;
330 case PLANE_Z:
331 dist = ins->lightpos[2] - splitplane->dist;
332 break;
333 default:
334 dist = DotProduct (ins->lightpos, splitplane->normal) - splitplane->dist;
335 break;
336 }
337
338 dist = abs(dist);
339
340 ProjectPlane(ins->lightpos,s,t,nearPt);
341
342 scale = 1 /((2 * currentshadowlight->radius) - dist);
343 colorscale = (1 - (dist / currentshadowlight->radius));
344
345 if (colorscale <0) colorscale = 0;
346
347 ins->colorscales[usedcolorscales] = colorscale;
348 usedcolorscales++;
349
350 //we could probably do this in hardware, with a vertex program!
351 v = (float *)(&globalVertexTable[poly->firstvertex]);
352 //v = poly->verts[0];
353 for (j=0 ; j<poly->numverts ; j++, v+= VERTEXSIZE)
354 {
355 // Project the light image onto the face
356 VectorSubtract(v,nearPt,nearToVert);
357
358 // Get our texture coordinates
359 ins->atencoords[usedattencoords][0] = DotProduct(nearToVert,s) * scale + 0.5;
360 ins->atencoords[usedattencoords][1] = DotProduct(nearToVert,t) * scale + 0.5;
361 usedattencoords++;
362 }
363 }
364 }
365
R_SetupBrushLightHAV(entity_t * ent,brushlightinstant_t * ins)366 void R_SetupBrushLightHAV(entity_t *ent, brushlightinstant_t *ins)
367 {
368 vec3_t lightDir,H, lightOr;
369 glpoly_t *poly;
370 int i,j,usedts;
371 float *v;
372 msurface_t *psurf;
373
374
375 VectorCopy(ins->lightpos,lightOr);
376 psurf = &ent->model->surfaces[ent->model->firstmodelsurface];
377 usedts = 0;
378 for (i=0 ; i<ent->model->nummodelsurfaces ; i++, psurf++)
379 {
380 if (!ins->polygonVis[i])
381 continue;
382
383 poly = psurf->polys;
384
385 //v = poly->verts[0];
386 v = (float *)(&globalVertexTable[poly->firstvertex]);
387 for (j=0 ; j<poly->numverts ; j++, v+= VERTEXSIZE)
388 {
389
390 VectorSubtract(lightOr,v,lightDir);
391
392 //store tangent space light vector
393 if (psurf->flags & SURF_PLANEBACK) {
394 ins->tslights[usedts][2] = -DotProduct(lightDir,psurf->plane->normal);
395 } else {
396 ins->tslights[usedts][2] = DotProduct(lightDir,psurf->plane->normal);
397 }
398 ins->tslights[usedts][1] = -DotProduct(lightDir,psurf->texinfo->vecs[1]);
399 ins->tslights[usedts][0] = DotProduct(lightDir,psurf->texinfo->vecs[0]);
400
401
402 VectorNormalize(lightDir);
403 //object_vieworg = camera position in object space
404 VectorSubtract(ins->vieworg,v,H);
405 VectorNormalize(H);
406 VectorAdd(lightDir,H,H);
407
408 //store tangent space half angle
409 if (psurf->flags & SURF_PLANEBACK) {
410 ins->tshalfangles[usedts][2] = -DotProduct(H,psurf->plane->normal);
411 } else {
412 ins->tshalfangles[usedts][2] = DotProduct(H,psurf->plane->normal);
413 }
414
415 ins->tshalfangles[usedts][1] = -DotProduct(H,psurf->texinfo->vecs[1]);
416 ins->tshalfangles[usedts][0] = DotProduct(H,psurf->texinfo->vecs[0]);
417
418 usedts++;
419 }
420 }
421 }
422
R_SetupBrushInstantForLight(entity_t * e)423 void R_SetupBrushInstantForLight(entity_t *e)
424 {
425 brushlightinstant_t *brushlightinstant;
426 vec3_t mins, maxs;
427 qboolean update;
428
429 brushlightinstant = R_AllocateBrushLightInstant(e);
430
431 VectorAdd (e->origin,e->model->mins, mins);
432 VectorAdd (e->origin,e->model->maxs, maxs);
433
434 if (R_CullBox (mins, maxs))
435 brushlightinstant->shadowonly = true;
436 else
437 brushlightinstant->shadowonly = false;
438
439 e->brushlightinstant = brushlightinstant;
440
441 R_SetupBrushObjectSpace(e, brushlightinstant);
442
443 update = CheckBrushLightUpdate(e, brushlightinstant);
444 if (update)
445 {
446 R_CalcBrushVolumeVerts(e, brushlightinstant);
447
448 if (!brushlightinstant->shadowonly) {
449 if ( gl_cardtype == GENERIC || gl_cardtype == GEFORCE ) {//PA:
450 R_CalcBrushAttenCoords(e, brushlightinstant);
451 }
452 //R_SetupBrushLightHAV(e, brushlightinstant);
453 }
454
455 //make sure that we can compare the next frame
456 VectorCopy(e->origin, brushlightinstant->lasteorg);
457 VectorCopy(currentshadowlight->origin, brushlightinstant->lastlorg);
458 VectorCopy(e->angles, brushlightinstant->lasteangles);
459 brushlightinstant->lastlradius = currentshadowlight->radius;
460 brushlightinstant->lastlight = currentshadowlight;
461 brushlightinstant->lastent = e;
462 brushlightinstant->lastshadowonly = brushlightinstant->shadowonly;
463 }
464
465 brushCacheRequests++;
466 //Half angles only change when the viewer changes his position
467 //this happens a lot so recalculate only this.
468 if ((update) || CheckBrushHalfAngle(brushlightinstant)) {
469 if (!brushlightinstant->shadowonly)
470 R_SetupBrushLightHAV(e, brushlightinstant);
471 VectorCopy(r_refdef.vieworg,brushlightinstant->lastvorg);
472
473 if(!update) brushPartialCacheHits++;
474 } else {
475 brushFullCacheHits++;
476 }
477
478 //lock it for this frame
479 brushlightinstant->lockframe = r_framecount;
480 }
481