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