/* * Copyright (C) Volition, Inc. 1999. All rights reserved. * * All source code herein is the property of Volition, Inc. You may not sell * or otherwise commercially exploit the source or things you created based on the * source. * */ #include "math/vecmat.h" #include "render/3d.h" #include "lighting/lighting.h" #include "globalincs/systemvars.h" #include "graphics/2d.h" #include "cmdline/cmdline.h" #define MAX_LIGHT_LEVELS 16 int cell_shaded_lightmap = -1; light Lights[MAX_LIGHTS]; int Num_lights=0; extern int Cmdline_nohtl; light *Relevent_lights[MAX_LIGHTS][MAX_LIGHT_LEVELS]; int Num_relevent_lights[MAX_LIGHT_LEVELS]; int Num_light_levels = 0; SCP_vector Static_light; static int Light_in_shadow = 0; // If true, this means we're in a shadow #define LM_BRIGHTEN 0 #define LM_DARKEN 1 #define MIN_LIGHT 0.03f // When light drops below this level, ignore it. Must be non-zero! (1/32) int Lighting_off = 0; // For lighting values, 0.75 is full intensity #if 1 // ADAM'S new stuff int Lighting_mode = LM_BRIGHTEN; #define AMBIENT_LIGHT_DEFAULT 0.15f //0.10f #define REFLECTIVE_LIGHT_DEFAULT 0.75f //0.90f #else int Lighting_mode = LM_DARKEN; #define AMBIENT_LIGHT_DEFAULT 0.75f //0.10f #define REFLECTIVE_LIGHT_DEFAULT 0.50f //0.90f #endif float Ambient_light = AMBIENT_LIGHT_DEFAULT; float Reflective_light = REFLECTIVE_LIGHT_DEFAULT; int Lighting_flag = 1; DCF(light,"Changes lighting parameters") { if ( Dc_command ) { dc_get_arg(ARG_STRING); if ( !strcmp( Dc_arg, "ambient" )) { dc_get_arg(ARG_FLOAT); if ( (Dc_arg_float < 0.0f) || (Dc_arg_float > 1.0f) ) { Dc_help = 1; } else { Ambient_light = Dc_arg_float; } } else if ( !strcmp( Dc_arg, "reflect" )) { dc_get_arg(ARG_FLOAT); if ( (Dc_arg_float < 0.0f) || (Dc_arg_float > 1.0f) ) { Dc_help = 1; } else { Reflective_light = Dc_arg_float; } } else if ( !strcmp( Dc_arg, "default" )) { Lighting_mode = LM_BRIGHTEN; Ambient_light = AMBIENT_LIGHT_DEFAULT; Reflective_light = REFLECTIVE_LIGHT_DEFAULT; Lighting_flag = 0; } else if ( !strcmp( Dc_arg, "mode" )) { dc_get_arg(ARG_STRING); if ( !strcmp(Dc_arg, "light") ) { Lighting_mode = LM_BRIGHTEN; } else if ( !strcmp(Dc_arg, "darken")) { Lighting_mode = LM_DARKEN; } else { Dc_help = 1; } } else if ( !strcmp( Dc_arg, "dynamic" )) { dc_get_arg(ARG_TRUE|ARG_FALSE|ARG_NONE); if ( Dc_arg_type & ARG_TRUE ) Lighting_flag = 1; else if ( Dc_arg_type & ARG_FALSE ) Lighting_flag = 0; else if ( Dc_arg_type & ARG_NONE ) Lighting_flag ^= 1; } else if ( !strcmp( Dc_arg, "on" ) ) { Lighting_off = 0; } else if ( !strcmp( Dc_arg, "off" ) ) { Lighting_off = 1; } else { // print usage, not stats Dc_help = 1; } } if ( Dc_help ) { dc_printf( "Usage: light keyword\nWhere keyword can be in the following forms:\n" ); dc_printf( "light on|off Turns all lighting on/off\n" ); dc_printf( "light default Resets lighting to all default values\n" ); dc_printf( "light ambient X Where X is the ambient light between 0 and 1.0\n" ); dc_printf( "light reflect X Where X is the material reflectiveness between 0 and 1.0\n" ); dc_printf( "light dynamic [bool] Toggles dynamic lighting on/off\n" ); dc_printf( "light mode [light|darken] Changes the lighting mode.\n" ); dc_printf( " Where 'light' means the global light adds light.\n"); dc_printf( " and 'darken' means the global light subtracts light.\n"); Dc_status = 0; // don't print status if help is printed. Too messy. } if ( Dc_status ) { dc_printf( "Ambient light is set to %.2f\n", Ambient_light ); dc_printf( "Reflective light is set to %.2f\n", Reflective_light ); dc_printf( "Dynamic lighting is: %s\n", (Lighting_flag?"on":"off") ); switch( Lighting_mode ) { case LM_BRIGHTEN: dc_printf( "Lighting mode is: light\n" ); break; case LM_DARKEN: dc_printf( "Lighting mode is: darken\n" ); break; default: dc_printf( "Lighting mode is: UNKNOWN\n" ); break; } } } void light_reset() { Static_light.clear(); Num_lights = 0; light_filter_reset(); } extern vec3d Object_position; /** * Rotates the light into the current frame of reference * * @param l Light to rotate */ void light_rotate(light * l) { switch( l->type ) { case LT_DIRECTIONAL: // Rotate the light direction into local coodinates vm_vec_rotate(&l->local_vec, &l->vec, &Light_matrix ); break; case LT_POINT: { vec3d tempv; // Rotate the point into local coordinates vm_vec_sub(&tempv, &l->vec, &Light_base ); vm_vec_rotate(&l->local_vec, &tempv, &Light_matrix ); } break; case LT_TUBE:{ vec3d tempv; // Rotate the point into local coordinates vm_vec_sub(&tempv, &l->vec, &Light_base ); vm_vec_rotate(&l->local_vec, &tempv, &Light_matrix ); // Rotate the point into local coordinates vm_vec_sub(&tempv, &l->vec2, &Light_base ); vm_vec_rotate(&l->local_vec2, &tempv, &Light_matrix ); } break; default: Int3(); // Invalid light type } } // Sets the ambient lighting level. // Ignored for now. void light_set_ambient(float ambient_light) { } void light_add_directional( vec3d *dir, float intensity, float r, float g, float b, float spec_r, float spec_g, float spec_b, bool specular ) { if(!specular){ spec_r = r; spec_g = g; spec_b = b; } light * l; if ( Lighting_off ) return; if ( Num_lights >= MAX_LIGHTS ) return; l = &Lights[Num_lights++]; l->type = LT_DIRECTIONAL; if ( Lighting_mode == LM_BRIGHTEN ) { vm_vec_copy_scale( &l->vec, dir, -1.0f ); } else { vm_vec_copy_scale( &l->vec, dir, 1.0f ); } l->r = r; l->g = g; l->b = b; l->spec_r = spec_r; l->spec_g = spec_g; l->spec_b = spec_b; l->intensity = intensity; l->rada = 0.0f; l->radb = 0.0f; l->rada_squared = l->rada*l->rada; l->radb_squared = l->radb*l->radb; l->light_ignore_objnum = -1; l->affected_objnum = -1; l->instance = Num_lights-1; Assert( Num_light_levels <= 1 ); Static_light.push_back(l); } void light_add_point( vec3d * pos, float r1, float r2, float intensity, float r, float g, float b, int light_ignore_objnum, float spec_r, float spec_g, float spec_b, bool specular ) { Assertion( r1 > 0.0f, "Invalid radius r1 specified for light: %d. Radius must be > 0.0f. Examine stack trace to determine culprit.\n", r1 ); Assertion( r2 > 0.0f, "Invalid radius r2 specified for light: %d. Radius must be > 0.0f. Examine stack trace to determine culprit.\n", r2 ); if (r1 < 0.0001f || r2 < 0.0001f) return; if(!specular){ spec_r = r; spec_g = g; spec_b = b; } light * l; if ( Lighting_off ) return; if (!Lighting_flag) return; if ( Num_lights >= MAX_LIGHTS ) { mprintf(( "Out of lights!\n" )); return; } l = &Lights[Num_lights++]; l->type = LT_POINT; l->vec = *pos; l->r = r; l->g = g; l->b = b; l->spec_r = spec_r; l->spec_g = spec_g; l->spec_b = spec_b; l->intensity = intensity; l->rada = r1; l->radb = r2; l->rada_squared = l->rada*l->rada; l->radb_squared = l->radb*l->radb; l->light_ignore_objnum = light_ignore_objnum; l->affected_objnum = -1; l->instance = Num_lights-1; Assert( Num_light_levels <= 1 ); } void light_add_point_unique( vec3d * pos, float r1, float r2, float intensity, float r, float g, float b, int affected_objnum, float spec_r, float spec_g, float spec_b, bool specular ) { Assert(r1 >0); Assert(r2 >0); if(!specular){ spec_r = r; spec_g = g; spec_b = b; } light * l; if ( Lighting_off ) return; if (!Lighting_flag) return; if ( Num_lights >= MAX_LIGHTS ) { mprintf(( "Out of lights!\n" )); return; } l = &Lights[Num_lights++]; l->type = LT_POINT; l->vec = *pos; l->r = r; l->g = g; l->b = b; l->spec_r = spec_r; l->spec_g = spec_g; l->spec_b = spec_b; l->intensity = intensity; l->rada = r1; l->radb = r2; l->rada_squared = l->rada*l->rada; l->radb_squared = l->radb*l->radb; l->light_ignore_objnum = -1; l->affected_objnum = affected_objnum; l->instance = Num_lights-1; Assert( Num_light_levels <= 1 ); } // beams affect every ship except the firing ship extern int Use_GLSL; void light_add_tube(vec3d *p0, vec3d *p1, float r1, float r2, float intensity, float r, float g, float b, int affected_objnum, float spec_r, float spec_g, float spec_b, bool specular ) { Assert(r1 >0); Assert(r2 >0); if(!specular){ spec_r = r; spec_g = g; spec_b = b; } light * l; if ( Lighting_off ) return; if (!Lighting_flag) return; if ( Num_lights >= MAX_LIGHTS ) { mprintf(( "Out of lights!\n" )); return; } l = &Lights[Num_lights++]; l->type = LT_TUBE; l->vec = *p0; l->vec2 = *p1; l->r = r; l->g = g; l->b = b; l->spec_r = spec_r; l->spec_g = spec_g; l->spec_b = spec_b; l->intensity = intensity; l->rada = r1; l->radb = r2; l->rada_squared = l->rada*l->rada; l->radb_squared = l->radb*l->radb; l->light_ignore_objnum = Use_GLSL>1 ?affected_objnum : -1; l->affected_objnum = Use_GLSL>1 ? -1 : affected_objnum; l->instance = Num_lights-1; Assert( Num_light_levels <= 1 ); } /** * Reset the list of lights to point to all lights. */ void light_filter_reset() { int i; light *l; if ( Lighting_off ) return; Num_light_levels = 1; int n = Num_light_levels-1; Num_relevent_lights[n] = 0; l = Lights; for (i=0; itype ) { case LT_DIRECTIONAL: //Relevent_lights[Num_relevent_lights[n2]++][n2] = l; break; case LT_POINT: { // if this is a "unique" light source, it only affects one guy if(l->affected_objnum >= 0){ if(objnum == l->affected_objnum){ vec3d to_light; float dist_squared, max_dist_squared; vm_vec_sub( &to_light, &l->vec, pos ); dist_squared = vm_vec_mag_squared(&to_light); max_dist_squared = l->radb+rad; max_dist_squared *= max_dist_squared; if ( dist_squared < max_dist_squared ) { Relevent_lights[Num_relevent_lights[n2]++][n2] = l; } } } // otherwise check all relevant objects else { vec3d to_light; float dist_squared, max_dist_squared; vm_vec_sub( &to_light, &l->vec, pos ); dist_squared = vm_vec_mag_squared(&to_light); max_dist_squared = l->radb+rad; max_dist_squared *= max_dist_squared; if ( dist_squared < max_dist_squared ) { Relevent_lights[Num_relevent_lights[n2]++][n2] = l; } } } break; // hmm. this could probably be more optimal case LT_TUBE: if(Use_GLSL > 1) { if(l->light_ignore_objnum != objnum){ vec3d nearest; float dist_squared, max_dist_squared; vm_vec_dist_squared_to_line(pos,&l->vec,&l->vec2,&nearest,&dist_squared); max_dist_squared = l->radb+rad; max_dist_squared *= max_dist_squared; if ( dist_squared < max_dist_squared ) Relevent_lights[Num_relevent_lights[n2]++][n2] = l; } } else { // all tubes are "unique" light sources for now if((l->affected_objnum >= 0) && (objnum == l->affected_objnum)) Relevent_lights[Num_relevent_lights[n2]++][n2] = l; } break; default: Int3(); // Invalid light type } } return Num_relevent_lights[n2]; } int is_inside( vec3d *min, vec3d *max, vec3d * p0, float rad ) { float *origin = (float *)&p0->xyz.x; float *minB = (float *)min; float *maxB = (float *)max; int i; for (i=0; i<3; i++ ) { if ( origin[i] < minB[i] - rad ) { return 0; } else if (origin[i] > maxB[i] + rad ) { return 0; } } return 1; } int light_filter_push_box( vec3d *min, vec3d *max ) { int i; light *l; if ( Lighting_off ) return 0; int n1,n2; n1 = Num_light_levels-1; n2 = Num_light_levels; Num_light_levels++; Assert( Num_light_levels < MAX_LIGHT_LEVELS ); Num_relevent_lights[n2] = 0; for (i=0; itype ) { case LT_DIRECTIONAL: //Relevent_lights[Num_relevent_lights[n2]++][n2] = l; break; case LT_POINT: { if ( is_inside( min, max, &l->local_vec, l->radb ) ) { Relevent_lights[Num_relevent_lights[n2]++][n2] = l; } } break; case LT_TUBE: if ( is_inside(min, max, &l->local_vec, l->radb) || is_inside(min, max, &l->local_vec2, l->radb) ) { Relevent_lights[Num_relevent_lights[n2]++][n2] = l; } break; default: Int3(); // Invalid light type } } return Num_relevent_lights[n2]; } void light_filter_pop() { if ( Lighting_off ) return; Num_light_levels--; Assert( Num_light_levels > 0 ); } int l_num_points=0, l_num_lights=0; void light_rotate_all() { int i; light *l; if ( Lighting_off ) return; int n = Num_light_levels-1; l = Lights; for (i=0; i= (int)Static_light.size()) ) { return 0; } if (pos) { *pos = Static_light[n]->vec; if ( Lighting_mode != LM_DARKEN ) { vm_vec_scale( pos, -1.0f ); } } return 1; } void light_set_shadow( int state ) { Light_in_shadow = state; } void light_set_all_relevent() { int idx; gr_reset_lighting(); for (idx = 0; idx < (int)Static_light.size(); idx++) gr_set_light( Static_light[idx] ); // for simplicity sake were going to forget about dynamic lights for the moment int n = Num_light_levels-1; for (idx = 0; idx < Num_relevent_lights[n]; idx++ ) gr_set_light( Relevent_lights[idx][n] ); } ubyte light_apply( vec3d *pos, vec3d * norm, float static_light_level ) { int i, idx; float lval; light *l; if (Detail.lighting==0) { // No static light return ubyte(fl2i(static_light_level*255.0f)); } if ( Lighting_off ) return 191; // Factor in ambient light lval = Ambient_light; // Factor in light from suns if there are any if ( !Light_in_shadow ){ // apply all sun lights for (idx = 0; idx < (int)Static_light.size(); idx++) { float ltmp; // calculate light from surface normal ltmp = -vm_vec_dot(&Static_light[idx]->local_vec, norm )*Static_light[idx]->intensity*Reflective_light; // reflective light switch(Lighting_mode) { case LM_BRIGHTEN: if ( ltmp > 0.0f ) lval += ltmp; break; case LM_DARKEN: if ( ltmp > 0.0f ) lval -= ltmp; if ( lval < 0.0f ) lval = 0.0f; break; } } } // At this point, l must be between 0 and 0.75 (0.75-1.0 is for dynamic light only) CLAMP(lval, 0.0f, 0.75f); lval *= static_light_level; int n = Num_light_levels-1; l_num_lights += Num_relevent_lights[n]; l_num_points++; for (i=0; ilocal_vec, pos ); dot = vm_vec_dot(&to_light, norm ); if ( dot > 0.0f ) { dist = vm_vec_mag_squared(&to_light); if ( dist < l->rada_squared ) { lval += l->intensity*dot; } else if ( dist < l->radb_squared ) { // dist from 0 to float nnum = dist - l->rada_squared; float dden = l->radb_squared - l->rada_squared; float ltmp = (1.0f - nnum / dden )*dot*l->intensity; lval += ltmp; } if ( lval > 1.0f ) { return 255; } } } return ubyte(fl2i(lval*255.0f)); } int spec = 0; float static_light_factor = 1.0f; float static_tube_factor = 1.0f; float static_point_factor = 1.0f; double specular_exponent_value = 16.0; void light_apply_specular(ubyte *param_r, ubyte *param_g, ubyte *param_b, vec3d *pos, vec3d * norm, vec3d * cam){ light *l; float rval = 0, gval = 0, bval = 0; int idx; if ( !Cmdline_spec ) { *param_r = 0; *param_g = 0; *param_b = 0; return; } if (Detail.lighting==0) { *param_r = 0; *param_g = 0; *param_b = 0; return; } if ( Lighting_off ) { *param_r = 0; *param_g = 0; *param_b = 0; return; } vec3d V, N; vm_vec_sub(&V, cam,pos); vm_vec_normalize(&V); N = *norm; vm_vec_normalize(&N); // Factor in light from sun if there is one // apply all sun lights for (idx = 0; idx < (int)Static_light.size(); idx++) { float ltmp; vec3d R; vm_vec_sub(&R,&V, &Static_light[idx]->local_vec); vm_vec_normalize(&R); ltmp = (float)pow((double)vm_vec_dot(&R, &N ), specular_exponent_value) * Static_light[idx]->intensity * static_light_factor; // reflective light switch(Lighting_mode) { case LM_BRIGHTEN: if ( ltmp > 0.0f ) { rval += Static_light[idx]->spec_r * ltmp; gval += Static_light[idx]->spec_g * ltmp; bval += Static_light[idx]->spec_b * ltmp; } break; case LM_DARKEN: if ( ltmp > 0.0f ) { rval -= ltmp; if ( rval < 0.0f ) rval = 0.0f; gval -= ltmp; if ( gval < 0.0f ) gval = 0.0f; bval -= ltmp; if ( bval < 0.0f ) bval = 0.0f; } break; } } CLAMP(rval, 0.0f, 0.75f); CLAMP(gval, 0.0f, 0.75f); CLAMP(bval, 0.0f, 0.75f); //dynamic lights int n = Num_light_levels-1; l_num_lights += Num_relevent_lights[n]; l_num_points++; vec3d to_light; float dot, dist; vec3d temp; float factor = 1.0f; for (idx = 0; idx < Num_relevent_lights[n]; idx++) { l = Relevent_lights[idx][n]; dist = -1.0f; switch(l->type){ // point lights case LT_POINT: vm_vec_sub( &to_light, &l->local_vec, pos ); factor = static_point_factor; break; // tube lights case LT_TUBE: if(vm_vec_dist_to_line(pos, &l->local_vec, &l->local_vec2, &temp, &dist) != 0){ continue; } factor = static_tube_factor; vm_vec_sub(&to_light, &temp, pos); dist *= dist; // since we use radius squared break; // others. BAD default: Int3(); } vec3d R; vm_vec_normalize(&to_light); vm_vec_add(&R,&V, &to_light); vm_vec_normalize(&R); dot = (float)pow((double)vm_vec_dot(&R, &N ), specular_exponent_value) * l->intensity * factor; // reflective light if ( dot > 0.0f ) { // indicating that we already calculated the distance (vm_vec_dist_to_line(...) does this for us) if(dist < 0.0f){ dist = vm_vec_mag_squared(&to_light); } if ( dist < l->rada_squared ) { float ratio; ratio = l->intensity*dot*factor; ratio *= 0.25f; rval += l->spec_r*ratio; gval += l->spec_g*ratio; bval += l->spec_b*ratio; } else if ( dist < l->radb_squared ) { float ratio; // dist from 0 to float nnum = dist - l->rada_squared; float dden = l->radb_squared - l->rada_squared; ratio = (1.0f - nnum / dden)*dot*l->intensity*factor; ratio *= 0.25f; rval += l->spec_r*ratio; gval += l->spec_g*ratio; bval += l->spec_b*ratio; } } } CLAMP(rval, 0.0f, 1.0f); CLAMP(gval, 0.0f, 1.0f); CLAMP(bval, 0.0f, 1.0f); *param_r = ubyte(fl2i(rval*254.0f)); *param_g = ubyte(fl2i(gval*254.0f)); *param_b = ubyte(fl2i(bval*254.0f)); } void light_apply_rgb( ubyte *param_r, ubyte *param_g, ubyte *param_b, vec3d *pos, vec3d * norm, float static_light_level ) { int idx; float rval, gval, bval; light *l; if (Detail.lighting==0) { // No static light ubyte lVal = ubyte(fl2i(static_light_level*255.0f)); *param_r = lVal; *param_g = lVal; *param_b = lVal; return; } if ( Lighting_off ) { *param_r = 255; *param_g = 255; *param_b = 255; return; } // Factor in ambient light rval = Ambient_light; gval = Ambient_light; bval = Ambient_light; // Factor in light from sun if there is one if ( !Light_in_shadow ){ // apply all sun lights for (idx = 0; idx < (int)Static_light.size(); idx++) { float ltmp; // calculate light from surface normal ltmp = -vm_vec_dot(&Static_light[idx]->local_vec, norm )*Static_light[idx]->intensity*Reflective_light; // reflective light switch(Lighting_mode) { case LM_BRIGHTEN: if ( ltmp > 0.0f ) { rval += Static_light[idx]->r * ltmp; gval += Static_light[idx]->g * ltmp; bval += Static_light[idx]->b * ltmp; } break; case LM_DARKEN: if ( ltmp > 0.0f ) { rval -= ltmp; if ( rval < 0.0f ) rval = 0.0f; gval -= ltmp; if ( gval < 0.0f ) gval = 0.0f; bval -= ltmp; if ( bval < 0.0f ) bval = 0.0f; } break; } } } // At this point, l must be between 0 and 0.75 (0.75-1.0 is for dynamic light only) CLAMP(rval, 0.0f, 0.75f); CLAMP(gval, 0.0f, 0.75f); CLAMP(bval, 0.0f, 0.75f); rval *= static_light_level; gval *= static_light_level; bval *= static_light_level; int n = Num_light_levels-1; l_num_lights += Num_relevent_lights[n]; l_num_points++; vec3d to_light; float dot, dist; vec3d temp; for (idx = 0; idx < Num_relevent_lights[n]; idx++) { l = Relevent_lights[idx][n]; dist = -1.0f; switch(l->type){ // point lights case LT_POINT: vm_vec_sub( &to_light, &l->local_vec, pos ); break; // tube lights case LT_TUBE: if(vm_vec_dist_to_line(pos, &l->local_vec, &l->local_vec2, &temp, &dist) != 0){ continue; } vm_vec_sub(&to_light, &temp, pos); dist *= dist; // since we use radius squared break; // others. BAD default: Int3(); } dot = vm_vec_dot(&to_light, norm); // dot = 1.0f; if ( dot > 0.0f ) { // indicating that we already calculated the distance (vm_vec_dist_to_line(...) does this for us) if(dist < 0.0f){ dist = vm_vec_mag_squared(&to_light); } if ( dist < l->rada_squared ) { float ratio; ratio = l->intensity*dot; ratio *= 0.25f; rval += l->r*ratio; gval += l->g*ratio; bval += l->b*ratio; } else if ( dist < l->radb_squared ) { float ratio; // dist from 0 to float nnum = dist - l->rada_squared; float dden = l->radb_squared - l->rada_squared; ratio = (1.0f - nnum / dden)*dot*l->intensity; ratio *= 0.25f; rval += l->r*ratio; gval += l->g*ratio; bval += l->b*ratio; } } } float m = rval; if ( gval > m ) m = gval; if ( bval > m ) m = bval; if ( m > 1.0f ) { float im = 1.0f / m; rval *= im; gval *= im; bval *= im; } CLAMP(rval, 0.0f, 1.0f); CLAMP(gval, 0.0f, 1.0f); CLAMP(bval, 0.0f, 1.0f); *param_r = ubyte(fl2i(rval*255.0f)); *param_g = ubyte(fl2i(gval*255.0f)); *param_b = ubyte(fl2i(bval*255.0f)); }