1 //********************************************************************************************
2 //*
3 //*    This file is part of Egoboo.
4 //*
5 //*    Egoboo is free software: you can redistribute it and/or modify it
6 //*    under the terms of the GNU General Public License as published by
7 //*    the Free Software Foundation, either version 3 of the License, or
8 //*    (at your option) any later version.
9 //*
10 //*    Egoboo is distributed in the hope that it will be useful, but
11 //*    WITHOUT ANY WARRANTY; without even the implied warranty of
12 //*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 //*    General Public License for more details.
14 //*
15 //*    You should have received a copy of the GNU General Public License
16 //*    along with Egoboo.  If not, see <http://www.gnu.org/licenses/>.
17 //*
18 //********************************************************************************************
19 
20 /// @file lighting.c
21 /// @brief Code for controlling the character and mesh lighting
22 /// @details
23 
24 #include "lighting.h"
25 
26 #include "egoboo_math.inl"
27 
28 //--------------------------------------------------------------------------------------------
29 //--------------------------------------------------------------------------------------------
30 float            light_a = 0.0f, light_d = 0.0f;
31 float            light_nrm[3] = {0.0f, 0.0f, 0.0f};
32 
33 //--------------------------------------------------------------------------------------------
34 //--------------------------------------------------------------------------------------------
35 static bool_t lighting_sum_project( lighting_cache_t * dst, lighting_cache_t * src, fvec3_t vec, int dir );
36 
37 static float  lighting_evaluate_cache_base( lighting_cache_base_t * lvec, fvec3_base_t nrm, float * amb );
38 
39 //--------------------------------------------------------------------------------------------
40 //--------------------------------------------------------------------------------------------
lighting_vector_evaluate(lighting_vector_t lvec,fvec3_base_t nrm,float * dir,float * amb)41 void lighting_vector_evaluate( lighting_vector_t lvec, fvec3_base_t nrm, float * dir, float * amb )
42 {
43     float loc_dir, loc_amb;
44 
45     if ( NULL == dir ) dir = &loc_dir;
46     if ( NULL == amb ) amb = &loc_amb;
47 
48     *dir = 0.0f;
49     *amb = 0.0f;
50 
51     if ( NULL == lvec ) return;
52 
53     if ( nrm[kX] > 0.0f )
54     {
55         *dir += nrm[kX] * lvec[LVEC_PX];
56     }
57     else if ( nrm[kX] < 0.0f )
58     {
59         *dir -= nrm[kX] * lvec[LVEC_MX];
60     }
61 
62     if ( nrm[kY] > 0.0f )
63     {
64         *dir += nrm[kY] * lvec[LVEC_PY];
65     }
66     else if ( nrm[kY] < 0.0f )
67     {
68         *dir -= nrm[kY] * lvec[LVEC_MY];
69     }
70 
71     if ( nrm[kZ] > 0.0f )
72     {
73         *dir += nrm[kZ] * lvec[LVEC_PZ];
74     }
75     else if ( nrm[kZ] < 0.0f )
76     {
77         *dir -= nrm[kZ] * lvec[LVEC_MZ];
78     }
79 
80     // the ambient is not summed
81     *amb += lvec[LVEC_AMB];
82 }
83 
84 //--------------------------------------------------------------------------------------------
lighting_vector_sum(lighting_vector_t lvec,fvec3_base_t nrm,float direct,float ambient)85 void lighting_vector_sum( lighting_vector_t lvec, fvec3_base_t nrm, float direct, float ambient )
86 {
87     if ( nrm[kX] > 0.0f )
88     {
89         lvec[LVEC_PX] += nrm[kX] * direct;
90     }
91     else if ( nrm[kX] < 0.0f )
92     {
93         lvec[LVEC_MX] -= nrm[kX] * direct;
94     }
95 
96     if ( nrm[kY] > 0.0f )
97     {
98         lvec[LVEC_PY] += nrm[kY] * direct;
99     }
100     else if ( nrm[kY] < 0.0f )
101     {
102         lvec[LVEC_MY] -= nrm[kY] * direct;
103     }
104 
105     if ( nrm[kZ] > 0.0f )
106     {
107         lvec[LVEC_PZ] += nrm[kZ] * direct;
108     }
109     else if ( nrm[kZ] < 0.0f )
110     {
111         lvec[LVEC_MZ] -= nrm[kZ] * direct;
112     }
113 
114     // the ambient is not summed
115     lvec[LVEC_AMB] += ambient;
116 }
117 
118 //--------------------------------------------------------------------------------------------
119 //--------------------------------------------------------------------------------------------
lighting_cache_base_init(lighting_cache_base_t * cache)120 lighting_cache_base_t * lighting_cache_base_init( lighting_cache_base_t * cache )
121 {
122     if ( NULL == cache ) return NULL;
123 
124     memset( cache, 0, sizeof( *cache ) );
125 
126     return cache;
127 }
128 
129 //--------------------------------------------------------------------------------------------
lighting_cache_base_max_light(lighting_cache_base_t * cache)130 bool_t lighting_cache_base_max_light( lighting_cache_base_t * cache )
131 {
132     int cnt;
133     float max_light;
134 
135     // determine the lighting extents
136     max_light = ABS( cache->lighting[0] );
137     for ( cnt = 1; cnt < LIGHTING_VEC_SIZE - 1; cnt++ )
138     {
139         max_light = MAX( max_light, ABS( cache->lighting[cnt] ) );
140     }
141 
142     cache->max_light = max_light;
143 
144     return btrue;
145 }
146 
147 //--------------------------------------------------------------------------------------------
lighting_cache_base_blend(lighting_cache_base_t * cold,lighting_cache_base_t * cnew,float keep)148 bool_t lighting_cache_base_blend( lighting_cache_base_t * cold, lighting_cache_base_t * cnew, float keep )
149 {
150     int tnc;
151     float max_delta;
152 
153     if ( NULL == cold || NULL == cnew ) return bfalse;
154 
155     // blend this in with the existing lighting
156     if ( 1.0f == keep )
157     {
158         // no change from last time
159         max_delta = 0.0f;
160     }
161     else if ( 0.0f == keep )
162     {
163         max_delta = 0.0f;
164         for ( tnc = 0; tnc < LIGHTING_VEC_SIZE; tnc++ )
165         {
166             float delta = ABS( cnew->lighting[tnc] - cold->lighting[tnc] );
167 
168             cold->lighting[tnc] = cnew->lighting[tnc];
169 
170             max_delta = MAX( max_delta, delta );
171         }
172     }
173     else
174     {
175         max_delta = 0.0f;
176         for ( tnc = 0; tnc < LIGHTING_VEC_SIZE; tnc++ )
177         {
178             float ftmp;
179 
180             ftmp = cold->lighting[tnc];
181             cold->lighting[tnc] = ftmp * keep + cnew->lighting[tnc] * ( 1.0f - keep );
182             max_delta = MAX( max_delta, ABS( cold->lighting[tnc] - ftmp ) );
183         }
184     }
185 
186     cold->max_delta = max_delta;
187 
188     return btrue;
189 }
190 
191 //--------------------------------------------------------------------------------------------
192 //--------------------------------------------------------------------------------------------
lighting_cache_init(lighting_cache_t * cache)193 lighting_cache_t * lighting_cache_init( lighting_cache_t * cache )
194 {
195     if ( NULL == cache ) return cache;
196 
197     lighting_cache_base_init( &( cache->low ) );
198     lighting_cache_base_init( &( cache->hgh ) );
199 
200     cache->max_delta = 0.0f;
201     cache->max_light = 0.0f;
202 
203     return cache;
204 }
205 
206 //--------------------------------------------------------------------------------------------
lighting_cache_max_light(lighting_cache_t * cache)207 bool_t lighting_cache_max_light( lighting_cache_t * cache )
208 {
209     if ( NULL == cache ) return bfalse;
210 
211     // determine the lighting extents
212     lighting_cache_base_max_light( &( cache->low ) );
213     lighting_cache_base_max_light( &( cache->hgh ) );
214 
215     // set the maximum direct light
216     cache->max_light = MAX( cache->low.max_light, cache->hgh.max_light );
217 
218     return btrue;
219 }
220 
221 //--------------------------------------------------------------------------------------------
lighting_cache_blend(lighting_cache_t * cache,lighting_cache_t * cnew,float keep)222 bool_t lighting_cache_blend( lighting_cache_t * cache, lighting_cache_t * cnew, float keep )
223 {
224     if ( NULL == cache || NULL == cnew ) return bfalse;
225 
226     // find deltas
227     lighting_cache_base_blend( &( cache->low ), ( &cnew->low ), keep );
228     lighting_cache_base_blend( &( cache->hgh ), ( &cnew->hgh ), keep );
229 
230     // find the absolute maximum delta
231     cache->max_delta = MAX( cache->low.max_delta, cache->hgh.max_delta );
232 
233     return btrue;
234 }
235 
236 //--------------------------------------------------------------------------------------------
237 //--------------------------------------------------------------------------------------------
lighting_project_cache(lighting_cache_t * dst,lighting_cache_t * src,fmat_4x4_t mat)238 bool_t lighting_project_cache( lighting_cache_t * dst, lighting_cache_t * src, fmat_4x4_t mat )
239 {
240     fvec3_t   fwd, right, up;
241 
242     if ( NULL == src ) return bfalse;
243 
244     // blank the destination lighting
245     if ( NULL == lighting_cache_init( dst ) ) return bfalse;
246 
247     // do the ambient
248     dst->low.lighting[LVEC_AMB] = src->low.lighting[LVEC_AMB];
249     dst->hgh.lighting[LVEC_AMB] = src->hgh.lighting[LVEC_AMB];
250 
251     if ( src->max_light == 0.0f ) return btrue;
252 
253     // grab the character directions
254     mat_getChrForward( mat.v, fwd.v );          // along body-fixed +y-axis
255     mat_getChrRight( mat.v, right.v );          // along body-fixed +x-axis
256     mat_getChrUp( mat.v, up.v );                    // along body-fixed +z axis
257 
258     fvec3_self_normalize( fwd.v );
259     fvec3_self_normalize( right.v );
260     fvec3_self_normalize( up.v );
261 
262     // split the lighting cache up
263     lighting_sum_project( dst, src, right, 0 );
264     lighting_sum_project( dst, src, fwd,   2 );
265     lighting_sum_project( dst, src, up,    4 );
266 
267     // determine the lighting extents
268     lighting_cache_max_light( dst );
269 
270     return btrue;
271 }
272 
273 //--------------------------------------------------------------------------------------------
lighting_cache_interpolate(lighting_cache_t * dst,lighting_cache_t * src[],float u,float v)274 bool_t lighting_cache_interpolate( lighting_cache_t * dst, lighting_cache_t * src[], float u, float v )
275 {
276     int   tnc;
277     float wt_sum;
278 
279     if ( NULL == src ) return bfalse;
280 
281     if ( NULL == lighting_cache_init( dst ) ) return bfalse;
282 
283     u = CLIP( u, 0.0f, 1.0f );
284     v = CLIP( v, 0.0f, 1.0f );
285 
286     wt_sum = 0.0f;
287 
288     if ( NULL != src[0] )
289     {
290         float wt = ( 1.0f - u ) * ( 1.0f - v );
291         for ( tnc = 0; tnc < LIGHTING_VEC_SIZE; tnc++ )
292         {
293             dst->low.lighting[tnc] += src[0]->low.lighting[tnc] * wt;
294             dst->hgh.lighting[tnc] += src[0]->hgh.lighting[tnc] * wt;
295         }
296         wt_sum += wt;
297     }
298 
299     if ( NULL != src[1] )
300     {
301         float wt = u * ( 1.0f - v );
302         for ( tnc = 0; tnc < LIGHTING_VEC_SIZE; tnc++ )
303         {
304             dst->low.lighting[tnc] += src[1]->low.lighting[tnc] * wt;
305             dst->hgh.lighting[tnc] += src[1]->hgh.lighting[tnc] * wt;
306         }
307         wt_sum += wt;
308     }
309 
310     if ( NULL != src[2] )
311     {
312         float wt = ( 1.0f - u ) * v;
313         for ( tnc = 0; tnc < LIGHTING_VEC_SIZE; tnc++ )
314         {
315             dst->low.lighting[tnc] += src[2]->low.lighting[tnc] * wt;
316             dst->hgh.lighting[tnc] += src[2]->hgh.lighting[tnc] * wt;
317         }
318         wt_sum += wt;
319     }
320 
321     if ( NULL != src[3] )
322     {
323         float wt = u * v;
324         for ( tnc = 0; tnc < LIGHTING_VEC_SIZE; tnc++ )
325         {
326             dst->low.lighting[tnc] += src[3]->low.lighting[tnc] * wt;
327             dst->hgh.lighting[tnc] += src[3]->hgh.lighting[tnc] * wt;
328         }
329         wt_sum += wt;
330     }
331 
332     if ( wt_sum > 0.0f )
333     {
334         if ( wt_sum != 1.0f )
335         {
336             for ( tnc = 0; tnc < LIGHTING_VEC_SIZE; tnc++ )
337             {
338                 dst->low.lighting[tnc] /= wt_sum;
339                 dst->hgh.lighting[tnc] /= wt_sum;
340             }
341         }
342 
343         // determine the lighting extents
344         lighting_cache_max_light( dst );
345     }
346 
347     return wt_sum > 0.0f;
348 }
349 
350 //--------------------------------------------------------------------------------------------
lighting_cache_test(lighting_cache_t * src[],float u,float v,float * low_delta,float * hgh_delta)351 float lighting_cache_test( lighting_cache_t * src[], float u, float v, float * low_delta, float * hgh_delta )
352 {
353     /// @details BB@> estimate the maximum change in the lighting at this point from the
354     ///               measured delta values
355 
356     float delta, wt_sum;
357     float loc_low_delta, loc_hgh_delta;
358 
359     delta = 0.0f;
360 
361     if ( NULL == src ) return delta;
362 
363     // handle the optional parameters
364     if ( NULL == low_delta ) low_delta = &loc_low_delta;
365     if ( NULL == hgh_delta ) hgh_delta = &loc_hgh_delta;
366 
367     u = CLIP( u, 0.0f, 1.0f );
368     v = CLIP( v, 0.0f, 1.0f );
369 
370     wt_sum = 0.0f;
371 
372     if ( NULL != src[0] )
373     {
374         float wt = ( 1.0f - u ) * ( 1.0f - v );
375 
376         delta      += wt * src[0]->max_delta;
377         *low_delta += wt * src[0]->low.max_delta;
378         *hgh_delta += wt * src[0]->hgh.max_delta;
379 
380         wt_sum += wt;
381     }
382 
383     if ( NULL != src[1] )
384     {
385         float wt = u * ( 1.0f - v );
386 
387         delta      += wt * src[1]->max_delta;
388         *low_delta += wt * src[1]->low.max_delta;
389         *hgh_delta += wt * src[1]->hgh.max_delta;
390 
391         wt_sum += wt;
392     }
393 
394     if ( NULL != src[2] )
395     {
396         float wt = ( 1.0f - u ) * v;
397 
398         delta      += wt * src[2]->max_delta;
399         *low_delta += wt * src[2]->low.max_delta;
400         *hgh_delta += wt * src[2]->hgh.max_delta;
401 
402         wt_sum += wt;
403     }
404 
405     if ( NULL != src[3] )
406     {
407         float wt = u * v;
408 
409         delta      += wt * src[3]->max_delta;
410         *low_delta += wt * src[3]->low.max_delta;
411         *hgh_delta += wt * src[3]->hgh.max_delta;
412 
413         wt_sum += wt;
414     }
415 
416     if ( wt_sum > 0.0f )
417     {
418         delta      /= wt_sum;
419         *low_delta /= wt_sum;
420         *hgh_delta /= wt_sum;
421     }
422 
423     return delta;
424 }
425 
426 //--------------------------------------------------------------------------------------------
lighting_sum_project(lighting_cache_t * dst,lighting_cache_t * src,fvec3_t vec,int dir)427 bool_t lighting_sum_project( lighting_cache_t * dst, lighting_cache_t * src, fvec3_t vec, int dir )
428 {
429     if ( NULL == src || NULL == dst ) return bfalse;
430 
431     if ( dir < 0 || dir > 4 || 0 != ( dir&1 ) )
432         return bfalse;
433 
434     if ( vec.x > 0 )
435     {
436         dst->low.lighting[dir+0] += vec.x * src->low.lighting[LVEC_PX];
437         dst->low.lighting[dir+1] += vec.x * src->low.lighting[LVEC_MX];
438 
439         dst->hgh.lighting[dir+0] += vec.x * src->hgh.lighting[LVEC_PX];
440         dst->hgh.lighting[dir+1] += vec.x * src->hgh.lighting[LVEC_MX];
441     }
442     else if ( vec.x < 0 )
443     {
444         dst->low.lighting[dir+0] -= vec.x * src->low.lighting[LVEC_MX];
445         dst->low.lighting[dir+1] -= vec.x * src->low.lighting[LVEC_PX];
446 
447         dst->hgh.lighting[dir+0] -= vec.x * src->hgh.lighting[LVEC_MX];
448         dst->hgh.lighting[dir+1] -= vec.x * src->hgh.lighting[LVEC_PX];
449     }
450 
451     if ( vec.y > 0 )
452     {
453         dst->low.lighting[dir+0] += vec.y * src->low.lighting[LVEC_PY];
454         dst->low.lighting[dir+1] += vec.y * src->low.lighting[LVEC_MY];
455 
456         dst->hgh.lighting[dir+0] += vec.y * src->hgh.lighting[LVEC_PY];
457         dst->hgh.lighting[dir+1] += vec.y * src->hgh.lighting[LVEC_MY];
458     }
459     else if ( vec.y < 0 )
460     {
461         dst->low.lighting[dir+0] -= vec.y * src->low.lighting[LVEC_MY];
462         dst->low.lighting[dir+1] -= vec.y * src->low.lighting[LVEC_PY];
463 
464         dst->hgh.lighting[dir+0] -= vec.y * src->hgh.lighting[LVEC_MY];
465         dst->hgh.lighting[dir+1] -= vec.y * src->hgh.lighting[LVEC_PY];
466     }
467 
468     if ( vec.z > 0 )
469     {
470         dst->low.lighting[dir+0] += vec.z * src->low.lighting[LVEC_PZ];
471         dst->low.lighting[dir+1] += vec.z * src->low.lighting[LVEC_MZ];
472 
473         dst->hgh.lighting[dir+0] += vec.z * src->hgh.lighting[LVEC_PZ];
474         dst->hgh.lighting[dir+1] += vec.z * src->hgh.lighting[LVEC_MZ];
475     }
476     else if ( vec.z < 0 )
477     {
478         dst->low.lighting[dir+0] -= vec.z * src->low.lighting[LVEC_MZ];
479         dst->low.lighting[dir+1] -= vec.z * src->low.lighting[LVEC_PZ];
480 
481         dst->hgh.lighting[dir+0] -= vec.z * src->hgh.lighting[LVEC_MZ];
482         dst->hgh.lighting[dir+1] -= vec.z * src->hgh.lighting[LVEC_PZ];
483     }
484 
485     return btrue;
486 }
487 
488 //--------------------------------------------------------------------------------------------
lighting_evaluate_cache_base(lighting_cache_base_t * lcache,fvec3_base_t nrm,float * amb)489 float lighting_evaluate_cache_base( lighting_cache_base_t * lcache, fvec3_base_t nrm, float * amb )
490 {
491     float dir;
492     float local_amb;
493 
494     // handle the optional parameter
495     if ( NULL == amb ) amb = &local_amb;
496 
497     // check for valid data
498     if ( NULL == lcache )
499     {
500         *amb = 0.0f;
501         return 0.0f;
502     }
503 
504     // evaluate the dir vector
505     if ( 0.0f == lcache->max_light )
506     {
507         // only ambient light, or black
508         dir  = 0.0f;
509         *amb = lcache->lighting[LVEC_AMB];
510     }
511     else
512     {
513         lighting_vector_evaluate( lcache->lighting, nrm, &dir, amb );
514     }
515 
516     return dir + *amb;
517 }
518 
519 //--------------------------------------------------------------------------------------------
lighting_evaluate_cache(lighting_cache_t * src,fvec3_base_t nrm,float z,aabb_t bbox,float * light_amb,float * light_dir)520 float lighting_evaluate_cache( lighting_cache_t * src, fvec3_base_t nrm, float z, aabb_t bbox, float * light_amb, float * light_dir )
521 {
522     float loc_light_amb = 0.0f, loc_light_dir = 0.0f;
523     float light_tot;
524     float hgh_wt, low_wt, amb ;
525 
526     // check for valid parameters
527     if ( NULL == src || NULL == nrm ) return 0.0f;
528 
529     // handle optional arguments
530     if ( NULL == light_amb ) light_amb = &loc_light_amb;
531     if ( NULL == light_dir ) light_dir = &loc_light_dir;
532 
533     // determine the weighting
534     hgh_wt = ( z - bbox.mins[kZ] ) / ( bbox.maxs[kZ] - bbox.mins[kZ] );
535     hgh_wt = CLIP( hgh_wt, 0.0f, 1.0f );
536     low_wt = 1.0f - hgh_wt;
537 
538     // initialize the output
539     light_tot  = 0.0f;
540     *light_amb = 0.0f;
541     *light_dir = 0.0f;
542 
543     // optimize the use of the lighting_evaluate_cache_base() function
544     if ( low_wt > 0.0f )
545     {
546         light_tot  += low_wt * lighting_evaluate_cache_base( &( src->low ), nrm, &amb );
547         *light_amb += low_wt * amb;
548     }
549 
550     // optimize the use of the lighting_evaluate_cache_base() function
551     if ( hgh_wt > 0.0f )
552     {
553         light_tot  += hgh_wt * lighting_evaluate_cache_base( &( src->hgh ), nrm, &amb );
554         *light_amb += hgh_wt * amb;
555     }
556 
557     *light_dir = light_tot - ( *light_amb );
558 
559     return light_tot;
560 }
561 
562 //--------------------------------------------------------------------------------------------
563 //--------------------------------------------------------------------------------------------
dyna_lighting_intensity(dynalight_t * pdyna,fvec3_base_t diff)564 float dyna_lighting_intensity( dynalight_t * pdyna, fvec3_base_t diff )
565 {
566     /// @details BB@> In the Aaron's lighting, the falloff function was
567     ///                  light = (255 - r^2 / falloff) / 255.0f
568     ///              this has a definite max radius for the light, rmax = sqrt(falloff*255),
569     ///              which was good because we could have a definite range for a given light
570     ///
571     ///              This is not ideal because the light cuts off too abruptly. The new form of the
572     ///              function is (in semi-maple notation)
573     ///
574     ///              f(n,r) = integral( (1+y)^n * y * (1-y)^n, y = -1 .. r )
575     ///
576     ///              this has the advantage that it forms a bell-shaped curve that approaches 0 smoothly
577     ///              at r = -1 and r = 1. The lowest order term will always be quadratic in r, just like
578     ///              Aaron's function. To eliminate terms like r^4 and higher order even terms, you can
579     //               various f(n,r) with different n's. But combining terms with larger and larger
580     ///              n means that the left-over terms that make the function approach zero smoothly
581     ///              will have higher and higher powers of r (more expensive) and the cutoff will
582     ///              be sharper and sharper (which is against the whole point of this type of function).
583     ///
584     ///              Eliminating just the r^4 term gives the function
585     ///                  f(y) = 1 - y^2 * ( 3.0f - y^4 ) / 2
586     ///              to make it match Aaron's function best, you have to scale the function by
587     ///                  y^2 = r^2 * 2 / 765 / falloff
588     ///
589     ///              I have previously tried rational polynomial functions like
590     ///                  f(r) = k0 / (1 + k1 * r^2 ) + k2 / (1 + k3 * r^4 )
591     ///              where the second term is to cancel make the function behave like Aaron's
592     ///              at small r, and to make the function approximate same "size" of lighting area
593     ///              as Aarons. An added benefit is that this function automatically has the right
594     ///              "physics" behavior at large distances (falls off like 1/r^2). But that is the
595     ///              exact problem because the infinite range means that it can potentally affect
596     ///              the entire mesh, causing problems with computing a large number of lights
597 
598     float rho_sqr;
599     float y2;
600     float level = 0.0f;
601 
602     if ( NULL == diff ) return 0.0f;
603 
604     if ( NULL == pdyna || 0.0f == pdyna->level ) return 0.0f;
605 
606     rho_sqr  = diff[kX] * diff[kX] + diff[kY] * diff[kY];
607     y2       = rho_sqr * 2.0f / 765.0f / pdyna->falloff;
608 
609     if ( y2 > 1.0f ) return bfalse;
610 
611     level = 1.0f - 0.5f * y2 * ( 3.0f - y2 * y2 );
612     level *= pdyna->level;
613 
614     return level;
615 }
616 
617 //--------------------------------------------------------------------------------------------
sum_dyna_lighting(dynalight_t * pdyna,lighting_vector_t lighting,fvec3_base_t nrm)618 bool_t sum_dyna_lighting( dynalight_t * pdyna, lighting_vector_t lighting, fvec3_base_t nrm )
619 {
620     fvec3_base_t local_nrm;
621 
622     float rad_sqr, level;
623 
624     if ( NULL == pdyna || NULL == lighting || NULL == nrm ) return bfalse;
625 
626     level = 255 * dyna_lighting_intensity( pdyna, nrm );
627     if ( 0.0f == level ) return btrue;
628 
629     // allow negative lighting, or blind spots will not work properly
630     rad_sqr = nrm[kX] * nrm[kX] + nrm[kY] * nrm[kY] + nrm[kZ] * nrm[kZ];
631 
632     // make a local copy of the normal so we do not normalize the data in the calling function
633     memcpy( local_nrm, nrm, sizeof( local_nrm ) );
634 
635     // do the normalization
636     if ( 1.0f != rad_sqr && 0.0f != rad_sqr )
637     {
638         float rad = SQRT( rad_sqr );
639         local_nrm[kX] /= rad;
640         local_nrm[kY] /= rad;
641         local_nrm[kZ] /= rad;
642     }
643 
644     // sum the lighting
645     lighting_vector_sum( lighting, local_nrm, level, 0.0f );
646 
647     return btrue;
648 }
649 
650