Files
fs2open.github.com/code/graphics/gropengllight.cpp
T
Zacam 4952b70488 Trunk Sync - r10911
There are a lot of changes here that should probably be examined, namely the Debug Console (which had conflicts) and Yarn's "any resolution" mainhalls code (Which also had conflicts) but for the most part this should be fairly clean.

git-svn-id: svn://svn.icculus.org/fs2open/branches/antipodes@10912 387891d4-d844-0410-90c0-e4c51a9137d3
2014-09-11 19:22:13 +10:00

609 lines
16 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.
*
*/
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
#include "globalincs/pstypes.h"
#include "graphics/gropengl.h"
#include "graphics/gropenglextension.h"
#include "graphics/gropengllight.h"
#include "graphics/gropenglstate.h"
#include "graphics/2d.h"
#include "render/3d.h"
#include "cmdline/cmdline.h"
#include "lighting/lighting.h"
// Variables
opengl_light *opengl_lights = NULL;
bool lighting_is_enabled = true;
int Num_active_gl_lights = 0;
int GL_center_alpha = 0;
extern float static_point_factor;
extern float static_light_factor;
extern float static_tube_factor;
extern double specular_exponent_value;
extern float Cmdline_ogl_spec;
GLint GL_max_lights = 0;
// OGL defaults
static const float GL_light_color[4] = { 0.8f, 0.8f, 0.8f, 1.0f };
static const float GL_light_spec[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
static const float GL_light_zero[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
static const float GL_light_emission[4] = { 0.09f, 0.09f, 0.09f, 1.0f };
static const float GL_light_true_zero[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
static float GL_light_ambient[4] = { 0.47f, 0.47f, 0.47f, 1.0f };
void FSLight2GLLight(light *FSLight, opengl_light *GLLight)
{
GLLight->Ambient[0] = 0.0f;
GLLight->Ambient[1] = 0.0f;
GLLight->Ambient[2] = 0.0f;
GLLight->Ambient[3] = 1.0f;
GLLight->Diffuse[0] = FSLight->r * FSLight->intensity;
GLLight->Diffuse[1] = FSLight->g * FSLight->intensity;
GLLight->Diffuse[2] = FSLight->b * FSLight->intensity;
GLLight->Diffuse[3] = 1.0f;
GLLight->Specular[0] = FSLight->spec_r * FSLight->intensity;
GLLight->Specular[1] = FSLight->spec_g * FSLight->intensity;
GLLight->Specular[2] = FSLight->spec_b * FSLight->intensity;
GLLight->Specular[3] = 1.0f;
GLLight->type = FSLight->type;
// GL default values...
// spot direction
GLLight->SpotDir[0] = 0.0f;
GLLight->SpotDir[1] = 0.0f;
GLLight->SpotDir[2] = -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[0] = FSLight->vec.xyz.x;
GLLight->Position[1] = FSLight->vec.xyz.y;
GLLight->Position[2] = FSLight->vec.xyz.z; // flipped axis for FS2
GLLight->Position[3] = 1.0f;
switch (FSLight->type) {
case LT_POINT: {
// this crap still needs work...
GLLight->ConstantAtten = 1.0f;
GLLight->LinearAtten = (1.0f / MAX(FSLight->rada, FSLight->radb)) * 1.25f;
GLLight->Specular[0] *= static_point_factor;
GLLight->Specular[1] *= static_point_factor;
GLLight->Specular[2] *= static_point_factor;
break;
}
case LT_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[0] *= static_tube_factor;
GLLight->Specular[1] *= static_tube_factor;
GLLight->Specular[2] *= static_tube_factor;
GLLight->Position[0] = FSLight->vec2.xyz.x; // Valathil: Use endpoint of tube as light position
GLLight->Position[1] = FSLight->vec2.xyz.y;
GLLight->Position[2] = FSLight->vec2.xyz.z;
GLLight->Position[3] = 1.0f;
if ( Use_GLSL > 1 ) {
// 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[0] = a.xyz.x;
GLLight->SpotDir[1] = a.xyz.y;
GLLight->SpotDir[2] = a.xyz.z;
GLLight->SpotCutOff = 90.0f; // Valathil: So shader dectects tube light
} else {
GLLight->SpotDir[0] = 1.0f; // Valathil: When not using shaders pass a fake spotdir
GLLight->SpotDir[1] = 0.0f;
GLLight->SpotDir[2] = 0.0f;
GLLight->SpotCutOff = 180.0f; // Valathil: Should be a point light not a spot; using tube only for the light sorting
}
break;
}
case LT_DIRECTIONAL: {
GLLight->Position[0] = -FSLight->vec.xyz.x;
GLLight->Position[1] = -FSLight->vec.xyz.y;
GLLight->Position[2] = -FSLight->vec.xyz.z;
GLLight->Position[3] = 0.0f; // Directional lights in OpenGL have w set to 0
GLLight->Specular[0] *= static_light_factor;
GLLight->Specular[1] *= static_light_factor;
GLLight->Specular[2] *= static_light_factor;
break;
}
default:
Int3();
break;
}
}
extern float Interp_light;
void opengl_set_light(int light_num, opengl_light *ltp)
{
Assert(light_num < GL_max_lights);
GLfloat diffuse[4];
memcpy(diffuse, ltp->Diffuse, sizeof(GLfloat) * 4);
if ( (ltp->type == LT_DIRECTIONAL) && (Interp_light < 1.0f) ) {
diffuse[0] *= Interp_light;
diffuse[1] *= Interp_light;
diffuse[2] *= Interp_light;
}
glLightfv(GL_LIGHT0+light_num, GL_POSITION, ltp->Position);
glLightfv(GL_LIGHT0+light_num, GL_AMBIENT, ltp->Ambient);
glLightfv(GL_LIGHT0+light_num, GL_DIFFUSE, diffuse);
glLightfv(GL_LIGHT0+light_num, GL_SPECULAR, ltp->Specular);
glLightfv(GL_LIGHT0+light_num, GL_SPOT_DIRECTION, ltp->SpotDir);
//glLightf(GL_LIGHT0+light_num, GL_CONSTANT_ATTENUATION, ltp->ConstantAtten); Default is 1.0 and we only use 1.0 - Valathil
glLightf(GL_LIGHT0+light_num, GL_LINEAR_ATTENUATION, ltp->LinearAtten);
//glLightf(GL_LIGHT0+light_num, GL_QUADRATIC_ATTENUATION, ltp->QuadraticAtten); Default is 0.0 and we only use 0.0 - Valathil
glLightf(GL_LIGHT0+light_num, GL_SPOT_EXPONENT, ltp->SpotExp);
glLightf(GL_LIGHT0+light_num, GL_SPOT_CUTOFF, ltp->SpotCutOff);
}
#include <globalincs/systemvars.h>
int opengl_sort_active_lights(const void *a, const void *b)
{
opengl_light *la, *lb;
la = (opengl_light *) a;
lb = (opengl_light *) b;
// directional lights always go first
if ( (la->type != LT_DIRECTIONAL) && (lb->type == LT_DIRECTIONAL) )
return 1;
else if ( (la->type == LT_DIRECTIONAL) && (lb->type != LT_DIRECTIONAL) )
return -1;
// tube lights go next, they are generally large and intense
if ( (la->type != LT_TUBE) && (lb->type == LT_TUBE) )
return 1;
else if ( (la->type == LT_TUBE) && (lb->type != LT_TUBE) )
return -1;
// everything else is sorted by linear atten (light size)
// NOTE: smaller atten is larger light radius!
if ( la->LinearAtten > lb->LinearAtten )
return 1;
else if ( la->LinearAtten < lb->LinearAtten )
return -1;
// as one extra check, if we're still here, go with overall brightness of light
float la_value = la->Diffuse[0] + la->Diffuse[1] + la->Diffuse[2];
float lb_value = lb->Diffuse[0] + lb->Diffuse[1] + lb->Diffuse[2];
if ( la_value < lb_value )
return 1;
else if ( la_value > lb_value )
return -1;
// the two are equal
return 0;
}
void opengl_pre_render_init_lights()
{
// sort the lights to try and get the most visible lights on the first pass
qsort(opengl_lights, Num_active_gl_lights, sizeof(opengl_light), opengl_sort_active_lights);
}
static GLdouble eyex, eyey, eyez;
static GLdouble vmatrix[16];
static vec3d last_view_pos;
static matrix last_view_orient;
static bool use_last_view = false;
void opengl_change_active_lights(int pos, int d_offset)
{
int i, offset;
if ( !lighting_is_enabled ) {
return;
}
Assert( d_offset < GL_max_lights );
offset = (pos * GL_max_lights) + d_offset;
glPushMatrix();
if ( !memcmp(&Eye_position, &last_view_pos, sizeof(vec3d)) && !memcmp(&Eye_matrix, &last_view_orient, sizeof(matrix)) ) {
use_last_view = true;
} else {
memcpy(&last_view_pos, &Eye_position, sizeof(vec3d));
memcpy(&last_view_orient, &Eye_matrix, sizeof(matrix));
use_last_view = false;
}
if ( !use_last_view ) {
// should already be normalized
eyex = (GLdouble)Eye_position.xyz.x;
eyey = (GLdouble)Eye_position.xyz.y;
eyez = -(GLdouble)Eye_position.xyz.z;
// should already be normalized
GLdouble fwdx = (GLdouble)Eye_matrix.vec.fvec.xyz.x;
GLdouble fwdy = (GLdouble)Eye_matrix.vec.fvec.xyz.y;
GLdouble fwdz = -(GLdouble)Eye_matrix.vec.fvec.xyz.z;
// should already be normalized
GLdouble upx = (GLdouble)Eye_matrix.vec.uvec.xyz.x;
GLdouble upy = (GLdouble)Eye_matrix.vec.uvec.xyz.y;
GLdouble upz = -(GLdouble)Eye_matrix.vec.uvec.xyz.z;
GLdouble mag;
// setup Side vector (crossprod of forward and up vectors)
GLdouble Sx = (fwdy * upz) - (fwdz * upy);
GLdouble Sy = (fwdz * upx) - (fwdx * upz);
GLdouble Sz = (fwdx * upy) - (fwdy * upx);
// normalize Side
mag = 1.0 / sqrt( (Sx*Sx) + (Sy*Sy) + (Sz*Sz) );
Sx *= mag;
Sy *= mag;
Sz *= mag;
// setup Up vector (crossprod of s and forward vectors)
GLdouble Ux = (Sy * fwdz) - (Sz * fwdy);
GLdouble Uy = (Sz * fwdx) - (Sx * fwdz);
GLdouble Uz = (Sx * fwdy) - (Sy * fwdx);
// normalize Up
mag = 1.0 / sqrt( (Ux*Ux) + (Uy*Uy) + (Uz*Uz) );
Ux *= mag;
Uy *= mag;
Uz *= mag;
// store the result in our matrix
memset( vmatrix, 0, sizeof(GLdouble) * 16 );
vmatrix[0] = Sx; vmatrix[1] = Ux; vmatrix[2] = -fwdx;
vmatrix[4] = Sy; vmatrix[5] = Uy; vmatrix[6] = -fwdy;
vmatrix[8] = Sz; vmatrix[9] = Uz; vmatrix[10] = -fwdz;
vmatrix[15] = 1.0;
}
glLoadMatrixd(vmatrix);
glTranslated(-eyex, -eyey, -eyez);
glScalef(1.0f, 1.0f, -1.0f);
//Valathil: Sort lights by priority
opengl_pre_render_init_lights();
for (i = 0; i < GL_max_lights; i++) {
if ( (offset + i) >= Num_active_gl_lights ) {
break;
}
if (opengl_lights[offset+i].occupied) {
opengl_set_light(i, &opengl_lights[offset+i]);
GL_state.Light(i, GL_TRUE);
}
}
opengl_light zero;
memset(&zero,0,sizeof(opengl_light));
zero.Position[0] = 1.0f;
// make sure that we turn off any lights that we aren't using right now
for ( ; i < GL_max_lights; i++) {
GL_state.Light(i, GL_FALSE);
opengl_set_light(i, &zero);
}
glPopMatrix();
}
int gr_opengl_make_light(light *fs_light, int idx, int priority)
{
return idx;
}
void gr_opengl_modify_light(light *fs_light, int idx, int priority)
{
}
void gr_opengl_destroy_light(int idx)
{
}
void gr_opengl_set_light(light *fs_light)
{
if (Cmdline_nohtl)
return;
if (Num_active_gl_lights >= MAX_LIGHTS)
return;
//if (fs_light->type == LT_POINT)
// return;
// init the light
FSLight2GLLight(fs_light, &opengl_lights[Num_active_gl_lights]);
opengl_lights[Num_active_gl_lights++].occupied = true;
}
// this sets up a light to be pointinf from the eye to the object,
// the point being to make the object ether center or edge alphaed with THL
// this effect is used mostly on shockwave models
// -1 == edge
// 1 == center
// 0 == none
// should be called after lighting has been set up,
// currently not designed for use with lit models
void gr_opengl_center_alpha(int type)
{
GL_center_alpha = type;
}
void gr_opengl_set_center_alpha(int type)
{
if (!type || Cmdline_nohtl)
return;
if(Num_active_gl_lights == MAX_LIGHTS)
return;
opengl_light glight;
vec3d dir;
vm_vec_sub(&dir, &Eye_position, &Object_position);
vm_vec_normalize(&dir);
if (type == 1) {
glight.Diffuse[0] = 0.0f;
glight.Diffuse[1] = 0.0f;
glight.Diffuse[2] = 0.0f;
glight.Ambient[0] = gr_screen.current_alpha;
glight.Ambient[1] = gr_screen.current_alpha;
glight.Ambient[2] = gr_screen.current_alpha;
} else {
glight.Diffuse[0] = gr_screen.current_alpha;
glight.Diffuse[1] = gr_screen.current_alpha;
glight.Diffuse[2] = gr_screen.current_alpha;
glight.Ambient[0] = 0.0f;
glight.Ambient[1] = 0.0f;
glight.Ambient[2] = 0.0f;
}
glight.type = type;
glight.Specular[0] = 0.0f;
glight.Specular[1] = 0.0f;
glight.Specular[2] = 0.0f;
glight.Specular[3] = 0.0f;
glight.Ambient[3] = 1.0f;
glight.Diffuse[3] = 1.0f;
glight.Position[0] = -dir.xyz.x;
glight.Position[1] = -dir.xyz.y;
glight.Position[2] = -dir.xyz.z;
glight.Position[3] = 0.0f;
// defaults
glight.SpotDir[0] = 0.0f;
glight.SpotDir[1] = 0.0f;
glight.SpotDir[2] = -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;
glight.occupied = false;
// first light
memcpy( &opengl_lights[Num_active_gl_lights], &glight, sizeof(opengl_light) );
opengl_lights[Num_active_gl_lights++].occupied = true;
// second light
glight.Position[0] = dir.xyz.x;
glight.Position[1] = dir.xyz.y;
glight.Position[2] = dir.xyz.z;
memcpy( &opengl_lights[Num_active_gl_lights], &glight, sizeof(opengl_light) );
opengl_lights[Num_active_gl_lights++].occupied = true;
// reset center alpha
GL_center_alpha = 0;
}
void gr_opengl_reset_lighting()
{
int i;
if (Cmdline_nohtl)
return;
// memset( opengl_lights, 0, sizeof(opengl_light) * MAX_LIGHTS );
for (i = 0; i < GL_max_lights; i++) {
GL_state.Light(i, GL_FALSE);
}
Num_active_gl_lights = 0;
GL_center_alpha = 0;
}
void opengl_calculate_ambient_factor()
{
float amb_user = 0.0f;
// assuming that the default is "128", just skip this if not a user setting
if (Cmdline_ambient_factor == 128)
return;
amb_user = (float)((Cmdline_ambient_factor * 2) - 255) / 255.0f;
// set the ambient light
GL_light_ambient[0] += amb_user;
GL_light_ambient[1] += amb_user;
GL_light_ambient[2] += amb_user;
CLAMP( GL_light_ambient[0], 0.02f, 1.0f );
CLAMP( GL_light_ambient[1], 0.02f, 1.0f );
CLAMP( GL_light_ambient[2], 0.02f, 1.0f );
GL_light_ambient[3] = 1.0f;
}
void opengl_light_shutdown()
{
if (opengl_lights != NULL) {
vm_free(opengl_lights);
opengl_lights = NULL;
}
}
void opengl_light_init()
{
opengl_calculate_ambient_factor();
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 0);
glMaterialf(GL_FRONT, GL_SHININESS, Cmdline_ogl_spec /*80.0f*/ );
// more realistic lighting model
glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 1);
glGetIntegerv(GL_MAX_LIGHTS, &GL_max_lights); // Get the max number of lights supported
// allocate memory for enabled lights
Verify(GL_max_lights > 0);
if ( opengl_lights == NULL )
opengl_lights = (opengl_light *) vm_malloc_q(MAX_LIGHTS * sizeof(opengl_light));
if (opengl_lights == NULL)
Error( LOCATION, "Unable to allocate memory for lights!\n");
memset( opengl_lights, 0, MAX_LIGHTS * sizeof(opengl_light) );
}
extern int Cmdline_no_emissive;
bool ambient_state = false;
bool emission_state = false;
bool specular_state = false;
void opengl_default_light_settings(int ambient, int emission, int specular)
{
if (!lighting_is_enabled)
return;
if (ambient) {
if (!ambient_state) {
glMaterialfv( GL_FRONT, GL_DIFFUSE, GL_light_color );
glMaterialfv( GL_FRONT, GL_AMBIENT, GL_light_ambient );
ambient_state = true;
}
} else {
if (ambient_state) {
if (GL_center_alpha) {
glMaterialfv( GL_FRONT, GL_AMBIENT_AND_DIFFUSE, GL_light_true_zero );
} else {
glMaterialfv( GL_FRONT, GL_AMBIENT_AND_DIFFUSE, GL_light_zero );
}
ambient_state = false;
}
}
if (emission && !Cmdline_no_emissive) {
// emissive light is just a general glow but without it things are *terribly* dark if there is no light on them
if (!emission_state) {
glMaterialfv( GL_FRONT, GL_EMISSION, GL_light_emission );
emission_state = true;
}
} else {
if (emission_state) {
glMaterialfv( GL_FRONT, GL_EMISSION, GL_light_zero );
emission_state = false;
}
}
if (specular) {
if (!specular_state) {
glMaterialfv( GL_FRONT, GL_SPECULAR, GL_light_spec );
specular_state = true;
}
} else {
if (specular_state) {
glMaterialfv( GL_FRONT, GL_SPECULAR, GL_light_zero );
specular_state = false;
}
}
}
void gr_opengl_set_lighting(bool set, bool state)
{
if (Cmdline_nohtl) {
return;
}
lighting_is_enabled = set;
opengl_default_light_settings();
if ( (gr_screen.current_alphablend_mode == GR_ALPHABLEND_FILTER) && !set ) {
float amb[4] = { gr_screen.current_alpha, gr_screen.current_alpha, gr_screen.current_alpha, gr_screen.current_alpha };
glLightModelfv( GL_LIGHT_MODEL_AMBIENT, amb );
} else {
glLightModelfv( GL_LIGHT_MODEL_AMBIENT, GL_light_ambient );
}
for (int i = 0; i < GL_max_lights; i++) {
GL_state.Light(i, GL_FALSE);
}
GL_state.Lighting( (state) ? GL_TRUE : GL_FALSE );
}
void gr_opengl_set_ambient_light(int red, int green, int blue)
{
GL_light_ambient[0] = i2fl(red)/255.0f;
GL_light_ambient[1] = i2fl(green)/255.0f;
GL_light_ambient[2] = i2fl(blue)/255.0f;
GL_light_ambient[3] = 1.0f;
opengl_calculate_ambient_factor();
}