mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-09 04:49:39 -10:00
1280 lines
28 KiB
C++
1280 lines
28 KiB
C++
/*
|
|
* 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"
|
|
#include "model/modelrender.h"
|
|
#include "graphics/gropengllight.h"
|
|
#include "graphics/gropengldraw.h"
|
|
#include "debugconsole/console.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<light*> 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")
|
|
{
|
|
SCP_string arg_str;
|
|
float val_f;
|
|
bool val_b;
|
|
|
|
if (dc_optional_string_either("help", "--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");
|
|
return;
|
|
}
|
|
|
|
if (dc_optional_string_either("status", "--status") || dc_optional_string_either("?", "--?")) {
|
|
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" );
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (dc_optional_string("ambient")) {
|
|
dc_stuff_float(&val_f);
|
|
if ((val_f < 0.0f) || (val_f > 1.0f)) {
|
|
dc_printf(" Error: ambient value must be between 0.0 and 1.0\n");
|
|
} else {
|
|
Ambient_light = val_f;
|
|
}
|
|
|
|
} else if (dc_optional_string("reflect")) {
|
|
dc_stuff_float(&val_f);
|
|
if ( (val_f < 0.0f) || (val_f > 1.0f)) {
|
|
dc_printf(" Error: reflect value mus be between 0.0 and 1.0\n");
|
|
} else {
|
|
Reflective_light = val_f;
|
|
}
|
|
|
|
} else if (dc_optional_string("default")) {
|
|
Lighting_mode = LM_BRIGHTEN;
|
|
Ambient_light = AMBIENT_LIGHT_DEFAULT;
|
|
Reflective_light = REFLECTIVE_LIGHT_DEFAULT;
|
|
Lighting_flag = 0;
|
|
|
|
} else if (dc_optional_string("mode")) {
|
|
dc_stuff_string_white(arg_str);
|
|
if (arg_str == "light") {
|
|
Lighting_mode = LM_BRIGHTEN;
|
|
|
|
} else if (arg_str == "darken") {
|
|
Lighting_mode = LM_DARKEN;
|
|
|
|
} else {
|
|
dc_printf(" Error: unknown light mode: '%s'\n", arg_str.c_str());
|
|
}
|
|
|
|
} else if (dc_optional_string("dynamic")) {
|
|
dc_stuff_boolean(&val_b);
|
|
Lighting_flag = val_b;
|
|
|
|
} else if(dc_maybe_stuff_boolean(&Lighting_off)) {
|
|
Lighting_off = !Lighting_off;
|
|
|
|
} else {
|
|
dc_stuff_string_white(arg_str);
|
|
dc_printf("Error: Unknown argument '%s'\n", arg_str.c_str());
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
case LT_CONE:
|
|
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; i<Num_lights; i++, l++ ) {
|
|
Relevent_lights[Num_relevent_lights[n]++][n] = l;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Makes a list of only the lights that will affect
|
|
* the sphere specified by 'pos' and 'rad' and 'objnum'
|
|
*
|
|
* @param objnum Object number
|
|
* @param pos World position
|
|
* @param rad Radius
|
|
*/
|
|
int light_filter_push( int objnum, vec3d *pos, float rad )
|
|
{
|
|
int i;
|
|
light *l;
|
|
|
|
if ( Lighting_off ) return 0;
|
|
|
|
light_filter_reset();
|
|
|
|
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; i<Num_relevent_lights[n1]; i++ ) {
|
|
l = Relevent_lights[i][n1];
|
|
|
|
switch( l->type ) {
|
|
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;
|
|
|
|
case LT_CONE:
|
|
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; i<Num_relevent_lights[n1]; i++ ) {
|
|
l = Relevent_lights[i][n1];
|
|
|
|
switch( l->type ) {
|
|
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;
|
|
|
|
case LT_CONE:
|
|
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<Num_relevent_lights[n]; i++ ) {
|
|
l = Relevent_lights[i][n];
|
|
light_rotate(l);
|
|
}
|
|
|
|
for (i = 0; i < (int)Static_light.size(); i++) {
|
|
light_rotate(Static_light[i]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return the # of global light sources
|
|
*/
|
|
int light_get_global_count()
|
|
{
|
|
return (int)Static_light.size();
|
|
}
|
|
|
|
/**
|
|
* Fills direction of global light source N in pos.
|
|
*
|
|
* @param pos Position
|
|
* @param n Light source
|
|
*
|
|
* Returns 0 if there is no global light.
|
|
*/
|
|
int light_get_global_dir(vec3d *pos, int n)
|
|
{
|
|
if ( (n < 0) || (n >= (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] );
|
|
|
|
extern bool Deferred_lighting;
|
|
if(Deferred_lighting)
|
|
return;
|
|
|
|
// 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; i<Num_relevent_lights[n]; i++ ) {
|
|
l = Relevent_lights[i][n];
|
|
|
|
vec3d to_light;
|
|
float dot, dist;
|
|
vm_vec_sub( &to_light, &l->local_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;
|
|
|
|
case LT_CONE:
|
|
continue;
|
|
|
|
// 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;
|
|
|
|
case LT_DIRECTIONAL:
|
|
continue;
|
|
|
|
case LT_CONE:
|
|
continue;
|
|
|
|
// 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));
|
|
}
|
|
|
|
void light_add_cone( vec3d * pos, vec3d * dir, float angle, float inner_angle, bool dual_cone, 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_CONE;
|
|
l->vec = *pos;
|
|
l->vec2= *dir;
|
|
l->cone_angle = angle;
|
|
l->cone_inner_angle = inner_angle;
|
|
l->dual_cone = dual_cone;
|
|
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 );
|
|
}
|
|
|
|
bool light_compare_by_type(const light &a, const light &b)
|
|
{
|
|
return a.type < b.type;
|
|
}
|
|
|
|
void scene_lights::addLight(light *light_ptr)
|
|
{
|
|
Assert(light_ptr != NULL);
|
|
|
|
AllLights.push_back(*light_ptr);
|
|
|
|
if ( light_ptr->type == LT_DIRECTIONAL ) {
|
|
StaticLightIndices.push_back(AllLights.size() - 1);
|
|
}
|
|
}
|
|
|
|
void scene_lights::setLightFilter(int objnum, vec3d *pos, float rad)
|
|
{
|
|
size_t i;
|
|
|
|
// clear out current filtered lights
|
|
FilteredLights.clear();
|
|
|
|
for ( i = 0; i < AllLights.size(); ++i ) {
|
|
light& l = AllLights[i];
|
|
|
|
switch ( l.type ) {
|
|
case LT_DIRECTIONAL:
|
|
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 ) {
|
|
FilteredLights.push_back(i);
|
|
}
|
|
}
|
|
} else { // otherwise check all relevant objects
|
|
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 ) {
|
|
FilteredLights.push_back(i);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
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 ) {
|
|
FilteredLights.push_back(i);
|
|
}
|
|
}
|
|
} else {
|
|
// all tubes are "unique" light sources for now
|
|
if ( ( l.affected_objnum >= 0 ) && ( objnum == l.affected_objnum ) ) {
|
|
FilteredLights.push_back(i);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case LT_CONE:
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
light_indexing_info scene_lights::bufferLights()
|
|
{
|
|
size_t i;
|
|
|
|
light_indexing_info light_info;
|
|
|
|
light_info.index_start = 0;
|
|
light_info.num_lights = 0;
|
|
|
|
// make sure that there are lights to bind?
|
|
if ( FilteredLights.size() <= 0 ) {
|
|
return light_info;
|
|
}
|
|
|
|
light_info.index_start = BufferedLights.size();
|
|
|
|
for ( i = 0; i < FilteredLights.size(); ++i ) {
|
|
BufferedLights.push_back(FilteredLights[i]);
|
|
}
|
|
|
|
light_info.num_lights = FilteredLights.size();
|
|
|
|
return light_info;
|
|
}
|
|
|
|
int scene_lights::getNumStaticLights()
|
|
{
|
|
return StaticLightIndices.size();
|
|
}
|
|
|
|
void scene_lights::resetLightState()
|
|
{
|
|
current_light_index = -1;
|
|
current_num_lights = -1;
|
|
}
|
|
|
|
bool scene_lights::setLights(light_indexing_info *info)
|
|
{
|
|
if ( info->index_start == current_light_index && info->num_lights == current_num_lights ) {
|
|
// don't need to set new lights since the ones requested to be set are currently set
|
|
return false;
|
|
}
|
|
|
|
current_light_index = info->index_start;
|
|
current_num_lights = info->num_lights;
|
|
|
|
gr_reset_lighting();
|
|
|
|
gr_set_lighting(true, true);
|
|
|
|
for ( size_t i = 0; i < StaticLightIndices.size(); ++i) {
|
|
int light_index = StaticLightIndices[i];
|
|
|
|
gr_set_light( &AllLights[light_index] );
|
|
}
|
|
|
|
extern bool Deferred_lighting;
|
|
if ( Deferred_lighting ) {
|
|
opengl_change_active_lights(0);
|
|
return false;
|
|
}
|
|
|
|
int index_start = info->index_start;
|
|
int num_lights = info->num_lights;
|
|
|
|
// check if there are any lights to actually set
|
|
if ( num_lights <= 0 || index_start < 0 ) {
|
|
opengl_change_active_lights(0);
|
|
return false;
|
|
}
|
|
|
|
// we definitely shouldn't be exceeding the number of buffered lights
|
|
Assert(index_start + num_lights <= (int)BufferedLights.size());
|
|
|
|
for ( int i = 0; i < num_lights; ++i ) {
|
|
int buffered_light_index = index_start + i;
|
|
int light_index = BufferedLights[buffered_light_index];
|
|
|
|
gr_set_light(&AllLights[light_index]);
|
|
}
|
|
|
|
opengl_change_active_lights(0);
|
|
|
|
return true;
|
|
}
|