490 lines
12 KiB
490 lines
12 KiB
/* |
|
gl_rlight.c - dynamic and static lights |
|
Copyright (C) 2010 Uncle Mike |
|
|
|
This program is free software: you can redistribute it and/or modify |
|
it under the terms of the GNU General Public License as published by |
|
the Free Software Foundation, either version 3 of the License, or |
|
(at your option) any later version. |
|
|
|
This program is distributed in the hope that it will be useful, |
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
GNU General Public License for more details. |
|
*/ |
|
|
|
#include "common.h" |
|
#include "client.h" |
|
#include "mathlib.h" |
|
#include "gl_local.h" |
|
#include "pm_local.h" |
|
#include "studio.h" |
|
|
|
/* |
|
============================================================================= |
|
|
|
DYNAMIC LIGHTS |
|
|
|
============================================================================= |
|
*/ |
|
/* |
|
================== |
|
CL_RunLightStyles |
|
|
|
================== |
|
*/ |
|
void CL_RunLightStyles( void ) |
|
{ |
|
int i, k, flight, clight; |
|
float l, lerpfrac, backlerp; |
|
float frametime = (cl.time - cl.oldtime); |
|
float scale; |
|
lightstyle_t *ls; |
|
|
|
if( !cl.worldmodel ) return; |
|
|
|
scale = r_lighting_modulate->value; |
|
|
|
// light animations |
|
// 'm' is normal light, 'a' is no light, 'z' is double bright |
|
for( i = 0, ls = cl.lightstyles; i < MAX_LIGHTSTYLES; i++, ls++ ) |
|
{ |
|
if( !cl.worldmodel->lightdata ) |
|
{ |
|
tr.lightstylevalue[i] = 256 * 256; |
|
continue; |
|
} |
|
|
|
if( !cl.paused && frametime <= 0.1f ) |
|
ls->time += frametime; // evaluate local time |
|
|
|
flight = (int)Q_floor( ls->time * 10 ); |
|
clight = (int)Q_ceil( ls->time * 10 ); |
|
lerpfrac = ( ls->time * 10 ) - flight; |
|
backlerp = 1.0f - lerpfrac; |
|
|
|
if( !ls->length ) |
|
{ |
|
tr.lightstylevalue[i] = 256 * scale; |
|
continue; |
|
} |
|
else if( ls->length == 1 ) |
|
{ |
|
// single length style so don't bother interpolating |
|
tr.lightstylevalue[i] = ls->map[0] * 22 * scale; |
|
continue; |
|
} |
|
else if( !ls->interp || !CVAR_TO_BOOL( cl_lightstyle_lerping )) |
|
{ |
|
tr.lightstylevalue[i] = ls->map[flight%ls->length] * 22 * scale; |
|
continue; |
|
} |
|
|
|
// interpolate animating light |
|
// frame just gone |
|
k = ls->map[flight % ls->length]; |
|
l = (float)( k * 22.0f ) * backlerp; |
|
|
|
// upcoming frame |
|
k = ls->map[clight % ls->length]; |
|
l += (float)( k * 22.0f ) * lerpfrac; |
|
|
|
tr.lightstylevalue[i] = (int)l * scale; |
|
} |
|
} |
|
|
|
/* |
|
============= |
|
R_MarkLights |
|
============= |
|
*/ |
|
void R_MarkLights( dlight_t *light, int bit, mnode_t *node ) |
|
{ |
|
float dist; |
|
msurface_t *surf; |
|
int i; |
|
|
|
if( node->contents < 0 ) |
|
return; |
|
|
|
dist = PlaneDiff( light->origin, node->plane ); |
|
|
|
if( dist > light->radius ) |
|
{ |
|
R_MarkLights( light, bit, node->children[0] ); |
|
return; |
|
} |
|
if( dist < -light->radius ) |
|
{ |
|
R_MarkLights( light, bit, node->children[1] ); |
|
return; |
|
} |
|
|
|
// mark the polygons |
|
surf = RI.currentmodel->surfaces + node->firstsurface; |
|
|
|
for( i = 0; i < node->numsurfaces; i++, surf++ ) |
|
{ |
|
if( !BoundsAndSphereIntersect( surf->info->mins, surf->info->maxs, light->origin, light->radius )) |
|
continue; // no intersection |
|
|
|
if( surf->dlightframe != tr.dlightframecount ) |
|
{ |
|
surf->dlightbits = 0; |
|
surf->dlightframe = tr.dlightframecount; |
|
} |
|
surf->dlightbits |= bit; |
|
} |
|
|
|
R_MarkLights( light, bit, node->children[0] ); |
|
R_MarkLights( light, bit, node->children[1] ); |
|
} |
|
|
|
/* |
|
============= |
|
R_PushDlights |
|
============= |
|
*/ |
|
void R_PushDlights( void ) |
|
{ |
|
dlight_t *l; |
|
int i; |
|
|
|
tr.dlightframecount = tr.framecount; |
|
l = cl_dlights; |
|
|
|
RI.currententity = clgame.entities; |
|
RI.currentmodel = RI.currententity->model; |
|
|
|
for( i = 0; i < MAX_DLIGHTS; i++, l++ ) |
|
{ |
|
if( l->die < cl.time || !l->radius ) |
|
continue; |
|
|
|
if( GL_FrustumCullSphere( &RI.frustum, l->origin, l->radius, 15 )) |
|
continue; |
|
|
|
R_MarkLights( l, 1<<i, RI.currentmodel->nodes ); |
|
} |
|
} |
|
|
|
/* |
|
============= |
|
R_CountDlights |
|
============= |
|
*/ |
|
int R_CountDlights( void ) |
|
{ |
|
dlight_t *l; |
|
int i, numDlights = 0; |
|
|
|
for( i = 0, l = cl_dlights; i < MAX_DLIGHTS; i++, l++ ) |
|
{ |
|
if( l->die < cl.time || !l->radius ) |
|
continue; |
|
|
|
numDlights++; |
|
} |
|
|
|
return numDlights; |
|
} |
|
|
|
/* |
|
============= |
|
R_CountSurfaceDlights |
|
============= |
|
*/ |
|
int R_CountSurfaceDlights( msurface_t *surf ) |
|
{ |
|
int i, numDlights = 0; |
|
|
|
for( i = 0; i < MAX_DLIGHTS; i++ ) |
|
{ |
|
if(!( surf->dlightbits & BIT( i ))) |
|
continue; // not lit by this light |
|
|
|
numDlights++; |
|
} |
|
|
|
return numDlights; |
|
} |
|
|
|
/* |
|
======================================================================= |
|
|
|
AMBIENT LIGHTING |
|
|
|
======================================================================= |
|
*/ |
|
static vec3_t g_trace_lightspot; |
|
static vec3_t g_trace_lightvec; |
|
static float g_trace_fraction; |
|
|
|
/* |
|
================= |
|
R_RecursiveLightPoint |
|
================= |
|
*/ |
|
static qboolean R_RecursiveLightPoint( model_t *model, mnode_t *node, float p1f, float p2f, colorVec *cv, const vec3_t start, const vec3_t end ) |
|
{ |
|
float front, back, frac, midf; |
|
int i, map, side, size; |
|
float ds, dt, s, t; |
|
int sample_size; |
|
color24 *lm, *dm; |
|
mextrasurf_t *info; |
|
msurface_t *surf; |
|
mtexinfo_t *tex; |
|
matrix3x4 tbn; |
|
vec3_t mid; |
|
|
|
// didn't hit anything |
|
if( !node || node->contents < 0 ) |
|
{ |
|
cv->r = cv->g = cv->b = cv->a = 0; |
|
return false; |
|
} |
|
|
|
// calculate mid point |
|
front = PlaneDiff( start, node->plane ); |
|
back = PlaneDiff( end, node->plane ); |
|
|
|
side = front < 0; |
|
if(( back < 0 ) == side ) |
|
return R_RecursiveLightPoint( model, node->children[side], p1f, p2f, cv, start, end ); |
|
|
|
frac = front / ( front - back ); |
|
|
|
VectorLerp( start, frac, end, mid ); |
|
midf = p1f + ( p2f - p1f ) * frac; |
|
|
|
// co down front side |
|
if( R_RecursiveLightPoint( model, node->children[side], p1f, midf, cv, start, mid )) |
|
return true; // hit something |
|
|
|
if(( back < 0 ) == side ) |
|
{ |
|
cv->r = cv->g = cv->b = cv->a = 0; |
|
return false; // didn't hit anything |
|
} |
|
|
|
// check for impact on this node |
|
surf = model->surfaces + node->firstsurface; |
|
VectorCopy( mid, g_trace_lightspot ); |
|
|
|
for( i = 0; i < node->numsurfaces; i++, surf++ ) |
|
{ |
|
int smax, tmax; |
|
|
|
tex = surf->texinfo; |
|
info = surf->info; |
|
|
|
if( FBitSet( surf->flags, SURF_DRAWTILED )) |
|
continue; // no lightmaps |
|
|
|
s = DotProduct( mid, info->lmvecs[0] ) + info->lmvecs[0][3]; |
|
t = DotProduct( mid, info->lmvecs[1] ) + info->lmvecs[1][3]; |
|
|
|
if( s < info->lightmapmins[0] || t < info->lightmapmins[1] ) |
|
continue; |
|
|
|
ds = s - info->lightmapmins[0]; |
|
dt = t - info->lightmapmins[1]; |
|
|
|
if ( ds > info->lightextents[0] || dt > info->lightextents[1] ) |
|
continue; |
|
|
|
cv->r = cv->g = cv->b = cv->a = 0; |
|
|
|
if( !surf->samples ) |
|
return true; |
|
|
|
sample_size = Mod_SampleSizeForFace( surf ); |
|
smax = (info->lightextents[0] / sample_size) + 1; |
|
tmax = (info->lightextents[1] / sample_size) + 1; |
|
ds /= sample_size; |
|
dt /= sample_size; |
|
|
|
lm = surf->samples + Q_rint( dt ) * smax + Q_rint( ds ); |
|
g_trace_fraction = midf; |
|
size = smax * tmax; |
|
dm = NULL; |
|
|
|
if( surf->info->deluxemap ) |
|
{ |
|
vec3_t faceNormal; |
|
|
|
if( FBitSet( surf->flags, SURF_PLANEBACK )) |
|
VectorNegate( surf->plane->normal, faceNormal ); |
|
else VectorCopy( surf->plane->normal, faceNormal ); |
|
|
|
// compute face TBN |
|
#if 1 |
|
Vector4Set( tbn[0], surf->info->lmvecs[0][0], surf->info->lmvecs[0][1], surf->info->lmvecs[0][2], 0.0f ); |
|
Vector4Set( tbn[1], -surf->info->lmvecs[1][0], -surf->info->lmvecs[1][1], -surf->info->lmvecs[1][2], 0.0f ); |
|
Vector4Set( tbn[2], faceNormal[0], faceNormal[1], faceNormal[2], 0.0f ); |
|
#else |
|
Vector4Set( tbn[0], surf->info->lmvecs[0][0], -surf->info->lmvecs[1][0], faceNormal[0], 0.0f ); |
|
Vector4Set( tbn[1], surf->info->lmvecs[0][1], -surf->info->lmvecs[1][1], faceNormal[1], 0.0f ); |
|
Vector4Set( tbn[2], surf->info->lmvecs[0][2], -surf->info->lmvecs[1][2], faceNormal[2], 0.0f ); |
|
#endif |
|
VectorNormalize( tbn[0] ); |
|
VectorNormalize( tbn[1] ); |
|
VectorNormalize( tbn[2] ); |
|
dm = surf->info->deluxemap + Q_rint( dt ) * smax + Q_rint( ds ); |
|
} |
|
|
|
for( map = 0; map < MAXLIGHTMAPS && surf->styles[map] != 255; map++ ) |
|
{ |
|
uint scale = tr.lightstylevalue[surf->styles[map]]; |
|
|
|
if( tr.ignore_lightgamma ) |
|
{ |
|
cv->r += lm->r * scale; |
|
cv->g += lm->g * scale; |
|
cv->b += lm->b * scale; |
|
} |
|
else |
|
{ |
|
cv->r += LightToTexGamma( lm->r ) * scale; |
|
cv->g += LightToTexGamma( lm->g ) * scale; |
|
cv->b += LightToTexGamma( lm->b ) * scale; |
|
} |
|
lm += size; // skip to next lightmap |
|
|
|
if( dm != NULL ) |
|
{ |
|
vec3_t srcNormal, lightNormal; |
|
float f = (1.0f / 128.0f); |
|
|
|
VectorSet( srcNormal, ((float)dm->r - 128.0f) * f, ((float)dm->g - 128.0f) * f, ((float)dm->b - 128.0f) * f ); |
|
Matrix3x4_VectorIRotate( tbn, srcNormal, lightNormal ); // turn to world space |
|
VectorScale( lightNormal, (float)scale * -1.0f, lightNormal ); // turn direction from light |
|
VectorAdd( g_trace_lightvec, lightNormal, g_trace_lightvec ); |
|
dm += size; // skip to next deluxmap |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
// go down back side |
|
return R_RecursiveLightPoint( model, node->children[!side], midf, p2f, cv, mid, end ); |
|
} |
|
|
|
/* |
|
================= |
|
R_LightVec |
|
|
|
check bspmodels to get light from |
|
================= |
|
*/ |
|
colorVec R_LightVecInternal( const vec3_t start, const vec3_t end, vec3_t lspot, vec3_t lvec ) |
|
{ |
|
float last_fraction; |
|
int i, maxEnts = 1; |
|
colorVec light, cv; |
|
|
|
if( lspot ) VectorClear( lspot ); |
|
if( lvec ) VectorClear( lvec ); |
|
|
|
if( cl.worldmodel && cl.worldmodel->lightdata ) |
|
{ |
|
light.r = light.g = light.b = light.a = 0; |
|
last_fraction = 1.0f; |
|
|
|
// get light from bmodels too |
|
if( CVAR_TO_BOOL( r_lighting_extended )) |
|
maxEnts = clgame.pmove->numphysent; |
|
|
|
// check all the bsp-models |
|
for( i = 0; i < maxEnts; i++ ) |
|
{ |
|
physent_t *pe = &clgame.pmove->physents[i]; |
|
vec3_t offset, start_l, end_l; |
|
mnode_t *pnodes; |
|
matrix4x4 matrix; |
|
|
|
if( !pe->model || pe->model->type != mod_brush ) |
|
continue; // skip non-bsp models |
|
|
|
pnodes = &pe->model->nodes[pe->model->hulls[0].firstclipnode]; |
|
VectorSubtract( pe->model->hulls[0].clip_mins, vec3_origin, offset ); |
|
VectorAdd( offset, pe->origin, offset ); |
|
VectorSubtract( start, offset, start_l ); |
|
VectorSubtract( end, offset, end_l ); |
|
|
|
// rotate start and end into the models frame of reference |
|
if( !VectorIsNull( pe->angles )) |
|
{ |
|
Matrix4x4_CreateFromEntity( matrix, pe->angles, offset, 1.0f ); |
|
Matrix4x4_VectorITransform( matrix, start, start_l ); |
|
Matrix4x4_VectorITransform( matrix, end, end_l ); |
|
} |
|
|
|
VectorClear( g_trace_lightspot ); |
|
VectorClear( g_trace_lightvec ); |
|
g_trace_fraction = 1.0f; |
|
|
|
if( !R_RecursiveLightPoint( pe->model, pnodes, 0.0f, 1.0f, &cv, start_l, end_l )) |
|
continue; // didn't hit anything |
|
|
|
if( g_trace_fraction < last_fraction ) |
|
{ |
|
if( lspot ) VectorCopy( g_trace_lightspot, lspot ); |
|
if( lvec ) VectorNormalize2( g_trace_lightvec, lvec ); |
|
light.r = Q_min(( cv.r >> 7 ), 255 ); |
|
light.g = Q_min(( cv.g >> 7 ), 255 ); |
|
light.b = Q_min(( cv.b >> 7 ), 255 ); |
|
last_fraction = g_trace_fraction; |
|
|
|
if(( light.r + light.g + light.b ) != 0 ) |
|
break; // we get light now |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
light.r = light.g = light.b = 255; |
|
light.a = 0; |
|
} |
|
|
|
return light; |
|
} |
|
|
|
/* |
|
================= |
|
R_LightVec |
|
|
|
check bspmodels to get light from |
|
================= |
|
*/ |
|
colorVec R_LightVec( const vec3_t start, const vec3_t end, vec3_t lspot, vec3_t lvec ) |
|
{ |
|
colorVec light = R_LightVecInternal( start, end, lspot, lvec ); |
|
|
|
if( CVAR_TO_BOOL( r_lighting_extended ) && lspot != NULL && lvec != NULL ) |
|
{ |
|
// trying to get light from ceiling (but ignore gradient analyze) |
|
if(( light.r + light.g + light.b ) == 0 ) |
|
return R_LightVecInternal( end, start, lspot, lvec ); |
|
} |
|
|
|
return light; |
|
} |
|
|
|
/* |
|
================= |
|
R_LightPoint |
|
|
|
light from floor |
|
================= |
|
*/ |
|
colorVec R_LightPoint( const vec3_t p0 ) |
|
{ |
|
vec3_t p1; |
|
|
|
VectorSet( p1, p0[0], p0[1], p0[2] - 2048.0f ); |
|
|
|
return R_LightVec( p0, p1, NULL, NULL ); |
|
} |