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