mirror of
https://github.com/w23/xash3d-fwgs
synced 2024-12-15 13:41:33 +01:00
5e0a0765ce
The `.editorconfig` file in this repo is configured to trim all trailing whitespace regardless of whether the line is modified. Trims all trailing whitespace in the repository to make the codebase easier to work with in editors that respect `.editorconfig`. `git blame` becomes less useful on these lines but it already isn't very useful. Commands: ``` find . -type f -name '*.h' -exec sed --in-place 's/[[:space:]]\+$//' {} \+ find . -type f -name '*.c' -exec sed --in-place 's/[[:space:]]\+$//' {} \+ ```
497 lines
12 KiB
C
497 lines
12 KiB
C
/*
|
|
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 "gl_local.h"
|
|
#include "pm_local.h"
|
|
#include "studio.h"
|
|
#include "xash3d_mathlib.h"
|
|
#include "ref_params.h"
|
|
|
|
/*
|
|
=============================================================================
|
|
|
|
DYNAMIC LIGHTS
|
|
|
|
=============================================================================
|
|
*/
|
|
/*
|
|
==================
|
|
CL_RunLightStyles
|
|
|
|
==================
|
|
*/
|
|
void CL_RunLightStyles( void )
|
|
{
|
|
int i, k, flight, clight;
|
|
float l, lerpfrac, backlerp;
|
|
float frametime = (gpGlobals->time - gpGlobals->oldtime);
|
|
float scale;
|
|
lightstyle_t *ls;
|
|
|
|
if( !WORLDMODEL ) return;
|
|
|
|
scale = r_lighting_modulate->value;
|
|
|
|
// light animations
|
|
// 'm' is normal light, 'a' is no light, 'z' is double bright
|
|
for( i = 0; i < MAX_LIGHTSTYLES; i++ )
|
|
{
|
|
ls = gEngfuncs.GetLightStyle( i );
|
|
if( !WORLDMODEL->lightdata )
|
|
{
|
|
tr.lightstylevalue[i] = 256 * 256;
|
|
continue;
|
|
}
|
|
|
|
if( !ENGINE_GET_PARM( PARAM_GAMEPAUSED ) && 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 || 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;
|
|
|
|
RI.currententity = gEngfuncs.GetEntityByIndex( 0 );
|
|
RI.currentmodel = RI.currententity->model;
|
|
|
|
for( i = 0; i < MAX_DLIGHTS; i++, l++ )
|
|
{
|
|
l = gEngfuncs.GetDynamicLight( i );
|
|
|
|
if( l->die < gpGlobals->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; i < MAX_DLIGHTS; i++ )
|
|
{
|
|
l = gEngfuncs.GetDynamicLight( i );
|
|
|
|
if( l->die < gpGlobals->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 = gEngfuncs.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 += gEngfuncs.LightToTexGamma( lm->r ) * scale;
|
|
cv->g += gEngfuncs.LightToTexGamma( lm->g ) * scale;
|
|
cv->b += gEngfuncs.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( WORLDMODEL && 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 = MAX_PHYSENTS;
|
|
|
|
// check all the bsp-models
|
|
for( i = 0; i < maxEnts; i++ )
|
|
{
|
|
physent_t *pe = gEngfuncs.EV_GetPhysent( i );
|
|
vec3_t offset, start_l, end_l;
|
|
mnode_t *pnodes;
|
|
matrix4x4 matrix;
|
|
|
|
if( !pe )
|
|
break;
|
|
|
|
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 );
|
|
}
|