mirror of
https://github.com/FWGS/xash3d-fwgs.git
synced 2026-08-05 03:24:56 +08:00
697 lines
18 KiB
C
697 lines
18 KiB
C
/*
|
|
ref_light.c - shared light code
|
|
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 "ref_common.h"
|
|
#include "xash3d_mathlib.h"
|
|
#include "enginefeatures.h"
|
|
#include "pm_local.h"
|
|
|
|
CVAR_DEFINE_AUTO( r_dlight_virtual_radius, "3", FCVAR_GLCONFIG, "increase dlight radius virtually by this amount" );
|
|
CVAR_DEFINE_AUTO( r_lighting_extended, "1", FCVAR_GLCONFIG, "allow to get lighting from world and bmodels" );
|
|
|
|
/*
|
|
==================
|
|
CL_RunLightStyles
|
|
|
|
==================
|
|
*/
|
|
void CL_RunLightStyles( lightstyle_t *ls )
|
|
{
|
|
const model_t *world = gp_cl->models[1];
|
|
float frametime = gp_cl->time - gp_cl->oldtime;
|
|
|
|
if( !world )
|
|
return;
|
|
|
|
if( r_fullbright->value || !world->lightdata )
|
|
{
|
|
for( int i = 0; i < MAX_LIGHTSTYLES; i++ )
|
|
g_lightstylevalue[i] = 256 * 256;
|
|
return;
|
|
}
|
|
|
|
// light animations
|
|
// 'm' is normal light, 'a' is no light, 'z' is double bright
|
|
for( int i = 0; i < MAX_LIGHTSTYLES; i++ )
|
|
{
|
|
if( !gp_cl->paused && frametime <= 0.1f )
|
|
ls[i].time += frametime; // evaluate local time
|
|
|
|
if( !ls[i].length )
|
|
{
|
|
g_lightstylevalue[i] = 256;
|
|
continue;
|
|
}
|
|
else if( ls[i].length == 1 )
|
|
{
|
|
// single length style so don't bother interpolating
|
|
g_lightstylevalue[i] = ls[i].map[0] * 22;
|
|
continue;
|
|
}
|
|
|
|
int flight = (int)Q_floor( ls[i].time * 10 );
|
|
|
|
if( !ls[i].interp || !cl_lightstyle_lerping->value )
|
|
{
|
|
g_lightstylevalue[i] = ls[i].map[flight % ls[i].length] * 22;
|
|
continue;
|
|
}
|
|
|
|
int clight = (int)Q_ceil( ls[i].time * 10 );
|
|
float lerpfrac = ( ls[i].time * 10 ) - flight;
|
|
float backlerp = 1.0f - lerpfrac;
|
|
|
|
// interpolate animating light
|
|
// frame just gone
|
|
int k = ls[i].map[flight % ls[i].length];
|
|
float l = (float)( k * 22.0f ) * backlerp;
|
|
|
|
// upcoming frame
|
|
k = ls[i].map[clight % ls[i].length];
|
|
l += (float)( k * 22.0f ) * lerpfrac;
|
|
|
|
g_lightstylevalue[i] = (int)l;
|
|
}
|
|
}
|
|
|
|
/*
|
|
=============
|
|
R_MarkLights
|
|
=============
|
|
*/
|
|
static void R_MarkLights( const dlight_t *light, int bit, const mnode_t *node, model_t *model, int dlightframecount )
|
|
{
|
|
const float virtual_radius = light->radius * Q_max( 1.0f, r_dlight_virtual_radius.value );
|
|
const float maxdist = light->radius * light->radius;
|
|
start:
|
|
if( !node || node->contents < 0 )
|
|
return;
|
|
|
|
float dist = PlaneDiff( light->origin, node->plane );
|
|
|
|
if( dist > virtual_radius )
|
|
{
|
|
node = node_child( node, 0, model );
|
|
goto start;
|
|
}
|
|
|
|
if( dist < -virtual_radius )
|
|
{
|
|
node = node_child( node, 1, model );
|
|
goto start;
|
|
}
|
|
|
|
const float dist_sq = dist * dist;
|
|
|
|
// mark the polygons
|
|
int firstsurface = node_firstsurface( node, model );
|
|
int numsurfaces = node_numsurfaces( node, model );
|
|
|
|
for( int i = 0; i < numsurfaces && dist_sq < maxdist; i++ )
|
|
{
|
|
vec3_t impact;
|
|
float s, t, l;
|
|
msurface_t *surf = &model->surfaces[firstsurface + i];
|
|
const mextrasurf_t *info = surf->info;
|
|
|
|
if( surf->plane->type < 3 )
|
|
{
|
|
VectorCopy( light->origin, impact );
|
|
impact[surf->plane->type] -= dist;
|
|
}
|
|
else VectorMA( light->origin, -dist, surf->plane->normal, impact );
|
|
|
|
// a1ba: the fix was taken from JoeQuake, which traces back to FitzQuake,
|
|
// which attributes it to LadyHavoc (Darkplaces author)
|
|
// clamp center of light to corner and check brightness
|
|
l = DotProduct( impact, info->lmvecs[0] ) + info->lmvecs[0][3] - info->lightmapmins[0];
|
|
s = l + 0.5;
|
|
s = bound( 0, s, info->lightextents[0] );
|
|
s = l - s;
|
|
|
|
l = DotProduct( impact, info->lmvecs[1] ) + info->lmvecs[1][3] - info->lightmapmins[1];
|
|
t = l + 0.5;
|
|
t = bound( 0, t, info->lightextents[1] );
|
|
t = l - t;
|
|
|
|
if( s * s + t * t + dist_sq >= maxdist )
|
|
continue;
|
|
|
|
if( surf->dlightframe != dlightframecount )
|
|
{
|
|
surf->dlightbits = bit;
|
|
surf->dlightframe = dlightframecount;
|
|
}
|
|
else surf->dlightbits |= bit;
|
|
}
|
|
|
|
R_MarkLights( light, bit, node_child( node, 0, model ), model, dlightframecount );
|
|
node = node_child( node, 1, model );
|
|
goto start;
|
|
}
|
|
|
|
/*
|
|
=============
|
|
R_PushDlights
|
|
=============
|
|
*/
|
|
int R_PushDlights( model_t *model, int framecount )
|
|
{
|
|
if( !model )
|
|
return framecount;
|
|
|
|
for( int i = 0; i < MAX_DLIGHTS; i++ )
|
|
{
|
|
const dlight_t *l = &gp_dlights[i];
|
|
|
|
if( l->die < gp_cl->time || !l->radius )
|
|
continue;
|
|
|
|
R_MarkLights( l, 1 << i, model->nodes, model, framecount );
|
|
}
|
|
|
|
return framecount;
|
|
}
|
|
|
|
void R_PushDlightsForBmodel( model_t *model, int framecount, const matrix4x4 object_matrix )
|
|
{
|
|
for( int i = 0; i < MAX_DLIGHTS; i++ )
|
|
{
|
|
dlight_t *l = &gp_dlights[i];
|
|
|
|
if( l->die < gp_cl->time || !l->radius )
|
|
continue;
|
|
|
|
vec3_t oldorigin = Vec3( l->origin );
|
|
|
|
Matrix4x4_VectorITransform( object_matrix, oldorigin, l->origin );
|
|
R_MarkLights( l, 1 << i, model->nodes + model->hulls[0].firstclipnode, model, framecount );
|
|
|
|
VectorCopy( oldorigin, l->origin );
|
|
}
|
|
}
|
|
|
|
/*
|
|
=======================================================================
|
|
|
|
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 )
|
|
{
|
|
start:
|
|
// didn't hit anything
|
|
if( !node || node->contents < 0 )
|
|
{
|
|
cv->r = cv->g = cv->b = cv->a = 0;
|
|
return false;
|
|
}
|
|
|
|
// calculate mid point
|
|
float front = PlaneDiff( start, node->plane );
|
|
float back = PlaneDiff( end, node->plane );
|
|
|
|
int side = front < 0;
|
|
if(( back < 0 ) == side )
|
|
{
|
|
node = node_child( node, side, model );
|
|
goto start;
|
|
}
|
|
|
|
float frac = front / ( front - back );
|
|
|
|
vec3_t mid;
|
|
VectorLerp( start, frac, end, mid );
|
|
|
|
float midf = p1f + ( p2f - p1f ) * frac;
|
|
|
|
// co down front side
|
|
if( R_RecursiveLightPoint( model, node_child( node, side, model ), 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
|
|
int firstsurface = node_firstsurface( node, model );
|
|
int numsurfaces = node_numsurfaces( node, model );
|
|
|
|
VectorCopy( mid, g_trace_lightspot );
|
|
|
|
for( int i = 0; i < numsurfaces; i++ )
|
|
{
|
|
const msurface_t *surf = &model->surfaces[firstsurface + i];
|
|
const mextrasurf_t *info = surf->info;
|
|
|
|
if( FBitSet( surf->flags, SURF_DRAWTILED ))
|
|
continue; // no lightmaps
|
|
|
|
float s = DotProduct( mid, info->lmvecs[0] ) + info->lmvecs[0][3];
|
|
float t = DotProduct( mid, info->lmvecs[1] ) + info->lmvecs[1][3];
|
|
|
|
if( s < info->lightmapmins[0] || t < info->lightmapmins[1] )
|
|
continue;
|
|
|
|
float ds = s - info->lightmapmins[0];
|
|
float 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;
|
|
|
|
int sample_size = gEngfuncs.Mod_SampleSizeForFace( surf );
|
|
int smax = (info->lightextents[0] / sample_size) + 1;
|
|
int tmax = (info->lightextents[1] / sample_size) + 1;
|
|
|
|
ds /= sample_size;
|
|
dt /= sample_size;
|
|
|
|
g_trace_fraction = midf;
|
|
|
|
const color24 *lm = surf->samples + Q_rint( dt ) * smax + Q_rint( ds );
|
|
const color24 *dm = NULL;
|
|
matrix3x4 tbn;
|
|
|
|
if( surf->info->deluxemap )
|
|
{
|
|
vec3_t faceNormal;
|
|
|
|
dm = surf->info->deluxemap + Q_rint( dt ) * smax + Q_rint( ds );
|
|
|
|
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] );
|
|
}
|
|
|
|
int size = smax * tmax;
|
|
|
|
for( int map = 0; map < MAXLIGHTMAPS && surf->styles[map] != 255; map++ )
|
|
{
|
|
uint scale = g_lightstylevalue[surf->styles[map]];
|
|
|
|
cv->r += lm->r * scale;
|
|
cv->g += lm->g * scale;
|
|
cv->b += lm->b * scale;
|
|
|
|
lm += size; // skip to next lightmap
|
|
|
|
if( dm != NULL )
|
|
{
|
|
const float f = (1.0f / 128.0f);
|
|
vec3_t srcNormal =
|
|
{
|
|
((float)dm->r - 128.0f) * f,
|
|
((float)dm->g - 128.0f) * f,
|
|
((float)dm->b - 128.0f) * f,
|
|
};
|
|
vec3_t lightNormal;
|
|
|
|
|
|
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_child( node, !side, model ), midf, p2f, cv, mid, end );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_LightVecInternal
|
|
|
|
check bspmodels to get light from
|
|
=================
|
|
*/
|
|
static colorVec R_LightVecInternal( const vec3_t start, const vec3_t end, vec3_t lspot, vec3_t lvec )
|
|
{
|
|
if( lspot )
|
|
VectorClear( lspot );
|
|
|
|
if( lvec )
|
|
VectorClear( lvec );
|
|
|
|
if( !gp_cl->models[1] || !gp_cl->models[1]->lightdata )
|
|
return (colorVec){ 255, 255, 255, 0 };
|
|
|
|
float last_fraction = 1.0f;
|
|
int max_ents = r_lighting_extended.value ? MAX_PHYSENTS : 1; // get light from bmodels too
|
|
colorVec light = { 0 };
|
|
|
|
// check all the bsp-models
|
|
for( int i = 0; i < max_ents; i++ )
|
|
{
|
|
const physent_t *pe = gEngfuncs.EV_GetPhysent( i );
|
|
|
|
if( !pe )
|
|
break;
|
|
|
|
if( !pe->model || pe->model->type != mod_brush )
|
|
continue; // skip non-bsp models
|
|
|
|
mnode_t *pnodes = &pe->model->nodes[pe->model->hulls[0].firstclipnode];
|
|
vec3_t offset, start_l, end_l;
|
|
|
|
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 matrix;
|
|
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;
|
|
|
|
colorVec cv;
|
|
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 >> 8 ), 255 );
|
|
light.g = Q_min(( cv.g >> 8 ), 255 );
|
|
light.b = Q_min(( cv.b >> 8 ), 255 );
|
|
last_fraction = g_trace_fraction;
|
|
|
|
if(( light.r + light.g + light.b ) != 0 )
|
|
break; // we get light now
|
|
}
|
|
}
|
|
|
|
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( r_lighting_extended.value && 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 = { p0[0], p0[1], p0[2] - 2048.0f };
|
|
|
|
return R_LightVec( p0, p1, NULL, NULL );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_GatherPlayerLight
|
|
|
|
get light level of an entity and set it as player's light level
|
|
=================
|
|
*/
|
|
void R_GatherPlayerLight( cl_entity_t *view )
|
|
{
|
|
colorVec c = R_LightPoint( view->origin );
|
|
|
|
gEngfuncs.SetLocalLightLevel(( c.r + c.g + c.b ) / 3 );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
R_EntityDynamicLight
|
|
|
|
===============
|
|
*/
|
|
void R_EntityDynamicLight( cl_entity_t *ent, alight_t *plight, qboolean draw_world, double time, vec3_t lightspot, vec3_t lightvec )
|
|
{
|
|
if( !plight || !ent )
|
|
return;
|
|
|
|
if( !draw_world || r_fullbright->value || FBitSet( ent->curstate.effects, EF_FULLBRIGHT ))
|
|
{
|
|
plight->shadelight = 0;
|
|
plight->ambientlight = 192;
|
|
|
|
VectorSet( plight->plightvec, 0.0f, 0.0f, -1.0f );
|
|
VectorSet( plight->color, 1.0f, 1.0f, 1.0f );
|
|
return;
|
|
}
|
|
|
|
movevars_t *mv = gp_movevars;
|
|
vec3_t lightDir;
|
|
|
|
// determine plane to get lightvalues from: ceil or floor
|
|
if( FBitSet( ent->curstate.effects, EF_INVLIGHT ))
|
|
VectorSet( lightDir, 0.0f, 0.0f, 1.0f );
|
|
else
|
|
VectorSet( lightDir, 0.0f, 0.0f, -1.0f );
|
|
|
|
vec3_t origin = Vec3( ent->origin );
|
|
|
|
vec3_t vecSrc = { origin[0], origin[1], origin[2] - lightDir[2] * 8.0f };
|
|
vec3_t vecEnd;
|
|
|
|
colorVec light;
|
|
light.r = light.g = light.b = light.a = 0;
|
|
|
|
if(( mv->skycolor[0] + mv->skycolor[1] + mv->skycolor[2] ) != 0 )
|
|
{
|
|
vec3_t skyvec;
|
|
|
|
if( FBitSet( gp_host->features, ENGINE_WRITE_LARGE_COORD ))
|
|
VectorScale( mv->skyvec, 65536.0f, skyvec );
|
|
else
|
|
VectorScale( mv->skyvec, 8192.0f, skyvec );
|
|
|
|
VectorSubtract( origin, skyvec, vecEnd );
|
|
|
|
pmtrace_t trace = gEngfuncs.CL_TraceLine( vecSrc, vecEnd, PM_WORLD_ONLY );
|
|
msurface_t *psurf;
|
|
if( trace.ent > 0 )
|
|
psurf = gEngfuncs.EV_TraceSurface( trace.ent, vecSrc, vecEnd );
|
|
else
|
|
psurf = gEngfuncs.EV_TraceSurface( 0, vecSrc, vecEnd );
|
|
|
|
if(( ent->model->type == mod_studio && FBitSet( ent->model->flags, STUDIO_FORCE_SKYLIGHT ))
|
|
|| ( psurf && FBitSet( psurf->flags, SURF_DRAWSKY )))
|
|
{
|
|
VectorCopy( mv->skyvec, lightDir );
|
|
|
|
light.r = mv->skycolor[0];
|
|
light.g = mv->skycolor[1];
|
|
light.b = mv->skycolor[2];
|
|
}
|
|
}
|
|
|
|
if(( light.r + light.g + light.b ) == 0 )
|
|
{
|
|
VectorScale( lightDir, 2048.0f, vecEnd );
|
|
VectorAdd( vecEnd, vecSrc, vecEnd );
|
|
|
|
light = R_LightVec( vecSrc, vecEnd, lightspot, lightvec );
|
|
|
|
if( VectorIsNull( lightvec ))
|
|
{
|
|
float grad[4];
|
|
colorVec gcolor;
|
|
|
|
vecSrc[0] -= 16.0f;
|
|
vecSrc[1] -= 16.0f;
|
|
vecEnd[0] -= 16.0f;
|
|
vecEnd[1] -= 16.0f;
|
|
|
|
gcolor = R_LightVec( vecSrc, vecEnd, NULL, NULL );
|
|
grad[0] = ( gcolor.r + gcolor.g + gcolor.b ) / 768.0f;
|
|
|
|
vecSrc[0] += 32.0f;
|
|
vecEnd[0] += 32.0f;
|
|
|
|
gcolor = R_LightVec( vecSrc, vecEnd, NULL, NULL );
|
|
grad[1] = ( gcolor.r + gcolor.g + gcolor.b ) / 768.0f;
|
|
|
|
vecSrc[1] += 32.0f;
|
|
vecEnd[1] += 32.0f;
|
|
|
|
gcolor = R_LightVec( vecSrc, vecEnd, NULL, NULL );
|
|
grad[2] = ( gcolor.r + gcolor.g + gcolor.b ) / 768.0f;
|
|
|
|
vecSrc[0] -= 32.0f;
|
|
vecEnd[0] -= 32.0f;
|
|
|
|
gcolor = R_LightVec( vecSrc, vecEnd, NULL, NULL );
|
|
grad[3] = ( gcolor.r + gcolor.g + gcolor.b ) / 768.0f;
|
|
|
|
lightDir[0] = grad[0] - grad[1] - grad[2] + grad[3];
|
|
lightDir[1] = grad[1] + grad[0] - grad[2] - grad[3];
|
|
VectorNormalize( lightDir );
|
|
}
|
|
else
|
|
{
|
|
VectorCopy( lightvec, lightDir );
|
|
}
|
|
}
|
|
|
|
if( ent->curstate.renderfx == kRenderFxLightMultiplier && ent->curstate.iuser4 != 10 )
|
|
{
|
|
light.r *= ent->curstate.iuser4 / 10.0f;
|
|
light.g *= ent->curstate.iuser4 / 10.0f;
|
|
light.b *= ent->curstate.iuser4 / 10.0f;
|
|
}
|
|
|
|
vec3_t finalLight = { light.r, light.g, light.b };
|
|
ent->cvFloorColor = light;
|
|
|
|
float total = Q_max( Q_max( light.r, light.g ), light.b );
|
|
if( total == 0.0f )
|
|
total = 1.0f;
|
|
|
|
// scale lightdir by light intentsity
|
|
VectorScale( lightDir, total, lightDir );
|
|
|
|
for( uint lnum = 0; lnum < MAX_DLIGHTS; lnum++ )
|
|
{
|
|
const dlight_t *dl = &gp_dlights[lnum];
|
|
|
|
if( dl->die < time || !r_dynamic->value )
|
|
continue;
|
|
|
|
vec3_t dist;
|
|
VectorSubtract( ent->origin, dl->origin, dist );
|
|
|
|
float radius = VectorLength( dist );
|
|
float add = ( dl->radius - radius );
|
|
|
|
if( add > 0.0f )
|
|
{
|
|
total += add;
|
|
|
|
if( radius > 1.0f )
|
|
VectorScale( dist, ( add / radius ), dist );
|
|
else
|
|
VectorScale( dist, add, dist );
|
|
|
|
VectorAdd( lightDir, dist, lightDir );
|
|
|
|
finalLight[0] += dl->color.r * ( add / 256.0f );
|
|
finalLight[1] += dl->color.g * ( add / 256.0f );
|
|
finalLight[2] += dl->color.b * ( add / 256.0f );
|
|
}
|
|
}
|
|
|
|
float scale;
|
|
if( ent->model->type == mod_alias )
|
|
scale = 0.9f;
|
|
else if( ent->model->type == mod_studio && FBitSet( ent->model->flags, STUDIO_AMBIENT_LIGHT ))
|
|
scale = 0.6f;
|
|
else
|
|
scale = bound( 0.75f, v_direct->value, 1.0f );
|
|
|
|
VectorScale( lightDir, scale, lightDir );
|
|
|
|
plight->shadelight = VectorLength( lightDir );
|
|
plight->ambientlight = total - plight->shadelight;
|
|
|
|
total = Q_max( Q_max( finalLight[0], finalLight[1] ), finalLight[2] );
|
|
|
|
if( total > 0.0f )
|
|
{
|
|
plight->color[0] = finalLight[0] * ( 1.0f / total );
|
|
plight->color[1] = finalLight[1] * ( 1.0f / total );
|
|
plight->color[2] = finalLight[2] * ( 1.0f / total );
|
|
}
|
|
else
|
|
VectorSet( plight->color, 1.0f, 1.0f, 1.0f );
|
|
|
|
if( plight->ambientlight > 128 )
|
|
plight->ambientlight = 128;
|
|
|
|
if( plight->ambientlight + plight->shadelight > 255 )
|
|
plight->shadelight = 255 - plight->ambientlight;
|
|
|
|
VectorNormalize2( lightDir, plight->plightvec );
|
|
}
|
|
|
|
/*
|
|
================
|
|
R_SetCacheState
|
|
================
|
|
*/
|
|
void R_UpdateSurfaceCachedLight( msurface_t *surf )
|
|
{
|
|
for( int i = 0; i < MAXLIGHTMAPS && surf->styles[i] != 255; i++ )
|
|
surf->cached_light[i] = g_lightstylevalue[surf->styles[i]];
|
|
}
|