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