mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-07 18:31:26 -10:00
338 lines
9.1 KiB
C++
338 lines
9.1 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/bitarray.h>
|
|
#include "cmdline/cmdline.h"
|
|
#include "globalincs/pstypes.h"
|
|
#include "graphics/2d.h"
|
|
#include "light.h"
|
|
#include "matrix.h"
|
|
#include "render/3d.h"
|
|
|
|
// Structures
|
|
struct gr_light
|
|
{
|
|
vec4 Ambient;
|
|
vec4 Diffuse;
|
|
vec4 Specular;
|
|
|
|
// light position
|
|
vec4 Position;
|
|
|
|
// spotlight direction (for tube lights)
|
|
vec3d SpotDir;
|
|
|
|
float SpotExp, SpotCutOff;
|
|
float ConstantAtten, LinearAtten, QuadraticAtten;
|
|
|
|
int type;
|
|
};
|
|
|
|
// Variables
|
|
SCP_vector<gr_light> gr_lights;
|
|
graphics::model_light gr_light_uniforms[graphics::MAX_UNIFORM_LIGHTS];
|
|
|
|
int Num_active_gr_lights = 0;
|
|
|
|
// OGL defaults
|
|
const float gr_light_color[4] = { 0.8f, 0.8f, 0.8f, 1.0f };
|
|
const float gr_light_zero[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
|
|
const float gr_light_emission[4] = { 0.09f, 0.09f, 0.09f, 1.0f };
|
|
float gr_light_ambient[4] = { 0.47f, 0.47f, 0.47f, 1.0f };
|
|
float gr_user_ambient = 0.0f;
|
|
|
|
void FSLight2GLLight(light* FSLight, gr_light* GLLight) {
|
|
GLLight->Ambient.xyzw.x = 0.0f;
|
|
GLLight->Ambient.xyzw.y = 0.0f;
|
|
GLLight->Ambient.xyzw.z = 0.0f;
|
|
GLLight->Ambient.xyzw.w = 1.0f;
|
|
|
|
GLLight->Diffuse.xyzw.x = FSLight->r * FSLight->intensity;
|
|
GLLight->Diffuse.xyzw.y = FSLight->g * FSLight->intensity;
|
|
GLLight->Diffuse.xyzw.z = FSLight->b * FSLight->intensity;
|
|
GLLight->Diffuse.xyzw.w = 1.0f;
|
|
|
|
GLLight->Specular.xyzw.x = FSLight->spec_r * FSLight->intensity;
|
|
GLLight->Specular.xyzw.y = FSLight->spec_g * FSLight->intensity;
|
|
GLLight->Specular.xyzw.z = FSLight->spec_b * FSLight->intensity;
|
|
GLLight->Specular.xyzw.w = 1.0f;
|
|
|
|
GLLight->type = static_cast<int>(FSLight->type);
|
|
|
|
// GL default values...
|
|
// spot direction
|
|
GLLight->SpotDir.xyz.x = 0.0f;
|
|
GLLight->SpotDir.xyz.y = 0.0f;
|
|
GLLight->SpotDir.xyz.z = -1.0f;
|
|
// spot exponent
|
|
GLLight->SpotExp = Cmdline_ogl_spec * 0.5f;
|
|
// spot cutoff
|
|
GLLight->SpotCutOff = 180.0f; // special value, light in all directions
|
|
// defaults to disable attenuation
|
|
GLLight->ConstantAtten = 1.0f;
|
|
GLLight->LinearAtten = 0.0f;
|
|
GLLight->QuadraticAtten = 0.0f;
|
|
// position
|
|
GLLight->Position.xyzw.x = FSLight->vec.xyz.x;
|
|
GLLight->Position.xyzw.y = FSLight->vec.xyz.y;
|
|
GLLight->Position.xyzw.z = FSLight->vec.xyz.z; // flipped axis for FS2
|
|
GLLight->Position.xyzw.w = 1.0f;
|
|
|
|
|
|
switch (FSLight->type) {
|
|
case Light_Type::Point: {
|
|
// this crap still needs work...
|
|
GLLight->ConstantAtten = 1.0f;
|
|
GLLight->LinearAtten = (1.0f / MAX(FSLight->rada, FSLight->radb)) * 1.25f;
|
|
|
|
GLLight->Specular.xyzw.x *= static_point_factor;
|
|
GLLight->Specular.xyzw.y *= static_point_factor;
|
|
GLLight->Specular.xyzw.z *= static_point_factor;
|
|
|
|
break;
|
|
}
|
|
|
|
case Light_Type::Tube: {
|
|
GLLight->ConstantAtten = 1.0f;
|
|
GLLight->LinearAtten = (1.0f / MAX(FSLight->rada, FSLight->radb)) * 1.25f;
|
|
GLLight->QuadraticAtten = (1.0f / MAX(FSLight->rada_squared, FSLight->radb_squared)) * 1.25f;
|
|
|
|
GLLight->Specular.xyzw.x *= static_tube_factor;
|
|
GLLight->Specular.xyzw.y *= static_tube_factor;
|
|
GLLight->Specular.xyzw.z *= static_tube_factor;
|
|
|
|
GLLight->Position.xyzw.x = FSLight->vec2.xyz.x; // Valathil: Use endpoint of tube as light position
|
|
GLLight->Position.xyzw.y = FSLight->vec2.xyz.y;
|
|
GLLight->Position.xyzw.z = FSLight->vec2.xyz.z;
|
|
GLLight->Position.xyzw.w = 1.0f;
|
|
|
|
// Valathil: When using shaders pass the beam direction (not normalized IMPORTANT for calculation of tube)
|
|
vec3d a;
|
|
vm_vec_sub(&a, &FSLight->vec2, &FSLight->vec);
|
|
GLLight->SpotDir.xyz.x = a.xyz.x;
|
|
GLLight->SpotDir.xyz.y = a.xyz.y;
|
|
GLLight->SpotDir.xyz.z = a.xyz.z;
|
|
GLLight->SpotCutOff = 90.0f; // Valathil: So shader dectects tube light
|
|
break;
|
|
}
|
|
|
|
case Light_Type::Directional: {
|
|
GLLight->Position.xyzw.x = -FSLight->vec.xyz.x;
|
|
GLLight->Position.xyzw.y = -FSLight->vec.xyz.y;
|
|
GLLight->Position.xyzw.z = -FSLight->vec.xyz.z;
|
|
GLLight->Position.xyzw.w = 0.0f; // This is a direction so the w part must be 0
|
|
|
|
GLLight->Specular.xyzw.x *= static_light_factor;
|
|
GLLight->Specular.xyzw.y *= static_light_factor;
|
|
GLLight->Specular.xyzw.z *= static_light_factor;
|
|
|
|
break;
|
|
}
|
|
|
|
case Light_Type::Cone:
|
|
break;
|
|
|
|
default:
|
|
Error(LOCATION, "Unknown light type in FSLight2GLLight. Expected was 0, 1, 2 or 3, we got %i", static_cast<int>(FSLight->type));
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void set_light(int light_num, gr_light* ltp) {
|
|
Assert(light_num < (int)graphics::MAX_UNIFORM_LIGHTS);
|
|
|
|
vm_vec_transform(&gr_light_uniforms[light_num].position, <p->Position, &gr_view_matrix);
|
|
vm_vec_transform(&gr_light_uniforms[light_num].direction, <p->SpotDir, &gr_view_matrix, false);
|
|
|
|
gr_light_uniforms[light_num].diffuse_color = vm_vec4_to_vec3(ltp->Diffuse);
|
|
|
|
gr_light_uniforms[light_num].spec_color = vm_vec4_to_vec3(ltp->Specular);
|
|
|
|
gr_light_uniforms[light_num].light_type = ltp->type;
|
|
|
|
gr_light_uniforms[light_num].attenuation = ltp->LinearAtten;
|
|
}
|
|
|
|
static bool sort_active_lights(const gr_light& la, const gr_light& lb) {
|
|
// directional lights always go first
|
|
if ((la.type != LT_DIRECTIONAL) && (lb.type == LT_DIRECTIONAL)) {
|
|
return false;
|
|
} else if ((la.type == LT_DIRECTIONAL) && (lb.type != LT_DIRECTIONAL)) {
|
|
return true;
|
|
}
|
|
|
|
// tube lights go next, they are generally large and intense
|
|
if ((la.type != LT_TUBE) && (lb.type == LT_TUBE)) {
|
|
return false;
|
|
} else if ((la.type == LT_TUBE) && (lb.type != LT_TUBE)) {
|
|
return true;
|
|
}
|
|
|
|
// everything else is sorted by linear atten (light size)
|
|
// NOTE: smaller atten is larger light radius!
|
|
if (la.LinearAtten > lb.LinearAtten) {
|
|
return false;
|
|
} else if (la.LinearAtten < lb.LinearAtten) {
|
|
return true;
|
|
}
|
|
|
|
// as one extra check, if we're still here, go with overall brightness of light
|
|
|
|
float la_value = la.Diffuse.xyzw.x + la.Diffuse.xyzw.y + la.Diffuse.xyzw.z;
|
|
float lb_value = lb.Diffuse.xyzw.x + lb.Diffuse.xyzw.y + lb.Diffuse.xyzw.z;
|
|
|
|
if (la_value < lb_value) {
|
|
return false;
|
|
} else if (la_value > lb_value) {
|
|
return true;
|
|
}
|
|
|
|
// the two are equal
|
|
return false;
|
|
}
|
|
|
|
static void pre_render_init_lights() {
|
|
// sort the lights to try and get the most visible lights on the first pass
|
|
std::sort(gr_lights.begin(), gr_lights.end(), sort_active_lights);
|
|
}
|
|
|
|
void gr_set_light(light* fs_light) {
|
|
// init the light
|
|
gr_light grl;
|
|
FSLight2GLLight(fs_light, &grl);
|
|
gr_lights.push_back(grl);
|
|
Num_active_gr_lights++;
|
|
}
|
|
|
|
void gr_set_center_alpha(int type) {
|
|
if (!type) {
|
|
return;
|
|
}
|
|
|
|
gr_light glight;
|
|
|
|
vec3d dir;
|
|
vm_vec_sub(&dir, &Eye_position, &Object_position);
|
|
vm_vec_normalize(&dir);
|
|
|
|
if (type == 1) {
|
|
glight.Diffuse.xyzw.x = 0.0f;
|
|
glight.Diffuse.xyzw.y = 0.0f;
|
|
glight.Diffuse.xyzw.z = 0.0f;
|
|
glight.Ambient.xyzw.x = gr_screen.current_alpha;
|
|
glight.Ambient.xyzw.y = gr_screen.current_alpha;
|
|
glight.Ambient.xyzw.z = gr_screen.current_alpha;
|
|
}
|
|
else {
|
|
glight.Diffuse.xyzw.x = gr_screen.current_alpha;
|
|
glight.Diffuse.xyzw.y = gr_screen.current_alpha;
|
|
glight.Diffuse.xyzw.z = gr_screen.current_alpha;
|
|
glight.Ambient.xyzw.x = 0.0f;
|
|
glight.Ambient.xyzw.y = 0.0f;
|
|
glight.Ambient.xyzw.z = 0.0f;
|
|
}
|
|
glight.type = type;
|
|
|
|
glight.Specular.xyzw.x = 0.0f;
|
|
glight.Specular.xyzw.y = 0.0f;
|
|
glight.Specular.xyzw.z = 0.0f;
|
|
glight.Specular.xyzw.w = 0.0f;
|
|
|
|
glight.Ambient.xyzw.w = 1.0f;
|
|
glight.Diffuse.xyzw.w = 1.0f;
|
|
|
|
glight.Position.xyzw.x = -dir.xyz.x;
|
|
glight.Position.xyzw.y = -dir.xyz.y;
|
|
glight.Position.xyzw.z = -dir.xyz.z;
|
|
glight.Position.xyzw.w = 0.0f;
|
|
|
|
// defaults
|
|
glight.SpotDir.xyz.x = 0.0f;
|
|
glight.SpotDir.xyz.y = 0.0f;
|
|
glight.SpotDir.xyz.z = -1.0f;
|
|
glight.SpotExp = Cmdline_ogl_spec * 0.5f;
|
|
glight.SpotCutOff = 180.0f;
|
|
glight.ConstantAtten = 1.0f;
|
|
glight.LinearAtten = 0.0f;
|
|
glight.QuadraticAtten = 0.0f;
|
|
|
|
// first light
|
|
gr_lights.push_back(glight);
|
|
|
|
// second light
|
|
glight.Position.xyzw.x = dir.xyz.x;
|
|
glight.Position.xyzw.y = dir.xyz.y;
|
|
glight.Position.xyzw.z = dir.xyz.z;
|
|
|
|
gr_lights.push_back(glight);
|
|
}
|
|
|
|
void gr_reset_lighting() {
|
|
gr_lights.clear();
|
|
Num_active_gr_lights = 0;
|
|
}
|
|
|
|
void gr_calculate_ambient_factor(int ambient_factor) {
|
|
gr_user_ambient = (float) ((ambient_factor * 2) - 255) / 255.0f;
|
|
}
|
|
|
|
void gr_light_shutdown() {
|
|
gr_lights.clear();
|
|
}
|
|
|
|
void gr_light_init() {
|
|
gr_calculate_ambient_factor();
|
|
|
|
// allocate memory for enabled lights
|
|
gr_lights.reserve(1024);
|
|
}
|
|
|
|
void gr_set_lighting(bool set, bool state) {
|
|
if (!state) {
|
|
return;
|
|
}
|
|
|
|
//Valathil: Sort lights by priority
|
|
extern bool Deferred_lighting;
|
|
if (!Deferred_lighting) {
|
|
pre_render_init_lights();
|
|
}
|
|
|
|
int i = 0;
|
|
|
|
for (i = 0; i < (int)graphics::MAX_UNIFORM_LIGHTS; i++) {
|
|
if (i >= Num_active_gr_lights) {
|
|
break;
|
|
}
|
|
|
|
if (gr_lights[i].type != LT_DIRECTIONAL)
|
|
continue;
|
|
|
|
set_light(i, &gr_lights[i]);
|
|
}
|
|
|
|
gr_light zero;
|
|
memset(&zero, 0, sizeof(gr_light));
|
|
zero.Position.xyzw.x = 1.0f;
|
|
|
|
// make sure that we turn off any lights that we aren't using right now
|
|
for (; i < (int)graphics::MAX_UNIFORM_LIGHTS; i++) {
|
|
set_light(i, &zero);
|
|
}
|
|
}
|
|
|
|
void gr_set_ambient_light(int red, int green, int blue) {
|
|
gr_light_ambient[0] = i2fl(red) / 255.0f;
|
|
gr_light_ambient[1] = i2fl(green) / 255.0f;
|
|
gr_light_ambient[2] = i2fl(blue) / 255.0f;
|
|
gr_light_ambient[3] = 1.0f;
|
|
|
|
gr_calculate_ambient_factor();
|
|
}
|