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