2 * $Logfile: /Freespace2/code/Lighting/Lighting.cpp $
7 * Code to calculate dynamic lighting on a vertex.
10 * Revision 1.1 2002/05/03 03:28:09 root
14 * 5 6/22/99 2:22p Dave
15 * Doh. Fixed a type bug.
17 * 4 6/18/99 5:16p Dave
18 * Added real beam weapon lighting. Fixed beam weapon sounds. Added MOTD
19 * dialog to PXO screen.
21 * 3 5/09/99 6:00p Dave
22 * Lots of cool new effects. E3 build tweaks.
24 * 2 10/07/98 10:53a Dave
27 * 1 10/07/98 10:49a Dave
29 * 37 4/12/98 9:56a John
30 * Made lighting detail flags work. Made explosions cast light on
33 * 36 4/10/98 5:20p John
34 * Changed RGB in lighting structure to be ubytes. Removed old
35 * not-necessary 24 bpp software stuff.
37 * 35 4/04/98 5:17p John
38 * Added sun stuff. Made Glide optionally use 8-bpp textures. (looks
41 * 34 3/14/98 3:07p Adam
42 * re-added some lighting changes
44 * 33 3/13/98 4:10p John
45 * Put back in Adam's old lighting values.
47 * 32 3/12/98 8:42a John
48 * Checked in Adam's new lighting values.
49 * Checked in changes to timing models code
51 * 31 2/26/98 5:42p John
53 * 30 2/26/98 3:25p John
54 * Added code to turn on/off lighting. Made lighting used dist*dist
57 * 29 2/19/98 10:51p John
58 * Enabled colored lighting for hardware (Glide)
60 * 28 2/13/98 5:00p John
61 * Made lighting push functions return number of releveent lights.
63 * 27 1/29/98 3:36p Johnson
64 * JAS: Fixed some problems with pre_player_entry.
66 * 26 1/29/98 3:16p Allender
67 * fix compiler warnings
69 * 25 1/29/98 8:14a John
70 * Added support for RGB lighting
72 * 24 1/23/98 5:08p John
73 * Took L out of vertex structure used B (blue) instead. Took all small
74 * fireballs out of fireball types and used particles instead. Fixed some
75 * debris explosion things. Restructured fireball code. Restructured
76 * some lighting code. Made dynamic lighting on by default. Made groups
77 * of lasers only cast one light. Made fireballs not cast light.
79 * 23 1/10/98 1:14p John
80 * Added explanation to debug console commands
82 * 22 1/02/98 11:53a Adam
83 * Changed lighting mode to darken. Changed ambient and reflect to .75
84 * and .50 to compensate.
86 * 21 1/02/98 11:28a Adam
87 * Changed default ambient to 0.55f from 0.45f.
89 * 20 12/21/97 4:33p John
90 * Made debug console functions a class that registers itself
91 * automatically, so you don't need to add the function to
94 * 19 12/17/97 7:53p John
95 * Fixed a bug where gunpoint for flashes were in world coordinates,
96 * should have been object.
98 * 18 12/17/97 5:11p John
99 * Added brightening back into fade table. Added code for doing the fast
100 * dynamic gun flashes and thruster flashes.
102 * 17 12/12/97 3:02p John
103 * First Rev of Ship Shadows
105 * 16 11/07/97 7:24p John
106 * changed lighting to take two ranges.
107 * In textest, added test code to draw nebulas
109 * 15 11/04/97 9:19p John
110 * Optimized dynamic lighting more.
112 * 14 10/29/97 5:05p John
113 * Changed dynamic lighting to only rotate and calculate lighting for
114 * point lights that are close to an object. Changed lower framerate cap
117 * 13 9/24/97 12:37p Mike
118 * Crank ambient lighting from 0.2 to 0.6
120 * 12 9/17/97 9:44a John
121 * fixed bug with lighting set to default
123 * 11 9/11/97 5:36p Jasen
124 * Changed ambient and reflective lighting values to give a bit more
125 * realism to the game. Yeah, yeah, I know I am not a programmer.
127 * 10 4/24/97 2:58p John
129 * 9 4/24/97 11:49a John
130 * added new lighting commands to console.
132 * 8 4/23/97 5:26p John
133 * First rev of new debug console stuff.
135 * 7 4/22/97 3:14p John
136 * upped the ambient light
138 * 6 4/17/97 6:06p John
139 * New code/data for v19 of BSPGEN with smoothing and zbuffer
142 * 5 4/08/97 5:18p John
143 * First rev of decent (dynamic, correct) lighting in FreeSpace.
145 * 4 2/17/97 5:18p John
146 * Added a bunch of RCS headers to a bunch of old files that don't have
149 * 3 1/30/97 9:35a Hoffoss
150 * Added header for files.
160 #include "lighting.h"
162 #include "systemvars.h"
164 #define MAX_LIGHTS 256
165 #define MAX_LIGHT_LEVELS 16
167 #define LT_DIRECTIONAL 0 // A light like a sun
168 #define LT_POINT 1 // A point light, like an explosion
169 #define LT_TUBE 2 // A tube light, like a fluorescent light
171 typedef struct light {
172 int type; // What type of light this is
173 vector vec; // location in world space of a point light or the direction of a directional light or the first point on the tube for a tube light
174 vector vec2; // second point on a tube light
175 vector local_vec; // rotated light vector
176 vector local_vec2; // rotated 2nd light vector for a tube light
177 float intensity; // How bright the light is.
178 float rad1, rad1_squared; // How big of an area a point light affect. Is equal to l->intensity / MIN_LIGHT;
179 float rad2, rad2_squared; // How big of an area a point light affect. Is equal to l->intensity / MIN_LIGHT;
180 float r,g,b; // The color components of the light
181 int ignore_objnum; // Don't light this object. Used to optimize weapons casting light on parents.
182 int affected_objnum; // for "unique lights". ie, lights which only affect one object (trust me, its useful)
185 light Lights[MAX_LIGHTS];
188 light *Relevent_lights[MAX_LIGHTS][MAX_LIGHT_LEVELS];
189 int Num_relevent_lights[MAX_LIGHT_LEVELS];
190 int Num_light_levels = 0;
192 #define MAX_STATIC_LIGHTS 10
193 light * Static_light[MAX_STATIC_LIGHTS];
194 int Static_light_count = 0;
196 static int Light_in_shadow = 0; // If true, this means we're in a shadow
198 #define LM_BRIGHTEN 0
201 #define MIN_LIGHT 0.03f // When light drops below this level, ignore it. Must be non-zero! (1/32)
204 int Lighting_off = 0;
206 // For lighting values, 0.75 is full intensity
208 #if 1 // ADAM'S new stuff
209 int Lighting_mode = LM_BRIGHTEN;
210 #define AMBIENT_LIGHT_DEFAULT 0.15f //0.10f
211 #define REFLECTIVE_LIGHT_DEFAULT 0.75f //0.90f
213 int Lighting_mode = LM_DARKEN;
214 #define AMBIENT_LIGHT_DEFAULT 0.75f //0.10f
215 #define REFLECTIVE_LIGHT_DEFAULT 0.50f //0.90f
218 float Ambient_light = AMBIENT_LIGHT_DEFAULT;
219 float Reflective_light = REFLECTIVE_LIGHT_DEFAULT;
221 int Lighting_flag = 1;
223 DCF(light,"Changes lighting parameters")
226 dc_get_arg(ARG_STRING);
227 if ( !strcmp( Dc_arg, "ambient" )) {
228 dc_get_arg(ARG_FLOAT);
229 if ( (Dc_arg_float < 0.0f) || (Dc_arg_float > 1.0f) ) {
232 Ambient_light = Dc_arg_float;
234 } else if ( !strcmp( Dc_arg, "reflect" )) {
235 dc_get_arg(ARG_FLOAT);
236 if ( (Dc_arg_float < 0.0f) || (Dc_arg_float > 1.0f) ) {
239 Reflective_light = Dc_arg_float;
241 } else if ( !strcmp( Dc_arg, "default" )) {
242 Lighting_mode = LM_BRIGHTEN;
243 Ambient_light = AMBIENT_LIGHT_DEFAULT;
244 Reflective_light = REFLECTIVE_LIGHT_DEFAULT;
246 } else if ( !strcmp( Dc_arg, "mode" )) {
247 dc_get_arg(ARG_STRING);
248 if ( !strcmp(Dc_arg, "light") ) {
249 Lighting_mode = LM_BRIGHTEN;
250 } else if ( !strcmp(Dc_arg, "darken")) {
251 Lighting_mode = LM_DARKEN;
255 } else if ( !strcmp( Dc_arg, "dynamic" )) {
256 dc_get_arg(ARG_TRUE|ARG_FALSE|ARG_NONE);
257 if ( Dc_arg_type & ARG_TRUE ) Lighting_flag = 1;
258 else if ( Dc_arg_type & ARG_FALSE ) Lighting_flag = 0;
259 else if ( Dc_arg_type & ARG_NONE ) Lighting_flag ^= 1;
260 } else if ( !strcmp( Dc_arg, "on" ) ) {
262 } else if ( !strcmp( Dc_arg, "off" ) ) {
265 // print usage, not stats
271 dc_printf( "Usage: light keyword\nWhere keyword can be in the following forms:\n" );
272 dc_printf( "light on|off Turns all lighting on/off\n" );
273 dc_printf( "light default Resets lighting to all default values\n" );
274 dc_printf( "light ambient X Where X is the ambient light between 0 and 1.0\n" );
275 dc_printf( "light reflect X Where X is the material reflectiveness between 0 and 1.0\n" );
276 dc_printf( "light dynamic [bool] Toggles dynamic lighting on/off\n" );
277 dc_printf( "light mode [light|darken] Changes the lighting mode.\n" );
278 dc_printf( " Where 'light' means the global light adds light.\n");
279 dc_printf( " and 'darken' means the global light subtracts light.\n");
280 Dc_status = 0; // don't print status if help is printed. Too messy.
284 dc_printf( "Ambient light is set to %.2f\n", Ambient_light );
285 dc_printf( "Reflective light is set to %.2f\n", Reflective_light );
286 dc_printf( "Dynamic lighting is: %s\n", (Lighting_flag?"on":"off") );
287 switch( Lighting_mode ) {
288 case LM_BRIGHTEN: dc_printf( "Lighting mode is: light\n" ); break;
289 case LM_DARKEN: dc_printf( "Lighting mode is: darken\n" ); break;
290 default: dc_printf( "Lighting mode is: UNKNOWN\n" ); break;
299 // reset static (sun) lights
300 for(idx=0; idx<MAX_STATIC_LIGHTS; idx++){
301 Static_light[idx] = NULL;
303 Static_light_count = 0;
306 light_filter_reset();
309 // Rotates the light into the current frame of reference
310 void light_rotate(light * l)
314 // Rotate the light direction into local coodinates
315 vm_vec_rotate(&l->local_vec, &l->vec, &Light_matrix );
320 // Rotate the point into local coordinates
321 vm_vec_sub(&tempv, &l->vec, &Light_base );
322 vm_vec_rotate(&l->local_vec, &tempv, &Light_matrix );
328 // Rotate the point into local coordinates
329 vm_vec_sub(&tempv, &l->vec, &Light_base );
330 vm_vec_rotate(&l->local_vec, &tempv, &Light_matrix );
332 // Rotate the point into local coordinates
333 vm_vec_sub(&tempv, &l->vec2, &Light_base );
334 vm_vec_rotate(&l->local_vec2, &tempv, &Light_matrix );
339 Int3(); // Invalid light type
343 // Sets the ambient lighting level.
345 void light_set_ambient(float ambient_light)
349 void light_add_directional( vector *dir, float intensity, float r, float g, float b )
353 if ( Lighting_off ) return;
355 if ( Num_lights >= MAX_LIGHTS ) return;
357 l = &Lights[Num_lights++];
359 l->type = LT_DIRECTIONAL;
361 if ( Lighting_mode == LM_BRIGHTEN ) {
362 vm_vec_copy_scale( &l->vec, dir, -1.0f );
364 vm_vec_copy_scale( &l->vec, dir, 1.0f );
370 l->intensity = intensity;
373 l->rad1_squared = l->rad1*l->rad1;
374 l->rad2_squared = l->rad2*l->rad2;
375 l->ignore_objnum = -1;
376 l->affected_objnum = -1;
378 Assert( Num_light_levels <= 1 );
379 // Relevent_lights[Num_relevent_lights[Num_light_levels-1]++][Num_light_levels-1] = l;
381 if(Static_light_count < MAX_STATIC_LIGHTS){
382 Static_light[Static_light_count++] = l;
387 void light_add_point( vector * pos, float rad1, float rad2, float intensity, float r, float g, float b, int ignore_objnum )
391 if ( Lighting_off ) return;
393 if (!Lighting_flag) return;
395 // if ( keyd_pressed[KEY_LSHIFT] ) return;
397 if ( Num_lights >= MAX_LIGHTS ) {
398 mprintf(( "Out of lights!\n" ));
402 l = &Lights[Num_lights++];
409 l->intensity = intensity;
412 l->rad1_squared = l->rad1*l->rad1;
413 l->rad2_squared = l->rad2*l->rad2;
414 l->ignore_objnum = ignore_objnum;
415 l->affected_objnum = -1;
417 Assert( Num_light_levels <= 1 );
418 // Relevent_lights[Num_relevent_lights[Num_light_levels-1]++][Num_light_levels-1] = l;
421 void light_add_point_unique( vector * pos, float rad1, float rad2, float intensity, float r, float g, float b, int affected_objnum)
425 if ( Lighting_off ) return;
427 if (!Lighting_flag) return;
429 // if ( keyd_pressed[KEY_LSHIFT] ) return;
431 if ( Num_lights >= MAX_LIGHTS ) {
432 mprintf(( "Out of lights!\n" ));
436 l = &Lights[Num_lights++];
443 l->intensity = intensity;
446 l->rad1_squared = l->rad1*l->rad1;
447 l->rad2_squared = l->rad2*l->rad2;
448 l->ignore_objnum = -1;
449 l->affected_objnum = affected_objnum;
451 Assert( Num_light_levels <= 1 );
454 // for now, tube lights only affect one ship (to keep the filter stuff simple)
455 void light_add_tube(vector *p0, vector *p1, float r1, float r2, float intensity, float r, float g, float b, int affected_objnum)
459 if ( Lighting_off ) return;
461 if (!Lighting_flag) return;
463 // if ( keyd_pressed[KEY_LSHIFT] ) return;
465 if ( Num_lights >= MAX_LIGHTS ) {
466 mprintf(( "Out of lights!\n" ));
470 l = &Lights[Num_lights++];
478 l->intensity = intensity;
481 l->rad1_squared = l->rad1*l->rad1;
482 l->rad2_squared = l->rad2*l->rad2;
483 l->ignore_objnum = -1;
484 l->affected_objnum = affected_objnum;
486 Assert( Num_light_levels <= 1 );
489 // Reset the list of lights to point to all lights.
490 void light_filter_reset()
495 if ( Lighting_off ) return;
497 Num_light_levels = 1;
499 int n = Num_light_levels-1;
500 Num_relevent_lights[n] = 0;
503 for (i=0; i<Num_lights; i++, l++ ) {
504 Relevent_lights[Num_relevent_lights[n]++][n] = l;
509 // Makes a list of only the lights that will affect
510 // the sphere specified by 'pos' and 'rad' and 'objnum'
511 int light_filter_push( int objnum, vector *pos, float rad )
516 if ( Lighting_off ) return 0;
518 light_filter_reset();
521 n1 = Num_light_levels-1;
522 n2 = Num_light_levels;
524 Assert( Num_light_levels < MAX_LIGHT_LEVELS );
526 Num_relevent_lights[n2] = 0;
528 for (i=0; i<Num_relevent_lights[n1]; i++ ) {
529 l = Relevent_lights[i][n1];
533 //Relevent_lights[Num_relevent_lights[n2]++][n2] = l;
537 // if this is a "unique" light source, it only affects one guy
538 if(l->affected_objnum >= 0){
539 if(objnum == l->affected_objnum){
541 float dist_squared, max_dist_squared;
542 vm_vec_sub( &to_light, &l->vec, pos );
543 dist_squared = vm_vec_mag_squared(&to_light);
545 max_dist_squared = l->rad2+rad;
546 max_dist_squared *= max_dist_squared;
548 if ( dist_squared < max_dist_squared ) {
549 Relevent_lights[Num_relevent_lights[n2]++][n2] = l;
553 // otherwise check all relevant objects
555 // if ( (l->ignore_objnum<0) || (l->ignore_objnum != objnum) ) {
557 float dist_squared, max_dist_squared;
558 vm_vec_sub( &to_light, &l->vec, pos );
559 dist_squared = vm_vec_mag_squared(&to_light);
561 max_dist_squared = l->rad2+rad;
562 max_dist_squared *= max_dist_squared;
564 if ( dist_squared < max_dist_squared ) {
565 Relevent_lights[Num_relevent_lights[n2]++][n2] = l;
572 // hmm. this could probably be more optimal
574 // all tubes are "unique" light sources for now
575 if((l->affected_objnum >= 0) && (objnum == l->affected_objnum)){
576 Relevent_lights[Num_relevent_lights[n2]++][n2] = l;
581 Int3(); // Invalid light type
585 return Num_relevent_lights[n2];
588 int is_inside( vector *min, vector *max, vector * p0, float rad )
590 float *origin = (float *)&p0->x;
591 float *minB = (float *)min;
592 float *maxB = (float *)max;
595 for (i=0; i<3; i++ ) {
596 if ( origin[i] < minB[i] - rad ) {
598 } else if (origin[i] > maxB[i] + rad ) {
606 int light_filter_push_box( vector *min, vector *max )
611 if ( Lighting_off ) return 0;
614 n1 = Num_light_levels-1;
615 n2 = Num_light_levels;
618 // static int mll = -1;
619 // if ( Num_light_levels > mll ) {
620 // mll = Num_light_levels;
621 // mprintf(( "Max level = %d\n", mll ));
624 Assert( Num_light_levels < MAX_LIGHT_LEVELS );
626 Num_relevent_lights[n2] = 0;
628 for (i=0; i<Num_relevent_lights[n1]; i++ ) {
629 l = Relevent_lights[i][n1];
633 Int3(); //Relevent_lights[Num_relevent_lights[n2]++][n2] = l;
638 if ( is_inside( min, max, &l->local_vec, l->rad2 ) ) {
639 Relevent_lights[Num_relevent_lights[n2]++][n2] = l;
645 if ( is_inside(min, max, &l->local_vec, l->rad2) || is_inside(min, max, &l->local_vec2, l->rad2) ) {
646 Relevent_lights[Num_relevent_lights[n2]++][n2] = l;
651 Int3(); // Invalid light type
655 return Num_relevent_lights[n2];
658 void light_filter_pop()
660 if ( Lighting_off ) return;
663 Assert( Num_light_levels > 0 );
666 int l_num_points=0, l_num_lights=0;
669 void light_rotate_all()
674 if ( Lighting_off ) return;
676 int n = Num_light_levels-1;
679 for (i=0; i<Num_relevent_lights[n]; i++ ) {
680 l = Relevent_lights[i][n];
684 for(i=0; i<Static_light_count; i++){
685 light_rotate(Static_light[i]);
689 // for (i=0; i<Num_lights; i++, l++ ) {
694 // return the # of global light sources
695 int light_get_global_count()
697 return Static_light_count;
700 int light_get_global_dir(vector *pos, int n)
702 if((n > MAX_STATIC_LIGHTS) || (n > Static_light_count-1)){
706 if ( Static_light[n] == NULL ) {
711 *pos = Static_light[n]->vec;
713 if ( Lighting_mode != LM_DARKEN ) {
714 vm_vec_scale( pos, -1.0f );
721 void light_set_shadow( int state )
723 Light_in_shadow = state;
727 ubyte light_apply( vector *pos, vector * norm, float static_light_level )
733 if (Detail.lighting==0) {
735 ubyte l = ubyte(fl2i(static_light_level*255.0f));
739 if ( Lighting_off ) return 191;
741 // Factor in ambient light
742 lval = Ambient_light;
744 // Factor in light from suns if there are any
745 if ( !Light_in_shadow ){
746 // apply all sun lights
747 for(idx=0; idx<Static_light_count; idx++){
751 if(Static_light[idx] == NULL){
755 // calculate light from surface normal
756 ltmp = -vm_vec_dot(&Static_light[idx]->local_vec, norm )*Static_light[idx]->intensity*Reflective_light; // reflective light
758 switch(Lighting_mode) {
774 // At this point, l must be between 0 and 0.75 (0.75-1.0 is for dynamic light only)
777 } else if ( lval > 0.75f ) {
781 lval *= static_light_level;
783 int n = Num_light_levels-1;
785 l_num_lights += Num_relevent_lights[n];
788 for (i=0; i<Num_relevent_lights[n]; i++ ) {
789 l = Relevent_lights[i][n];
793 vm_vec_sub( &to_light, &l->local_vec, pos );
794 dot = vm_vec_dot(&to_light, norm );
796 dist = vm_vec_mag_squared(&to_light);
797 if ( dist < l->rad1_squared ) {
798 lval += l->intensity*dot;
799 } else if ( dist < l->rad2_squared ) {
801 float n = dist - l->rad1_squared;
802 float d = l->rad2_squared - l->rad1_squared;
803 float ltmp = (1.0f - n / d )*dot*l->intensity;
812 return ubyte(fl2i(lval*255.0f));
816 void light_apply_rgb( ubyte *param_r, ubyte *param_g, ubyte *param_b, vector *pos, vector * norm, float static_light_level )
819 float rval, gval, bval;
822 if (Detail.lighting==0) {
824 ubyte l = ubyte(fl2i(static_light_level*255.0f));
831 if ( Lighting_off ) {
838 // Factor in ambient light
839 rval = Ambient_light;
840 gval = Ambient_light;
841 bval = Ambient_light;
843 // Factor in light from sun if there is one
844 if ( !Light_in_shadow ){
845 // apply all sun lights
846 for(idx=0; idx<Static_light_count; idx++){
850 if(Static_light[idx] == NULL){
854 // calculate light from surface normal
855 ltmp = -vm_vec_dot(&Static_light[idx]->local_vec, norm )*Static_light[idx]->intensity*Reflective_light; // reflective light
857 switch(Lighting_mode) {
860 rval += Static_light[idx]->r * ltmp;
861 gval += Static_light[idx]->g * ltmp;
862 bval += Static_light[idx]->b * ltmp;
867 rval -= ltmp; if ( rval < 0.0f ) rval = 0.0f;
868 gval -= ltmp; if ( gval < 0.0f ) gval = 0.0f;
869 bval -= ltmp; if ( bval < 0.0f ) bval = 0.0f;
876 // At this point, l must be between 0 and 0.75 (0.75-1.0 is for dynamic light only)
879 } else if ( rval > 0.75f ) {
884 } else if ( gval > 0.75f ) {
889 } else if ( bval > 0.75f ) {
893 rval *= static_light_level;
894 gval *= static_light_level;
895 bval *= static_light_level;
897 int n = Num_light_levels-1;
899 l_num_lights += Num_relevent_lights[n];
905 for (i=0; i<Num_relevent_lights[n]; i++ ) {
906 l = Relevent_lights[i][n];
912 vm_vec_sub( &to_light, &l->local_vec, pos );
917 if(vm_vec_dist_to_line(pos, &l->local_vec, &l->local_vec2, &temp, &dist) != 0){
920 vm_vec_sub(&to_light, &temp, pos);
921 dist *= dist; // since we use radius squared
929 dot = vm_vec_dot(&to_light, norm);
932 // indicating that we already calculated the distance (vm_vec_dist_to_line(...) does this for us)
934 dist = vm_vec_mag_squared(&to_light);
936 if ( dist < l->rad1_squared ) {
938 ratio = l->intensity*dot;
943 } else if ( dist < l->rad2_squared ) {
946 float n = dist - l->rad1_squared;
947 float d = l->rad2_squared - l->rad1_squared;
948 ratio = (1.0f - n / d)*dot*l->intensity;
958 if ( gval > m ) m = gval;
959 if ( bval > m ) m = bval;
971 } else if ( rval > 1.0f ) {
976 } else if ( gval > 1.0f ) {
981 } else if ( bval > 1.0f ) {
985 *param_r = ubyte(fl2i(rval*255.0f));
986 *param_g = ubyte(fl2i(gval*255.0f));
987 *param_b = ubyte(fl2i(bval*255.0f));
992 float light_apply( vector *pos, vector * norm )
996 light_apply_rgb( &r, &g, &b, pos, norm );
997 return (r+g+b) / 3.0f;
999 return light_apply_ramp( pos, norm );