This repository has been archived on 2022-06-27. You can view files and clone it, but cannot push or open issues or pull requests.
Xash3DArchive/vid_gl/r_shadow.c

477 lines
13 KiB
C

/*
Copyright (C) 2002-2007 Victor Luchits
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 2
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.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "r_local.h"
#include "mathlib.h"
#include "matrix_lib.h"
#include "cl_entity.h"
/*
=============================================================
PLANAR STENCIL SHADOWS
=============================================================
*/
static ref_shader_t *r_planarShadowShader;
/*
===============
R_InitPlanarShadows
===============
*/
static void R_InitPlanarShadows( void )
{
r_planarShadowShader = R_LoadShader( "***r_planarShadow***", SHADER_PLANAR_SHADOW, true, 0, 0 );
}
/*
===============
R_PlanarShadowShader
===============
*/
ref_shader_t *R_PlanarShadowShader( void )
{
return r_planarShadowShader;
}
/*
===============
R_GetShadowImpactAndDir
===============
*/
static void R_GetShadowImpactAndDir( ref_entity_t *e, pmtrace_t *tr, vec3_t lightdir )
{
vec3_t point;
R_LightDir( e->lightingOrigin, lightdir, e->model->radius * e->scale );
VectorSet( lightdir, -lightdir[0], -lightdir[1], -1 );
VectorNormalizeFast( lightdir );
VectorMA( e->origin, 1024.0f, lightdir, point );
R_TraceLine( tr, e->origin, point, 0 );
}
/*
===============
R_CullPlanarShadow
===============
*/
qboolean R_CullPlanarShadow( ref_entity_t *e, vec3_t mins, vec3_t maxs, qboolean occlusion_query )
{
float planedist, dist;
vec3_t lightdir, point;
vec3_t bbox[8], newmins, newmaxs;
pmtrace_t tr;
int i;
if(( e->flags & EF_NOSHADOW ) || ( e->ent_type == ET_VIEWENTITY ))
return true;
if( RP_LOCALCLIENT( e ))
return false;
R_GetShadowImpactAndDir( e, &tr, lightdir );
if( tr.fraction == 1.0f )
return true;
R_TransformEntityBBox( e, mins, maxs, bbox, true );
VectorSubtract( tr.endpos, e->origin, point );
planedist = DotProduct( point, tr.plane.normal ) + 1;
dist = -1.0f / DotProduct( lightdir, tr.plane.normal );
VectorScale( lightdir, dist, lightdir );
ClearBounds( newmins, newmaxs );
for( i = 0; i < 8; i++ )
{
VectorSubtract( bbox[i], e->origin, bbox[i] );
dist = DotProduct( bbox[i], tr.plane.normal ) - planedist;
if( dist > 0 ) VectorMA( bbox[i], dist, lightdir, bbox[i] );
AddPointToBounds( bbox[i], newmins, newmaxs );
}
VectorAdd( newmins, e->origin, newmins );
VectorAdd( newmaxs, e->origin, newmaxs );
if( R_CullBox( newmins, newmaxs, RI.clipFlags ))
return true;
// mins/maxs are pretransfomed so use r_worldent here
if( occlusion_query && OCCLUSION_QUERIES_ENABLED( RI ) )
R_IssueOcclusionQuery( R_GetOcclusionQueryNum( OQ_PLANARSHADOW, e - r_entities ), r_worldent, newmins, newmaxs );
return false;
}
/*
===============
R_DeformVPlanarShadow
===============
*/
void R_DeformVPlanarShadow( int numV, float *v )
{
float planedist, dist;
ref_entity_t *e = RI.currententity;
vec3_t planenormal, lightdir, lightdir2, point;
pmtrace_t tr;
R_GetShadowImpactAndDir( e, &tr, lightdir );
Matrix3x3_Transform( e->axis, lightdir, lightdir2 );
Matrix3x3_Transform( e->axis, tr.plane.normal, planenormal );
VectorScale( planenormal, e->scale, planenormal );
VectorSubtract( tr.endpos, e->origin, point );
planedist = DotProduct( point, tr.plane.normal ) + 1;
dist = -1.0f / DotProduct( lightdir2, planenormal );
VectorScale( lightdir2, dist, lightdir2 );
for( ; numV > 0; numV--, v += 4 )
{
dist = DotProduct( v, planenormal ) - planedist;
if( dist > 0 ) VectorMA( v, dist, lightdir2, v );
}
}
/*
===============
R_PlanarShadowPass
===============
*/
void R_PlanarShadowPass( int state )
{
GL_EnableTexGen( GL_S, 0 );
GL_EnableTexGen( GL_T, 0 );
GL_EnableTexGen( GL_R, 0 );
GL_EnableTexGen( GL_Q, 0 );
GL_SetTexCoordArrayMode( 0 );
GL_SetState( state );
pglColor4f( 0, 0, 0, bound( 0.0f, r_shadows_alpha->value, 1.0f ) );
pglDisable( GL_TEXTURE_2D );
if( glState.stencilEnabled )
pglEnable( GL_STENCIL_TEST );
R_FlushArrays();
if( glState.stencilEnabled )
pglDisable( GL_STENCIL_TEST );
pglEnable( GL_TEXTURE_2D );
}
/*
=============================================================
STANDARD PROJECTIVE SHADOW MAPS (SSM)
=============================================================
*/
int r_numShadowGroups;
shadowGroup_t r_shadowGroups[MAX_SHADOWGROUPS];
int r_entShadowBits[MAX_ENTITIES];
#define SHADOWGROUPS_HASH_SIZE 8
static shadowGroup_t *r_shadowGroups_hash[SHADOWGROUPS_HASH_SIZE];
static byte r_shadowCullBits[MAX_SHADOWGROUPS/8];
/*
===============
R_InitShadowmaps
===============
*/
static void R_InitShadowmaps( void )
{
// clear all possible values, should be called once per scene
r_numShadowGroups = 0;
Mem_Set( r_shadowGroups, 0, sizeof( r_shadowGroups ));
Mem_Set( r_entShadowBits, 0, sizeof( r_entShadowBits ));
Mem_Set( r_shadowGroups_hash, 0, sizeof( r_shadowGroups_hash ));
}
/*
===============
R_ClearShadowmaps
===============
*/
void R_ClearShadowmaps( void )
{
r_numShadowGroups = 0;
if( r_shadows->integer != SHADOW_MAPPING || !RI.drawWorld )
return;
// clear all possible values, should be called once per scene
Mem_Set( r_shadowGroups, 0, sizeof( r_shadowGroups ));
Mem_Set( r_entShadowBits, 0, sizeof( r_entShadowBits ));
Mem_Set( r_shadowGroups_hash, 0, sizeof( r_shadowGroups_hash ));
}
/*
===============
R_AddShadowCaster
===============
*/
qboolean R_AddShadowCaster( ref_entity_t *ent )
{
int i;
float radius;
vec3_t origin;
unsigned int hash_key;
shadowGroup_t *group;
mleaf_t *leaf;
vec3_t mins, maxs, bbox[8];
if( r_shadows->integer != SHADOW_MAPPING || !RI.drawWorld )
return false;
if( !GL_Support( R_SHADER_GLSL100_EXT ) || !GL_Support( R_DEPTH_TEXTURE ) || !GL_Support( R_SHADOW_EXT ))
return false;
VectorCopy( ent->lightingOrigin, origin );
if( VectorIsNull( origin ))
return false;
// find lighting group containing entities with same lightingOrigin as ours
hash_key = (uint)( origin[0] * 7 + origin[1] * 5 + origin[2] * 3 );
hash_key &= (SHADOWGROUPS_HASH_SIZE - 1);
for( group = r_shadowGroups_hash[hash_key]; group; group = group->hashNext )
{
if( VectorCompare( group->origin, origin ))
goto add; // found an existing one, add
}
if( r_numShadowGroups == MAX_SHADOWGROUPS )
return false; // no free groups
leaf = Mod_PointInLeaf( origin, r_worldmodel );
// start a new group
group = &r_shadowGroups[r_numShadowGroups];
group->bit = ( 1<<r_numShadowGroups );
group->vis = Mod_LeafPVS( leaf, r_worldmodel );
// clear group bounds
VectorCopy( origin, group->origin );
ClearBounds( group->mins, group->maxs );
// add to hash table
group->hashNext = r_shadowGroups_hash[hash_key];
r_shadowGroups_hash[hash_key] = group;
r_numShadowGroups++;
add:
// get model bounds
switch( ent->model->type )
{
case mod_studio:
R_StudioModelBBox( ent, mins, maxs );
break;
default:
VectorClear( mins );
VectorClear( maxs );
break;
}
for( i = 0; i < 3; i++ )
{
if( mins[i] >= maxs[i] )
return false;
}
r_entShadowBits[ent - r_entities] |= group->bit;
if( ent->ent_type == ET_VIEWENTITY )
return true;
// rotate local bounding box and compute the full bounding box for this group
R_TransformEntityBBox( ent, mins, maxs, bbox, true );
for( i = 0; i < 8; i++ )
AddPointToBounds( bbox[i], group->mins, group->maxs );
// increase projection distance if needed
VectorSubtract( group->mins, origin, mins );
VectorSubtract( group->maxs, origin, maxs );
radius = RadiusFromBounds( mins, maxs );
group->projDist = max( group->projDist, radius * ent->scale * 2 + min( r_shadows_projection_distance->value, 64 ));
return true;
}
/*
===============
R_CullShadowmapGroups
===============
*/
void R_CullShadowmapGroups( void )
{
int i, j;
vec3_t mins, maxs;
shadowGroup_t *group;
if( !RI.drawWorld ) return;
Mem_Set( r_shadowCullBits, 0, sizeof( r_shadowCullBits ));
for( i = 0, group = r_shadowGroups; i < r_numShadowGroups; i++, group++ )
{
for( j = 0; j < 3; j++ )
{
mins[j] = group->origin[j] - group->projDist * 1.75 * 0.5 * 0.5;
maxs[j] = group->origin[j] + group->projDist * 1.75 * 0.5 * 0.5;
}
// check if view point is inside the bounding box...
for( j = 0; j < 3; j++ )
{
if( RI.viewOrigin[j] < mins[j] || RI.viewOrigin[j] > maxs[j] )
break;
}
if( j == 3 ) continue; // ...it is, so trivially accept
if( R_CullBox( mins, maxs, RI.clipFlags ) )
r_shadowCullBits[i>>3] |= (1<<(i&7)); // trivially reject
else if( OCCLUSION_QUERIES_ENABLED( RI ))
R_IssueOcclusionQuery( R_GetOcclusionQueryNum( OQ_SHADOWGROUP, i ), r_worldent, mins, maxs );
}
}
/*
===============
R_DrawShadowmaps
===============
*/
void R_DrawShadowmaps( void )
{
int i, j;
int width, height, textureWidth, textureHeight;
vec3_t angles;
vec3_t lightdir, M[3];
refinst_t oldRI;
shadowGroup_t *group;
if( !r_numShadowGroups )
return;
width = r_lastRefdef.viewport[2];
height = r_lastRefdef.viewport[3];
RI.previousentity = NULL;
Mem_Copy( &oldRI, &prevRI, sizeof( refinst_t ));
Mem_Copy( &prevRI, &RI, sizeof( refinst_t ));
RI.rdflags &= ~RDF_SKYPORTALINVIEW;
// find lighting group containing entities with same lightingOrigin as ours
for( i = 0, group = r_shadowGroups; i < r_numShadowGroups; i++, group++ )
{
if( r_shadowCullBits[i>>3] & ( 1<<( i&7 ) ) )
continue;
if( OCCLUSION_QUERIES_ENABLED( prevRI ) )
{
if( !R_GetOcclusionQueryResultBool( OQ_SHADOWGROUP, i, true ) )
continue;
}
RI.farClip = group->projDist;
RI.clipFlags |= ( 1<<4 ); // clip by far plane too
RI.shadowBits = 0; // no shadowing yet
RI.meshlist = &r_shadowlist;
RI.shadowGroup = group;
RI.params = RP_SHADOWMAPVIEW|RP_FLIPFRONTFACE|RP_OLDVIEWLEAF; // make sure RP_WORLDSURFVISIBLE isn't set
// allocate/resize the texture if needed
R_InitShadowmapTexture( &( tr.shadowmapTextures[i] ), i, width, height );
group->depthTexture = tr.shadowmapTextures[i];
textureWidth = group->depthTexture->width;
textureHeight = group->depthTexture->height;
// default to fov 90, R_SetupFrame will most likely alter the values to give depth more precision
RI.refdef.viewport[2] = textureWidth;
RI.refdef.viewport[3] = textureHeight;
RI.refdef.fov_x = 90;
RI.refdef.fov_y = CalcFov( RI.refdef.fov_x, RI.refdef.viewport[2], RI.refdef.viewport[3] );
Vector4Set( RI.viewport, RI.refdef.viewport[0], RI.refdef.viewport[1], textureWidth, textureHeight );
Vector4Set( RI.scissor, RI.refdef.viewport[0], RI.refdef.viewport[1], textureWidth, textureHeight );
// set the view transformation matrix according to lightgrid
R_LightDir( group->origin, lightdir, group->projDist * 0.5f );
VectorSet( lightdir, -lightdir[0], -lightdir[1], -lightdir[2] );
VectorNormalizeFast( lightdir );
NormalVectorToAxis( lightdir, M );
Matrix3x3_ToAngles( M, angles, true );
for( j = 0; j < 3; j++ )
RI.refdef.viewangles[j] = anglemod( angles[j] );
// position the light source in the opposite direction
VectorMA( group->origin, -group->projDist * 0.5, lightdir, RI.refdef.vieworg );
R_RenderView( &RI.refdef );
if( !( RI.params & RP_WORLDSURFVISIBLE ) )
continue; // we didn't cast any shadows on opaque meshes so discard this group
if( !( prevRI.shadowBits & group->bit ))
{
// capture results from framebuffer into depth texture
prevRI.shadowBits |= group->bit;
GL_Bind( 0, group->depthTexture );
pglCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, RI.refdef.viewport[0], RI.refdef.viewport[1], textureWidth, textureHeight );
}
Matrix4x4_Copy( group->worldviewProjectionMatrix, RI.worldviewProjectionMatrix );
}
oldRI.shadowBits |= prevRI.shadowBits; // set shadowBits for all RI's so that we won't
Mem_Copy( &RI, &prevRI, sizeof( refinst_t ));
Mem_Copy( &prevRI, &oldRI, sizeof( refinst_t ));
}
//==================================================================================
/*
===============
R_InitShadows
===============
*/
void R_InitShadows( void )
{
R_InitPlanarShadows();
R_InitShadowmaps();
}
/*
===============
R_ShutdownShadows
===============
*/
void R_ShutdownShadows( void )
{
r_planarShadowShader = NULL;
}