1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4 
5 This file is part of Quake III Arena source code.
6 
7 Quake III Arena source code is free software; you can redistribute it
8 and/or modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the License,
10 or (at your option) any later version.
11 
12 Quake III Arena source code is distributed in the hope that it will be
13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with Quake III Arena source code; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 ===========================================================================
21 */
22 // tr_light.c
23 
24 #include "tr_local.h"
25 
26 #define	DLIGHT_AT_RADIUS		16
27 // at the edge of a dlight's influence, this amount of light will be added
28 
29 #define	DLIGHT_MINIMUM_RADIUS	16
30 // never calculate a range less than this to prevent huge light numbers
31 
32 
33 /*
34 ===============
35 R_TransformDlights
36 
37 Transforms the origins of an array of dlights.
38 Used by both the front end (for DlightBmodel) and
39 the back end (before doing the lighting calculation)
40 ===============
41 */
R_TransformDlights(int count,dlight_t * dl,orientationr_t * or)42 void R_TransformDlights( int count, dlight_t *dl, orientationr_t *or) {
43 	int		i;
44 	vec3_t	temp;
45 
46 	for ( i = 0 ; i < count ; i++, dl++ ) {
47 		VectorSubtract( dl->origin, or->origin, temp );
48 		dl->transformed[0] = DotProduct( temp, or->axis[0] );
49 		dl->transformed[1] = DotProduct( temp, or->axis[1] );
50 		dl->transformed[2] = DotProduct( temp, or->axis[2] );
51 	}
52 }
53 
54 /*
55 =============
56 R_DlightBmodel
57 
58 Determine which dynamic lights may effect this bmodel
59 =============
60 */
R_DlightBmodel(bmodel_t * bmodel)61 void R_DlightBmodel( bmodel_t *bmodel ) {
62 	int			i, j;
63 	dlight_t	*dl;
64 	int			mask;
65 	msurface_t	*surf;
66 
67 	// transform all the lights
68 	R_TransformDlights( tr.refdef.num_dlights, tr.refdef.dlights, &tr.or );
69 
70 	mask = 0;
71 	for ( i=0 ; i<tr.refdef.num_dlights ; i++ ) {
72 		dl = &tr.refdef.dlights[i];
73 
74 		// see if the point is close enough to the bounds to matter
75 		for ( j = 0 ; j < 3 ; j++ ) {
76 			if ( dl->transformed[j] - bmodel->bounds[1][j] > dl->radius ) {
77 				break;
78 			}
79 			if ( bmodel->bounds[0][j] - dl->transformed[j] > dl->radius ) {
80 				break;
81 			}
82 		}
83 		if ( j < 3 ) {
84 			continue;
85 		}
86 
87 		// we need to check this light
88 		mask |= 1 << i;
89 	}
90 
91 	tr.currentEntity->needDlights = (mask != 0);
92 
93 	// set the dlight bits in all the surfaces
94 	for ( i = 0 ; i < bmodel->numSurfaces ; i++ ) {
95 		surf = bmodel->firstSurface + i;
96 
97 		if ( *surf->data == SF_FACE ) {
98 			((srfSurfaceFace_t *)surf->data)->dlightBits[ tr.smpFrame ] = mask;
99 		} else if ( *surf->data == SF_GRID ) {
100 			((srfGridMesh_t *)surf->data)->dlightBits[ tr.smpFrame ] = mask;
101 		} else if ( *surf->data == SF_TRIANGLES ) {
102 			((srfTriangles_t *)surf->data)->dlightBits[ tr.smpFrame ] = mask;
103 		}
104 	}
105 }
106 
107 
108 /*
109 =============================================================================
110 
111 LIGHT SAMPLING
112 
113 =============================================================================
114 */
115 
116 extern	cvar_t	*r_ambientScale;
117 extern	cvar_t	*r_directedScale;
118 extern	cvar_t	*r_debugLight;
119 
120 /*
121 =================
122 R_SetupEntityLightingGrid
123 
124 =================
125 */
R_SetupEntityLightingGrid(trRefEntity_t * ent)126 static void R_SetupEntityLightingGrid( trRefEntity_t *ent ) {
127 	vec3_t	lightOrigin;
128 	int		pos[3];
129 	int		i, j;
130 	byte	*gridData;
131 	float	frac[3];
132 	int		gridStep[3];
133 	vec3_t	direction;
134 	float	totalFactor;
135 
136 	if ( ent->e.renderfx & RF_LIGHTING_ORIGIN ) {
137 		// seperate lightOrigins are needed so an object that is
138 		// sinking into the ground can still be lit, and so
139 		// multi-part models can be lit identically
140 		VectorCopy( ent->e.lightingOrigin, lightOrigin );
141 	} else {
142 		VectorCopy( ent->e.origin, lightOrigin );
143 	}
144 
145 	VectorSubtract( lightOrigin, tr.world->lightGridOrigin, lightOrigin );
146 	for ( i = 0 ; i < 3 ; i++ ) {
147 		float	v;
148 
149 		v = lightOrigin[i]*tr.world->lightGridInverseSize[i];
150 		pos[i] = floor( v );
151 		frac[i] = v - pos[i];
152 		if ( pos[i] < 0 ) {
153 			pos[i] = 0;
154 		} else if ( pos[i] >= tr.world->lightGridBounds[i] - 1 ) {
155 			pos[i] = tr.world->lightGridBounds[i] - 1;
156 		}
157 	}
158 
159 	VectorClear( ent->ambientLight );
160 	VectorClear( ent->directedLight );
161 	VectorClear( direction );
162 
163 	assert( tr.world->lightGridData ); // NULL with -nolight maps
164 
165 	// trilerp the light value
166 	gridStep[0] = 8;
167 	gridStep[1] = 8 * tr.world->lightGridBounds[0];
168 	gridStep[2] = 8 * tr.world->lightGridBounds[0] * tr.world->lightGridBounds[1];
169 	gridData = tr.world->lightGridData + pos[0] * gridStep[0]
170 		+ pos[1] * gridStep[1] + pos[2] * gridStep[2];
171 
172 	totalFactor = 0;
173 	for ( i = 0 ; i < 8 ; i++ ) {
174 		float	factor;
175 		byte	*data;
176 		int		lat, lng;
177 		vec3_t	normal;
178 		#if idppc
179 		float d0, d1, d2, d3, d4, d5;
180 		#endif
181 		factor = 1.0;
182 		data = gridData;
183 		for ( j = 0 ; j < 3 ; j++ ) {
184 			if ( i & (1<<j) ) {
185 				factor *= frac[j];
186 				data += gridStep[j];
187 			} else {
188 				factor *= (1.0f - frac[j]);
189 			}
190 		}
191 
192 		if ( !(data[0]+data[1]+data[2]) ) {
193 			continue;	// ignore samples in walls
194 		}
195 		totalFactor += factor;
196 		#if idppc
197 		d0 = data[0]; d1 = data[1]; d2 = data[2];
198 		d3 = data[3]; d4 = data[4]; d5 = data[5];
199 
200 		ent->ambientLight[0] += factor * d0;
201 		ent->ambientLight[1] += factor * d1;
202 		ent->ambientLight[2] += factor * d2;
203 
204 		ent->directedLight[0] += factor * d3;
205 		ent->directedLight[1] += factor * d4;
206 		ent->directedLight[2] += factor * d5;
207 		#else
208 		ent->ambientLight[0] += factor * data[0];
209 		ent->ambientLight[1] += factor * data[1];
210 		ent->ambientLight[2] += factor * data[2];
211 
212 		ent->directedLight[0] += factor * data[3];
213 		ent->directedLight[1] += factor * data[4];
214 		ent->directedLight[2] += factor * data[5];
215 		#endif
216 		lat = data[7];
217 		lng = data[6];
218 		lat *= (FUNCTABLE_SIZE/256);
219 		lng *= (FUNCTABLE_SIZE/256);
220 
221 		// decode X as cos( lat ) * sin( long )
222 		// decode Y as sin( lat ) * sin( long )
223 		// decode Z as cos( long )
224 
225 		normal[0] = tr.sinTable[(lat+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK] * tr.sinTable[lng];
226 		normal[1] = tr.sinTable[lat] * tr.sinTable[lng];
227 		normal[2] = tr.sinTable[(lng+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK];
228 
229 		VectorMA( direction, factor, normal, direction );
230 	}
231 
232 	if ( totalFactor > 0 && totalFactor < 0.99 ) {
233 		totalFactor = 1.0f / totalFactor;
234 		VectorScale( ent->ambientLight, totalFactor, ent->ambientLight );
235 		VectorScale( ent->directedLight, totalFactor, ent->directedLight );
236 	}
237 
238 	VectorScale( ent->ambientLight, r_ambientScale->value, ent->ambientLight );
239 	VectorScale( ent->directedLight, r_directedScale->value, ent->directedLight );
240 
241 	VectorNormalize2( direction, ent->lightDir );
242 }
243 
244 
245 /*
246 ===============
247 LogLight
248 ===============
249 */
LogLight(trRefEntity_t * ent)250 static void LogLight( trRefEntity_t *ent ) {
251 	int	max1, max2;
252 
253 	if ( !(ent->e.renderfx & RF_FIRST_PERSON ) ) {
254 		return;
255 	}
256 
257 	max1 = ent->ambientLight[0];
258 	if ( ent->ambientLight[1] > max1 ) {
259 		max1 = ent->ambientLight[1];
260 	} else if ( ent->ambientLight[2] > max1 ) {
261 		max1 = ent->ambientLight[2];
262 	}
263 
264 	max2 = ent->directedLight[0];
265 	if ( ent->directedLight[1] > max2 ) {
266 		max2 = ent->directedLight[1];
267 	} else if ( ent->directedLight[2] > max2 ) {
268 		max2 = ent->directedLight[2];
269 	}
270 
271 	ri.Printf( PRINT_ALL, "amb:%i  dir:%i\n", max1, max2 );
272 }
273 
274 /*
275 =================
276 R_SetupEntityLighting
277 
278 Calculates all the lighting values that will be used
279 by the Calc_* functions
280 =================
281 */
R_SetupEntityLighting(const trRefdef_t * refdef,trRefEntity_t * ent)282 void R_SetupEntityLighting( const trRefdef_t *refdef, trRefEntity_t *ent ) {
283 	int				i;
284 	dlight_t		*dl;
285 	float			power;
286 	vec3_t			dir;
287 	float			d;
288 	vec3_t			lightDir;
289 	vec3_t			lightOrigin;
290 
291 	// lighting calculations
292 	if ( ent->lightingCalculated ) {
293 		return;
294 	}
295 	ent->lightingCalculated = qtrue;
296 
297 	//
298 	// trace a sample point down to find ambient light
299 	//
300 	if ( ent->e.renderfx & RF_LIGHTING_ORIGIN ) {
301 		// seperate lightOrigins are needed so an object that is
302 		// sinking into the ground can still be lit, and so
303 		// multi-part models can be lit identically
304 		VectorCopy( ent->e.lightingOrigin, lightOrigin );
305 	} else {
306 		VectorCopy( ent->e.origin, lightOrigin );
307 	}
308 
309 	// if NOWORLDMODEL, only use dynamic lights (menu system, etc)
310 	if ( !(refdef->rdflags & RDF_NOWORLDMODEL )
311 		&& tr.world->lightGridData ) {
312 		R_SetupEntityLightingGrid( ent );
313 	} else {
314 		ent->ambientLight[0] = ent->ambientLight[1] =
315 			ent->ambientLight[2] = tr.identityLight * 150;
316 		ent->directedLight[0] = ent->directedLight[1] =
317 			ent->directedLight[2] = tr.identityLight * 150;
318 		VectorCopy( tr.sunDirection, ent->lightDir );
319 	}
320 
321 	// bonus items and view weapons have a fixed minimum add
322 	if ( 1 /* ent->e.renderfx & RF_MINLIGHT */ ) {
323 		// give everything a minimum light add
324 		ent->ambientLight[0] += tr.identityLight * 32;
325 		ent->ambientLight[1] += tr.identityLight * 32;
326 		ent->ambientLight[2] += tr.identityLight * 32;
327 	}
328 
329 	//
330 	// modify the light by dynamic lights
331 	//
332 	d = VectorLength( ent->directedLight );
333 	VectorScale( ent->lightDir, d, lightDir );
334 
335 	for ( i = 0 ; i < refdef->num_dlights ; i++ ) {
336 		dl = &refdef->dlights[i];
337 		VectorSubtract( dl->origin, lightOrigin, dir );
338 		d = VectorNormalize( dir );
339 
340 		power = DLIGHT_AT_RADIUS * ( dl->radius * dl->radius );
341 		if ( d < DLIGHT_MINIMUM_RADIUS ) {
342 			d = DLIGHT_MINIMUM_RADIUS;
343 		}
344 		d = power / ( d * d );
345 
346 		VectorMA( ent->directedLight, d, dl->color, ent->directedLight );
347 		VectorMA( lightDir, d, dir, lightDir );
348 	}
349 
350 	// clamp ambient
351 	for ( i = 0 ; i < 3 ; i++ ) {
352 		if ( ent->ambientLight[i] > tr.identityLightByte ) {
353 			ent->ambientLight[i] = tr.identityLightByte;
354 		}
355 	}
356 
357 	if ( r_debugLight->integer ) {
358 		LogLight( ent );
359 	}
360 
361 	// save out the byte packet version
362 	((byte *)&ent->ambientLightInt)[0] = myftol( ent->ambientLight[0] );
363 	((byte *)&ent->ambientLightInt)[1] = myftol( ent->ambientLight[1] );
364 	((byte *)&ent->ambientLightInt)[2] = myftol( ent->ambientLight[2] );
365 	((byte *)&ent->ambientLightInt)[3] = 0xff;
366 
367 	// transform the direction to local space
368 	VectorNormalize( lightDir );
369 	ent->lightDir[0] = DotProduct( lightDir, ent->e.axis[0] );
370 	ent->lightDir[1] = DotProduct( lightDir, ent->e.axis[1] );
371 	ent->lightDir[2] = DotProduct( lightDir, ent->e.axis[2] );
372 }
373 
374 /*
375 =================
376 R_LightForPoint
377 =================
378 */
R_LightForPoint(vec3_t point,vec3_t ambientLight,vec3_t directedLight,vec3_t lightDir)379 int R_LightForPoint( vec3_t point, vec3_t ambientLight, vec3_t directedLight, vec3_t lightDir )
380 {
381 	trRefEntity_t ent;
382 
383 	if ( tr.world->lightGridData == NULL )
384 	  return qfalse;
385 
386 	Com_Memset(&ent, 0, sizeof(ent));
387 	VectorCopy( point, ent.e.origin );
388 	R_SetupEntityLightingGrid( &ent );
389 	VectorCopy(ent.ambientLight, ambientLight);
390 	VectorCopy(ent.directedLight, directedLight);
391 	VectorCopy(ent.lightDir, lightDir);
392 
393 	return qtrue;
394 }
395