mirror of
https://github.com/id-Software/DOOM-3-BFG.git
synced 2024-12-11 13:11:47 +00:00
915 lines
25 KiB
C++
915 lines
25 KiB
C++
/*
|
|
===========================================================================
|
|
|
|
Doom 3 BFG Edition GPL Source Code
|
|
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
|
|
Copyright (C) 2013-2014 Robert Beckebans
|
|
|
|
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
|
|
|
|
Doom 3 BFG Edition Source Code 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.
|
|
|
|
Doom 3 BFG Edition Source Code 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 Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
|
|
|
|
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
|
|
|
===========================================================================
|
|
*/
|
|
|
|
#pragma hdrstop
|
|
#include "precompiled.h"
|
|
|
|
#include "tr_local.h"
|
|
|
|
/*
|
|
=================================================================================
|
|
|
|
ENTITY DEFS
|
|
|
|
=================================================================================
|
|
*/
|
|
|
|
/*
|
|
=================
|
|
R_DeriveEntityData
|
|
=================
|
|
*/
|
|
void R_DeriveEntityData( idRenderEntityLocal* entity )
|
|
{
|
|
R_AxisToModelMatrix( entity->parms.axis, entity->parms.origin, entity->modelMatrix );
|
|
|
|
idRenderMatrix::CreateFromOriginAxis( entity->parms.origin, entity->parms.axis, entity->modelRenderMatrix );
|
|
|
|
// calculate the matrix that transforms the unit cube to exactly cover the model in world space
|
|
idRenderMatrix::OffsetScaleForBounds( entity->modelRenderMatrix, entity->localReferenceBounds, entity->inverseBaseModelProject );
|
|
|
|
// calculate the global model bounds by inverse projecting the unit cube with the 'inverseBaseModelProject'
|
|
idRenderMatrix::ProjectedBounds( entity->globalReferenceBounds, entity->inverseBaseModelProject, bounds_unitCube, false );
|
|
}
|
|
|
|
/*
|
|
===================
|
|
R_FreeEntityDefDerivedData
|
|
|
|
Used by both FreeEntityDef and UpdateEntityDef
|
|
Does not actually free the entityDef.
|
|
===================
|
|
*/
|
|
void R_FreeEntityDefDerivedData( idRenderEntityLocal* def, bool keepDecals, bool keepCachedDynamicModel )
|
|
{
|
|
// demo playback needs to free the joints, while normal play
|
|
// leaves them in the control of the game
|
|
if( common->ReadDemo() )
|
|
{
|
|
if( def->parms.joints )
|
|
{
|
|
Mem_Free16( def->parms.joints );
|
|
def->parms.joints = NULL;
|
|
}
|
|
if( def->parms.callbackData )
|
|
{
|
|
Mem_Free( def->parms.callbackData );
|
|
def->parms.callbackData = NULL;
|
|
}
|
|
for( int i = 0; i < MAX_RENDERENTITY_GUI; i++ )
|
|
{
|
|
if( def->parms.gui[ i ] )
|
|
{
|
|
delete def->parms.gui[ i ];
|
|
def->parms.gui[ i ] = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
// free all the interactions
|
|
while( def->firstInteraction != NULL )
|
|
{
|
|
def->firstInteraction->UnlinkAndFree();
|
|
}
|
|
def->dynamicModelFrameCount = 0;
|
|
|
|
// clear the dynamic model if present
|
|
if( def->dynamicModel )
|
|
{
|
|
def->dynamicModel = NULL;
|
|
}
|
|
|
|
if( !keepDecals )
|
|
{
|
|
R_FreeEntityDefDecals( def );
|
|
R_FreeEntityDefOverlay( def );
|
|
}
|
|
|
|
if( !keepCachedDynamicModel )
|
|
{
|
|
delete def->cachedDynamicModel;
|
|
def->cachedDynamicModel = NULL;
|
|
}
|
|
|
|
// free the entityRefs from the areas
|
|
areaReference_t* next = NULL;
|
|
for( areaReference_t* ref = def->entityRefs; ref != NULL; ref = next )
|
|
{
|
|
next = ref->ownerNext;
|
|
|
|
// unlink from the area
|
|
ref->areaNext->areaPrev = ref->areaPrev;
|
|
ref->areaPrev->areaNext = ref->areaNext;
|
|
|
|
// put it back on the free list for reuse
|
|
def->world->areaReferenceAllocator.Free( ref );
|
|
}
|
|
def->entityRefs = NULL;
|
|
}
|
|
|
|
/*
|
|
===================
|
|
R_FreeEntityDefDecals
|
|
===================
|
|
*/
|
|
void R_FreeEntityDefDecals( idRenderEntityLocal* def )
|
|
{
|
|
def->decals = NULL;
|
|
}
|
|
|
|
/*
|
|
===================
|
|
R_FreeEntityDefFadedDecals
|
|
===================
|
|
*/
|
|
void R_FreeEntityDefFadedDecals( idRenderEntityLocal* def, int time )
|
|
{
|
|
if( def->decals != NULL )
|
|
{
|
|
def->decals->RemoveFadedDecals( time );
|
|
}
|
|
}
|
|
|
|
/*
|
|
===================
|
|
R_FreeEntityDefOverlay
|
|
===================
|
|
*/
|
|
void R_FreeEntityDefOverlay( idRenderEntityLocal* def )
|
|
{
|
|
def->overlays = NULL;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
R_CreateEntityRefs
|
|
|
|
Creates all needed model references in portal areas,
|
|
chaining them to both the area and the entityDef.
|
|
|
|
Bumps tr.viewCount, which means viewCount can change many times each frame.
|
|
===============
|
|
*/
|
|
void R_CreateEntityRefs( idRenderEntityLocal* entity )
|
|
{
|
|
if( entity->parms.hModel == NULL )
|
|
{
|
|
entity->parms.hModel = renderModelManager->DefaultModel();
|
|
}
|
|
|
|
// if the entity hasn't been fully specified due to expensive animation calcs
|
|
// for md5 and particles, use the provided conservative bounds.
|
|
if( entity->parms.callback != NULL )
|
|
{
|
|
entity->localReferenceBounds = entity->parms.bounds;
|
|
}
|
|
else
|
|
{
|
|
entity->localReferenceBounds = entity->parms.hModel->Bounds( &entity->parms );
|
|
}
|
|
|
|
// some models, like empty particles, may not need to be added at all
|
|
if( entity->localReferenceBounds.IsCleared() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( r_showUpdates.GetBool() &&
|
|
( entity->localReferenceBounds[1][0] - entity->localReferenceBounds[0][0] > 1024.0f ||
|
|
entity->localReferenceBounds[1][1] - entity->localReferenceBounds[0][1] > 1024.0f ) )
|
|
{
|
|
common->Printf( "big entityRef: %f,%f\n", entity->localReferenceBounds[1][0] - entity->localReferenceBounds[0][0],
|
|
entity->localReferenceBounds[1][1] - entity->localReferenceBounds[0][1] );
|
|
}
|
|
|
|
// derive entity data
|
|
R_DeriveEntityData( entity );
|
|
|
|
// bump the view count so we can tell if an
|
|
// area already has a reference
|
|
tr.viewCount++;
|
|
|
|
// push the model frustum down the BSP tree into areas
|
|
entity->world->PushFrustumIntoTree( entity, NULL, entity->inverseBaseModelProject, bounds_unitCube );
|
|
}
|
|
|
|
/*
|
|
=================================================================================
|
|
|
|
LIGHT DEFS
|
|
|
|
=================================================================================
|
|
*/
|
|
|
|
/*
|
|
========================
|
|
R_ComputePointLightProjectionMatrix
|
|
|
|
Computes the light projection matrix for a point light.
|
|
========================
|
|
*/
|
|
static float R_ComputePointLightProjectionMatrix( idRenderLightLocal* light, idRenderMatrix& localProject )
|
|
{
|
|
assert( light->parms.pointLight );
|
|
|
|
// A point light uses a box projection.
|
|
// This projects into the 0.0 - 1.0 texture range instead of -1.0 to 1.0 clip space range.
|
|
localProject.Zero();
|
|
localProject[0][0] = 0.5f / light->parms.lightRadius[0];
|
|
localProject[1][1] = 0.5f / light->parms.lightRadius[1];
|
|
localProject[2][2] = 0.5f / light->parms.lightRadius[2];
|
|
localProject[0][3] = 0.5f;
|
|
localProject[1][3] = 0.5f;
|
|
localProject[2][3] = 0.5f;
|
|
localProject[3][3] = 1.0f; // identity perspective
|
|
|
|
return 1.0f;
|
|
}
|
|
|
|
static const float SPOT_LIGHT_MIN_Z_NEAR = 8.0f;
|
|
static const float SPOT_LIGHT_MIN_Z_FAR = 16.0f;
|
|
|
|
/*
|
|
========================
|
|
R_ComputeSpotLightProjectionMatrix
|
|
|
|
Computes the light projection matrix for a spot light.
|
|
========================
|
|
*/
|
|
static float R_ComputeSpotLightProjectionMatrix( idRenderLightLocal* light, idRenderMatrix& localProject )
|
|
{
|
|
const float targetDistSqr = light->parms.target.LengthSqr();
|
|
const float invTargetDist = idMath::InvSqrt( targetDistSqr );
|
|
const float targetDist = invTargetDist * targetDistSqr;
|
|
|
|
const idVec3 normalizedTarget = light->parms.target * invTargetDist;
|
|
const idVec3 normalizedRight = light->parms.right * ( 0.5f * targetDist / light->parms.right.LengthSqr() );
|
|
const idVec3 normalizedUp = light->parms.up * ( -0.5f * targetDist / light->parms.up.LengthSqr() );
|
|
|
|
localProject[0][0] = normalizedRight[0];
|
|
localProject[0][1] = normalizedRight[1];
|
|
localProject[0][2] = normalizedRight[2];
|
|
localProject[0][3] = 0.0f;
|
|
|
|
localProject[1][0] = normalizedUp[0];
|
|
localProject[1][1] = normalizedUp[1];
|
|
localProject[1][2] = normalizedUp[2];
|
|
localProject[1][3] = 0.0f;
|
|
|
|
localProject[3][0] = normalizedTarget[0];
|
|
localProject[3][1] = normalizedTarget[1];
|
|
localProject[3][2] = normalizedTarget[2];
|
|
localProject[3][3] = 0.0f;
|
|
|
|
// Set the falloff vector.
|
|
// This is similar to the Z calculation for depth buffering, which means that the
|
|
// mapped texture is going to be perspective distorted heavily towards the zero end.
|
|
const float zNear = Max( light->parms.start * normalizedTarget, SPOT_LIGHT_MIN_Z_NEAR );
|
|
const float zFar = Max( light->parms.end * normalizedTarget, SPOT_LIGHT_MIN_Z_FAR );
|
|
const float zScale = ( zNear + zFar ) / zFar;
|
|
|
|
localProject[2][0] = normalizedTarget[0] * zScale;
|
|
localProject[2][1] = normalizedTarget[1] * zScale;
|
|
localProject[2][2] = normalizedTarget[2] * zScale;
|
|
localProject[2][3] = - zNear * zScale;
|
|
|
|
// now offset to the 0.0 - 1.0 texture range instead of -1.0 to 1.0 clip space range
|
|
idVec4 projectedTarget;
|
|
localProject.TransformPoint( light->parms.target, projectedTarget );
|
|
|
|
const float ofs0 = 0.5f - projectedTarget[0] / projectedTarget[3];
|
|
localProject[0][0] += ofs0 * localProject[3][0];
|
|
localProject[0][1] += ofs0 * localProject[3][1];
|
|
localProject[0][2] += ofs0 * localProject[3][2];
|
|
localProject[0][3] += ofs0 * localProject[3][3];
|
|
|
|
const float ofs1 = 0.5f - projectedTarget[1] / projectedTarget[3];
|
|
localProject[1][0] += ofs1 * localProject[3][0];
|
|
localProject[1][1] += ofs1 * localProject[3][1];
|
|
localProject[1][2] += ofs1 * localProject[3][2];
|
|
localProject[1][3] += ofs1 * localProject[3][3];
|
|
|
|
return 1.0f / ( zNear + zFar );
|
|
}
|
|
|
|
/*
|
|
========================
|
|
R_ComputeParallelLightProjectionMatrix
|
|
|
|
Computes the light projection matrix for a parallel light.
|
|
========================
|
|
*/
|
|
static float R_ComputeParallelLightProjectionMatrix( idRenderLightLocal* light, idRenderMatrix& localProject )
|
|
{
|
|
assert( light->parms.parallel );
|
|
|
|
// A parallel light uses a box projection.
|
|
// This projects into the 0.0 - 1.0 texture range instead of -1.0 to 1.0 clip space range.
|
|
localProject.Zero();
|
|
localProject[0][0] = 0.5f / light->parms.lightRadius[0];
|
|
localProject[1][1] = 0.5f / light->parms.lightRadius[1];
|
|
localProject[2][2] = 0.5f / light->parms.lightRadius[2];
|
|
localProject[0][3] = 0.5f;
|
|
localProject[1][3] = 0.5f;
|
|
localProject[2][3] = 0.5f;
|
|
localProject[3][3] = 1.0f; // identity perspective
|
|
|
|
return 1.0f;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_DeriveLightData
|
|
|
|
Fills everything in based on light->parms
|
|
=================
|
|
*/
|
|
void R_DeriveLightData( idRenderLightLocal* light )
|
|
{
|
|
|
|
// decide which light shader we are going to use
|
|
if( light->parms.shader != NULL )
|
|
{
|
|
light->lightShader = light->parms.shader;
|
|
}
|
|
else if( light->lightShader == NULL )
|
|
{
|
|
if( light->parms.pointLight )
|
|
{
|
|
light->lightShader = tr.defaultPointLight;
|
|
}
|
|
else
|
|
{
|
|
light->lightShader = tr.defaultProjectedLight;
|
|
}
|
|
}
|
|
|
|
// get the falloff image
|
|
light->falloffImage = light->lightShader->LightFalloffImage();
|
|
|
|
if( light->falloffImage == NULL )
|
|
{
|
|
// use the falloff from the default shader of the correct type
|
|
const idMaterial* defaultShader;
|
|
|
|
if( light->parms.pointLight )
|
|
{
|
|
defaultShader = tr.defaultPointLight;
|
|
|
|
// Touch the default shader. to make sure it's decl has been parsed ( it might have been purged ).
|
|
declManager->Touch( static_cast< const idDecl*>( defaultShader ) );
|
|
|
|
light->falloffImage = defaultShader->LightFalloffImage();
|
|
|
|
}
|
|
else
|
|
{
|
|
// projected lights by default don't diminish with distance
|
|
defaultShader = tr.defaultProjectedLight;
|
|
|
|
// Touch the light shader. to make sure it's decl has been parsed ( it might have been purged ).
|
|
declManager->Touch( static_cast< const idDecl*>( defaultShader ) );
|
|
|
|
light->falloffImage = defaultShader->LightFalloffImage();
|
|
}
|
|
}
|
|
|
|
// ------------------------------------
|
|
// compute the light projection matrix
|
|
// ------------------------------------
|
|
|
|
idRenderMatrix localProject;
|
|
float zScale = 1.0f;
|
|
if( light->parms.parallel )
|
|
{
|
|
zScale = R_ComputeParallelLightProjectionMatrix( light, localProject );
|
|
}
|
|
else if( light->parms.pointLight )
|
|
{
|
|
zScale = R_ComputePointLightProjectionMatrix( light, localProject );
|
|
}
|
|
else
|
|
{
|
|
zScale = R_ComputeSpotLightProjectionMatrix( light, localProject );
|
|
}
|
|
|
|
// set the old style light projection where Z and W are flipped and
|
|
// for projected lights lightProject[3] is divided by ( zNear + zFar )
|
|
light->lightProject[0][0] = localProject[0][0];
|
|
light->lightProject[0][1] = localProject[0][1];
|
|
light->lightProject[0][2] = localProject[0][2];
|
|
light->lightProject[0][3] = localProject[0][3];
|
|
|
|
light->lightProject[1][0] = localProject[1][0];
|
|
light->lightProject[1][1] = localProject[1][1];
|
|
light->lightProject[1][2] = localProject[1][2];
|
|
light->lightProject[1][3] = localProject[1][3];
|
|
|
|
light->lightProject[2][0] = localProject[3][0];
|
|
light->lightProject[2][1] = localProject[3][1];
|
|
light->lightProject[2][2] = localProject[3][2];
|
|
light->lightProject[2][3] = localProject[3][3];
|
|
|
|
light->lightProject[3][0] = localProject[2][0] * zScale;
|
|
light->lightProject[3][1] = localProject[2][1] * zScale;
|
|
light->lightProject[3][2] = localProject[2][2] * zScale;
|
|
light->lightProject[3][3] = localProject[2][3] * zScale;
|
|
|
|
// transform the lightProject
|
|
float lightTransform[16];
|
|
R_AxisToModelMatrix( light->parms.axis, light->parms.origin, lightTransform );
|
|
for( int i = 0; i < 4; i++ )
|
|
{
|
|
idPlane temp = light->lightProject[i];
|
|
R_LocalPlaneToGlobal( lightTransform, temp, light->lightProject[i] );
|
|
}
|
|
|
|
// adjust global light origin for off center projections and parallel projections
|
|
// we are just faking parallel by making it a very far off center for now
|
|
if( light->parms.parallel )
|
|
{
|
|
idVec3 dir = light->parms.lightCenter;
|
|
if( dir.Normalize() == 0.0f )
|
|
{
|
|
// make point straight up if not specified
|
|
dir[2] = 1.0f;
|
|
}
|
|
light->globalLightOrigin = light->parms.origin + dir * 100000.0f;
|
|
}
|
|
else
|
|
{
|
|
light->globalLightOrigin = light->parms.origin + light->parms.axis * light->parms.lightCenter;
|
|
}
|
|
|
|
// Rotate and translate the light projection by the light matrix.
|
|
// 99% of lights remain axis aligned in world space.
|
|
idRenderMatrix lightMatrix;
|
|
idRenderMatrix::CreateFromOriginAxis( light->parms.origin, light->parms.axis, lightMatrix );
|
|
|
|
idRenderMatrix inverseLightMatrix;
|
|
if( !idRenderMatrix::Inverse( lightMatrix, inverseLightMatrix ) )
|
|
{
|
|
idLib::Warning( "lightMatrix invert failed" );
|
|
}
|
|
|
|
// 'baseLightProject' goes from global space -> light local space -> light projective space
|
|
idRenderMatrix::Multiply( localProject, inverseLightMatrix, light->baseLightProject );
|
|
|
|
// Invert the light projection so we can deform zero-to-one cubes into
|
|
// the light model and calculate global bounds.
|
|
if( !idRenderMatrix::Inverse( light->baseLightProject, light->inverseBaseLightProject ) )
|
|
{
|
|
idLib::Warning( "baseLightProject invert failed" );
|
|
}
|
|
|
|
// calculate the global light bounds by inverse projecting the zero to one cube with the 'inverseBaseLightProject'
|
|
idRenderMatrix::ProjectedBounds( light->globalLightBounds, light->inverseBaseLightProject, bounds_zeroOneCube, false );
|
|
}
|
|
|
|
/*
|
|
====================
|
|
R_FreeLightDefDerivedData
|
|
|
|
Frees all references and lit surfaces from the light
|
|
====================
|
|
*/
|
|
void R_FreeLightDefDerivedData( idRenderLightLocal* ldef )
|
|
{
|
|
// remove any portal fog references
|
|
for( doublePortal_t* dp = ldef->foggedPortals; dp != NULL; dp = dp->nextFoggedPortal )
|
|
{
|
|
dp->fogLight = NULL;
|
|
}
|
|
|
|
// free all the interactions
|
|
while( ldef->firstInteraction != NULL )
|
|
{
|
|
ldef->firstInteraction->UnlinkAndFree();
|
|
}
|
|
|
|
// free all the references to the light
|
|
areaReference_t* nextRef = NULL;
|
|
for( areaReference_t* lref = ldef->references; lref != NULL; lref = nextRef )
|
|
{
|
|
nextRef = lref->ownerNext;
|
|
|
|
// unlink from the area
|
|
lref->areaNext->areaPrev = lref->areaPrev;
|
|
lref->areaPrev->areaNext = lref->areaNext;
|
|
|
|
// put it back on the free list for reuse
|
|
ldef->world->areaReferenceAllocator.Free( lref );
|
|
}
|
|
ldef->references = NULL;
|
|
}
|
|
|
|
// RB begin
|
|
void R_RenderLightFrustum( const renderLight_t& renderLight, idPlane lightFrustum[6] )
|
|
{
|
|
idRenderLightLocal fakeLight;
|
|
|
|
fakeLight.parms = renderLight;
|
|
|
|
R_DeriveLightData( &fakeLight );
|
|
|
|
idRenderMatrix::GetFrustumPlanes( lightFrustum, fakeLight.baseLightProject, true, true );
|
|
|
|
// the DOOM 3 frustum planes point outside the frustum
|
|
for( int i = 0; i < 6; i++ )
|
|
{
|
|
lightFrustum[i] = -lightFrustum[i];
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
=====================
|
|
R_PolytopeSurface
|
|
|
|
Generate vertexes and indexes for a polytope, and optionally returns the polygon windings.
|
|
The positive sides of the planes will be visible.
|
|
=====================
|
|
*/
|
|
srfTriangles_t* R_PolytopeSurface( int numPlanes, const idPlane* planes, idWinding** windings )
|
|
{
|
|
int i, j;
|
|
srfTriangles_t* tri;
|
|
|
|
const int MAX_POLYTOPE_PLANES = 6;
|
|
|
|
idFixedWinding planeWindings[MAX_POLYTOPE_PLANES];
|
|
int numVerts, numIndexes;
|
|
|
|
if( numPlanes > MAX_POLYTOPE_PLANES )
|
|
{
|
|
common->Error( "R_PolytopeSurface: more than %d planes", MAX_POLYTOPE_PLANES );
|
|
}
|
|
|
|
numVerts = 0;
|
|
numIndexes = 0;
|
|
for( i = 0; i < numPlanes; i++ )
|
|
{
|
|
const idPlane& plane = planes[i];
|
|
idFixedWinding& w = planeWindings[i];
|
|
|
|
w.BaseForPlane( plane );
|
|
for( j = 0; j < numPlanes; j++ )
|
|
{
|
|
const idPlane& plane2 = planes[j];
|
|
if( j == i )
|
|
{
|
|
continue;
|
|
}
|
|
if( !w.ClipInPlace( -plane2, ON_EPSILON ) )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if( w.GetNumPoints() <= 2 )
|
|
{
|
|
continue;
|
|
}
|
|
numVerts += w.GetNumPoints();
|
|
numIndexes += ( w.GetNumPoints() - 2 ) * 3;
|
|
}
|
|
|
|
// allocate the surface
|
|
tri = R_AllocStaticTriSurf();
|
|
R_AllocStaticTriSurfVerts( tri, numVerts );
|
|
R_AllocStaticTriSurfIndexes( tri, numIndexes );
|
|
|
|
// copy the data from the windings
|
|
for( i = 0; i < numPlanes; i++ )
|
|
{
|
|
idFixedWinding& w = planeWindings[i];
|
|
if( !w.GetNumPoints() )
|
|
{
|
|
continue;
|
|
}
|
|
for( j = 0 ; j < w.GetNumPoints() ; j++ )
|
|
{
|
|
tri->verts[tri->numVerts + j ].Clear();
|
|
tri->verts[tri->numVerts + j ].xyz = w[j].ToVec3();
|
|
}
|
|
|
|
for( j = 1 ; j < w.GetNumPoints() - 1 ; j++ )
|
|
{
|
|
tri->indexes[ tri->numIndexes + 0 ] = tri->numVerts;
|
|
tri->indexes[ tri->numIndexes + 1 ] = tri->numVerts + j;
|
|
tri->indexes[ tri->numIndexes + 2 ] = tri->numVerts + j + 1;
|
|
tri->numIndexes += 3;
|
|
}
|
|
tri->numVerts += w.GetNumPoints();
|
|
|
|
// optionally save the winding
|
|
if( windings )
|
|
{
|
|
windings[i] = new idWinding( w.GetNumPoints() );
|
|
*windings[i] = w;
|
|
}
|
|
}
|
|
|
|
R_BoundTriSurf( tri );
|
|
|
|
return tri;
|
|
}
|
|
// RB end
|
|
|
|
/*
|
|
===============
|
|
WindingCompletelyInsideLight
|
|
===============
|
|
*/
|
|
static bool WindingCompletelyInsideLight( const idWinding* w, const idRenderLightLocal* ldef )
|
|
{
|
|
for( int i = 0; i < w->GetNumPoints(); i++ )
|
|
{
|
|
if( idRenderMatrix::CullPointToMVP( ldef->baseLightProject, ( *w )[i].ToVec3(), true ) )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
======================
|
|
R_CreateLightDefFogPortals
|
|
|
|
When a fog light is created or moved, see if it completely
|
|
encloses any portals, which may allow them to be fogged closed.
|
|
======================
|
|
*/
|
|
static void R_CreateLightDefFogPortals( idRenderLightLocal* ldef )
|
|
{
|
|
ldef->foggedPortals = NULL;
|
|
|
|
if( !ldef->lightShader->IsFogLight() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// some fog lights will explicitly disallow portal fogging
|
|
if( ldef->lightShader->TestMaterialFlag( MF_NOPORTALFOG ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
for( areaReference_t* lref = ldef->references; lref != NULL; lref = lref->ownerNext )
|
|
{
|
|
// check all the models in this area
|
|
portalArea_t* area = lref->area;
|
|
|
|
for( portal_t* prt = area->portals; prt != NULL; prt = prt->next )
|
|
{
|
|
doublePortal_t* dp = prt->doublePortal;
|
|
|
|
// we only handle a single fog volume covering a portal
|
|
// this will never cause incorrect drawing, but it may
|
|
// fail to cull a portal
|
|
if( dp->fogLight )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if( WindingCompletelyInsideLight( prt->w, ldef ) )
|
|
{
|
|
dp->fogLight = ldef;
|
|
dp->nextFoggedPortal = ldef->foggedPortals;
|
|
ldef->foggedPortals = dp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_CreateLightRefs
|
|
=================
|
|
*/
|
|
void R_CreateLightRefs( idRenderLightLocal* light )
|
|
{
|
|
// derive light data
|
|
R_DeriveLightData( light );
|
|
|
|
// determine the areaNum for the light origin, which may let us
|
|
// cull the light if it is behind a closed door
|
|
// it is debatable if we want to use the entity origin or the center offset origin,
|
|
// but we definitely don't want to use a parallel offset origin
|
|
light->areaNum = light->world->PointInArea( light->globalLightOrigin );
|
|
if( light->areaNum == -1 )
|
|
{
|
|
light->areaNum = light->world->PointInArea( light->parms.origin );
|
|
}
|
|
|
|
// bump the view count so we can tell if an
|
|
// area already has a reference
|
|
tr.viewCount++;
|
|
|
|
// if we have a prelight model that includes all the shadows for the major world occluders,
|
|
// we can limit the area references to those visible through the portals from the light center.
|
|
// We can't do this in the normal case, because shadows are cast from back facing triangles, which
|
|
// may be in areas not directly visible to the light projection center.
|
|
if( light->parms.prelightModel != NULL && r_useLightPortalFlow.GetBool() && light->lightShader->LightCastsShadows() )
|
|
{
|
|
light->world->FlowLightThroughPortals( light );
|
|
}
|
|
else
|
|
{
|
|
// push the light frustum down the BSP tree into areas
|
|
light->world->PushFrustumIntoTree( NULL, light, light->inverseBaseLightProject, bounds_zeroOneCube );
|
|
}
|
|
|
|
R_CreateLightDefFogPortals( light );
|
|
}
|
|
|
|
/*
|
|
=================================================================================
|
|
|
|
WORLD MODEL & LIGHT DEFS
|
|
|
|
=================================================================================
|
|
*/
|
|
|
|
/*
|
|
===================
|
|
R_FreeDerivedData
|
|
|
|
ReloadModels and RegenerateWorld call this
|
|
===================
|
|
*/
|
|
void R_FreeDerivedData()
|
|
{
|
|
for( int j = 0; j < tr.worlds.Num(); j++ )
|
|
{
|
|
idRenderWorldLocal* rw = tr.worlds[j];
|
|
|
|
for( int i = 0; i < rw->entityDefs.Num(); i++ )
|
|
{
|
|
idRenderEntityLocal* def = rw->entityDefs[i];
|
|
if( def == NULL )
|
|
{
|
|
continue;
|
|
}
|
|
R_FreeEntityDefDerivedData( def, false, false );
|
|
}
|
|
|
|
for( int i = 0; i < rw->lightDefs.Num(); i++ )
|
|
{
|
|
idRenderLightLocal* light = rw->lightDefs[i];
|
|
if( light == NULL )
|
|
{
|
|
continue;
|
|
}
|
|
R_FreeLightDefDerivedData( light );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
===================
|
|
R_CheckForEntityDefsUsingModel
|
|
===================
|
|
*/
|
|
void R_CheckForEntityDefsUsingModel( idRenderModel* model )
|
|
{
|
|
for( int j = 0; j < tr.worlds.Num(); j++ )
|
|
{
|
|
idRenderWorldLocal* rw = tr.worlds[j];
|
|
|
|
for( int i = 0; i < rw->entityDefs.Num(); i++ )
|
|
{
|
|
idRenderEntityLocal* def = rw->entityDefs[i];
|
|
if( !def )
|
|
{
|
|
continue;
|
|
}
|
|
if( def->parms.hModel == model )
|
|
{
|
|
//assert( 0 );
|
|
// this should never happen but Radiant messes it up all the time so just free the derived data
|
|
R_FreeEntityDefDerivedData( def, false, false );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
===================
|
|
R_ReCreateWorldReferences
|
|
|
|
ReloadModels and RegenerateWorld call this
|
|
===================
|
|
*/
|
|
void R_ReCreateWorldReferences()
|
|
{
|
|
// let the interaction generation code know this
|
|
// shouldn't be optimized for a particular view
|
|
tr.viewDef = NULL;
|
|
|
|
for( int j = 0; j < tr.worlds.Num(); j++ )
|
|
{
|
|
idRenderWorldLocal* rw = tr.worlds[j];
|
|
|
|
for( int i = 0; i < rw->entityDefs.Num(); i++ )
|
|
{
|
|
idRenderEntityLocal* def = rw->entityDefs[i];
|
|
if( def == NULL )
|
|
{
|
|
continue;
|
|
}
|
|
// the world model entities are put specifically in a single
|
|
// area, instead of just pushing their bounds into the tree
|
|
if( i < rw->numPortalAreas )
|
|
{
|
|
rw->AddEntityRefToArea( def, &rw->portalAreas[i] );
|
|
}
|
|
else
|
|
{
|
|
R_CreateEntityRefs( def );
|
|
}
|
|
}
|
|
|
|
for( int i = 0; i < rw->lightDefs.Num(); i++ )
|
|
{
|
|
idRenderLightLocal* light = rw->lightDefs[i];
|
|
if( light == NULL )
|
|
{
|
|
continue;
|
|
}
|
|
renderLight_t parms = light->parms;
|
|
|
|
light->world->FreeLightDef( i );
|
|
rw->UpdateLightDef( i, &parms );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
====================
|
|
R_ModulateLights_f
|
|
|
|
Modifies the shaderParms on all the lights so the level
|
|
designers can easily test different color schemes
|
|
====================
|
|
*/
|
|
void R_ModulateLights_f( const idCmdArgs& args )
|
|
{
|
|
if( !tr.primaryWorld )
|
|
{
|
|
return;
|
|
}
|
|
if( args.Argc() != 4 )
|
|
{
|
|
common->Printf( "usage: modulateLights <redFloat> <greenFloat> <blueFloat>\n" );
|
|
return;
|
|
}
|
|
|
|
float modulate[3];
|
|
for( int i = 0; i < 3; i++ )
|
|
{
|
|
modulate[i] = atof( args.Argv( i + 1 ) );
|
|
}
|
|
|
|
int count = 0;
|
|
for( int i = 0; i < tr.primaryWorld->lightDefs.Num(); i++ )
|
|
{
|
|
idRenderLightLocal* light = tr.primaryWorld->lightDefs[i];
|
|
if( light != NULL )
|
|
{
|
|
count++;
|
|
for( int j = 0; j < 3; j++ )
|
|
{
|
|
light->parms.shaderParms[j] *= modulate[j];
|
|
}
|
|
}
|
|
}
|
|
common->Printf( "modulated %i lights\n", count );
|
|
}
|