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