uid issue
This commit is contained in:
467
engine/mod_vis.cpp
Normal file
467
engine/mod_vis.cpp
Normal file
@ -0,0 +1,467 @@
|
||||
//========= Copyright <20> 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
// TODO: Should all of this Map_Vis* stuff be an interface?
|
||||
//
|
||||
|
||||
#include "quakedef.h"
|
||||
#include "gl_model_private.h"
|
||||
#include "view_shared.h"
|
||||
#include "cmodel_engine.h"
|
||||
#include "tier0/vprof.h"
|
||||
#include "utllinkedlist.h"
|
||||
#include "ivrenderview.h"
|
||||
#include "tier0/cache_hints.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
static ConVar r_novis( "r_novis","0", FCVAR_CHEAT , "Turn off the PVS." );
|
||||
static ConVar r_lockpvs( "r_lockpvs", "0", FCVAR_CHEAT, "Lock the PVS so you can fly around and inspect what is being drawn." );
|
||||
|
||||
static ConVar r_portal_use_pvs_optimization( "r_portal_use_pvs_optimization", "1", 0, "Enables an optimization that allows portals to be culled when outside of the PVS." );
|
||||
|
||||
bool g_bNoClipEnabled = false;
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Renderer interface to vis
|
||||
// ----------------------------------------------------------------------
|
||||
int r_visframecount = 0;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: For each cluster to be OR'd into vis, remember the origin, the last viewcluster
|
||||
// for that origin and the current one, so we can tell when vis is dirty and needs to be
|
||||
// recomputed
|
||||
//-----------------------------------------------------------------------------
|
||||
typedef struct
|
||||
{
|
||||
Vector origin;
|
||||
int viewcluster;
|
||||
int oldviewcluster;
|
||||
} VISCLUSTER;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Stores info for updating vis data for the map
|
||||
//-----------------------------------------------------------------------------
|
||||
typedef struct
|
||||
{
|
||||
// Number of relevant vis clusters
|
||||
int nClusters;
|
||||
// Last number ( if != nClusters, recompute vis )
|
||||
int oldnClusters;
|
||||
// List of clusters to merge together for final vis
|
||||
VISCLUSTER rgVisClusters[ MAX_VIS_LEAVES ];
|
||||
// Composite vis data
|
||||
byte rgCurrentVis[ MAX_MAP_LEAFS/8 ];
|
||||
bool bSkyVisible;
|
||||
bool bForceFullSky;
|
||||
} VISINFO;
|
||||
|
||||
static VISINFO vis;
|
||||
|
||||
// I think this is enough. We should have enough here to cover what we might have in a frame, including:
|
||||
// 1) water reflection
|
||||
// 2) camera/monitor (actually, this is merged with the regular world)
|
||||
// 3) 3dskybox
|
||||
// 4) regular world
|
||||
const int VISCACHE_SIZE = 8;
|
||||
|
||||
class VisCacheEntry
|
||||
{
|
||||
public:
|
||||
VisCacheEntry() { nClusters = 0; }
|
||||
|
||||
int nClusters;
|
||||
int originclusters[MAX_VIS_LEAVES];
|
||||
CUtlVector< unsigned short > leaflist;
|
||||
CUtlVector< unsigned short > nodelist;
|
||||
};
|
||||
|
||||
|
||||
static CUtlLinkedList< VisCacheEntry > viscache( 0, VISCACHE_SIZE );
|
||||
|
||||
|
||||
static void SortVisViewClusters()
|
||||
{
|
||||
for (int i = 1; i < vis.nClusters; ++i)
|
||||
{
|
||||
int t = vis.rgVisClusters[i].viewcluster;
|
||||
int j = i;
|
||||
while (j > 0 && vis.rgVisClusters[j-1].viewcluster > t)
|
||||
{
|
||||
vis.rgVisClusters[j].viewcluster = vis.rgVisClusters[j-1].viewcluster;
|
||||
--j;
|
||||
}
|
||||
vis.rgVisClusters[j].viewcluster = t;
|
||||
}
|
||||
}
|
||||
|
||||
static void VisMark_Cached( const VisCacheEntry &cache, const worldbrushdata_t &worldbrush )
|
||||
{
|
||||
int count, visframe;
|
||||
|
||||
visframe = r_visframecount;
|
||||
|
||||
count = cache.leaflist.Count();
|
||||
const unsigned short * RESTRICT pSrc = cache.leaflist.Base();
|
||||
|
||||
#if defined( _X360 ) || defined( _PS3 )
|
||||
const int offsetLeaf = offsetof(mleaf_t, visframe);
|
||||
const int offsetNode = offsetof(mnode_t, visframe);
|
||||
#endif
|
||||
|
||||
while ( count >= 8 )
|
||||
{
|
||||
#if defined( _X360 ) || defined( _PS3 )
|
||||
PREFETCH_128( (void *)(worldbrush.leafs + pSrc[0]), offsetLeaf );
|
||||
PREFETCH_128( (void *)(worldbrush.leafs + pSrc[1]), offsetLeaf );
|
||||
PREFETCH_128( (void *)(worldbrush.leafs + pSrc[2]), offsetLeaf );
|
||||
PREFETCH_128( (void *)(worldbrush.leafs + pSrc[3]), offsetLeaf );
|
||||
PREFETCH_128( (void *)(worldbrush.leafs + pSrc[4]), offsetLeaf );
|
||||
PREFETCH_128( (void *)(worldbrush.leafs + pSrc[5]), offsetLeaf );
|
||||
PREFETCH_128( (void *)(worldbrush.leafs + pSrc[6]), offsetLeaf );
|
||||
PREFETCH_128( (void *)(worldbrush.leafs + pSrc[7]), offsetLeaf );
|
||||
#endif
|
||||
worldbrush.leafs[pSrc[0]].visframe = visframe;
|
||||
worldbrush.leafs[pSrc[1]].visframe = visframe;
|
||||
worldbrush.leafs[pSrc[2]].visframe = visframe;
|
||||
worldbrush.leafs[pSrc[3]].visframe = visframe;
|
||||
worldbrush.leafs[pSrc[4]].visframe = visframe;
|
||||
worldbrush.leafs[pSrc[5]].visframe = visframe;
|
||||
worldbrush.leafs[pSrc[6]].visframe = visframe;
|
||||
worldbrush.leafs[pSrc[7]].visframe = visframe;
|
||||
pSrc += 8;
|
||||
count -= 8;
|
||||
}
|
||||
while ( count )
|
||||
{
|
||||
worldbrush.leafs[pSrc[0]].visframe = visframe;
|
||||
count--;
|
||||
pSrc++;
|
||||
}
|
||||
|
||||
count = cache.nodelist.Count();
|
||||
pSrc = cache.nodelist.Base();
|
||||
|
||||
while ( count >= 8 )
|
||||
{
|
||||
#if defined( _X360 ) || defined( _PS3 )
|
||||
PREFETCH_128( (void *)(worldbrush.nodes + pSrc[0]), offsetNode );
|
||||
PREFETCH_128( (void *)(worldbrush.nodes + pSrc[1]), offsetNode );
|
||||
PREFETCH_128( (void *)(worldbrush.nodes + pSrc[2]), offsetNode );
|
||||
PREFETCH_128( (void *)(worldbrush.nodes + pSrc[3]), offsetNode );
|
||||
PREFETCH_128( (void *)(worldbrush.nodes + pSrc[4]), offsetNode );
|
||||
PREFETCH_128( (void *)(worldbrush.nodes + pSrc[5]), offsetNode );
|
||||
PREFETCH_128( (void *)(worldbrush.nodes + pSrc[6]), offsetNode );
|
||||
PREFETCH_128( (void *)(worldbrush.nodes + pSrc[7]), offsetNode );
|
||||
#endif
|
||||
worldbrush.nodes[pSrc[0]].visframe = visframe;
|
||||
worldbrush.nodes[pSrc[1]].visframe = visframe;
|
||||
worldbrush.nodes[pSrc[2]].visframe = visframe;
|
||||
worldbrush.nodes[pSrc[3]].visframe = visframe;
|
||||
worldbrush.nodes[pSrc[4]].visframe = visframe;
|
||||
worldbrush.nodes[pSrc[5]].visframe = visframe;
|
||||
worldbrush.nodes[pSrc[6]].visframe = visframe;
|
||||
worldbrush.nodes[pSrc[7]].visframe = visframe;
|
||||
pSrc += 8;
|
||||
count -= 8;
|
||||
}
|
||||
while ( count )
|
||||
{
|
||||
worldbrush.nodes[pSrc[0]].visframe = visframe;
|
||||
count--;
|
||||
pSrc++;
|
||||
}
|
||||
}
|
||||
|
||||
static void VisCache_Build( VisCacheEntry &cache, const worldbrushdata_t &worldbrush )
|
||||
{
|
||||
VPROF_INCREMENT_COUNTER( "VisCache misses", 1 );
|
||||
int i;
|
||||
mleaf_t *leaf;
|
||||
int cluster;
|
||||
|
||||
cache.nClusters = vis.nClusters;
|
||||
for (i = 0; i < vis.nClusters; ++i)
|
||||
{
|
||||
cache.originclusters[i] = vis.rgVisClusters[i].viewcluster;
|
||||
}
|
||||
|
||||
cache.leaflist.RemoveAll();
|
||||
cache.nodelist.RemoveAll();
|
||||
|
||||
int visframe = r_visframecount;
|
||||
|
||||
for ( i = 0, leaf = worldbrush.leafs ; i < worldbrush.numleafs ; i++, leaf++)
|
||||
{
|
||||
MEM_ALLOC_CREDIT();
|
||||
cluster = leaf->cluster;
|
||||
if ( cluster == -1 )
|
||||
continue;
|
||||
|
||||
if (vis.rgCurrentVis[cluster>>3] & (1<<(cluster&7)))
|
||||
{
|
||||
leaf->visframe = visframe;
|
||||
cache.leaflist.AddToTail( i );
|
||||
mnode_t *node = leaf->parent;
|
||||
while (node && node->visframe != visframe)
|
||||
{
|
||||
cache.nodelist.AddToTail( node - worldbrush.nodes );
|
||||
node->visframe = visframe;
|
||||
node = node->parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool Map_AreAnyLeavesVisible( const worldbrushdata_t &worldbrush, int *leafList, int nLeaves )
|
||||
{
|
||||
for ( int i=0; i < nLeaves; i++ )
|
||||
{
|
||||
const mleaf_t *leaf = &worldbrush.leafs[leafList[i]];
|
||||
int cluster = leaf->cluster;
|
||||
if ( cluster == -1 )
|
||||
continue;
|
||||
|
||||
if ( vis.rgCurrentVis[cluster>>3] & (1<<(cluster&7)) )
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Mark the leaves and nodes that are in the PVS for the current
|
||||
// cluster(s)
|
||||
// Input : *worldmodel -
|
||||
//-----------------------------------------------------------------------------
|
||||
void Map_VisMark( bool forcenovis, model_t *worldmodel )
|
||||
{
|
||||
VPROF( "Map_VisMark" );
|
||||
int i, c;
|
||||
|
||||
// development aid to let you run around and see exactly where
|
||||
// the pvs ends
|
||||
if ( r_lockpvs.GetInt() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SortVisViewClusters();
|
||||
|
||||
bool outsideWorld = false;
|
||||
for ( i = 0; i < vis.nClusters; i++ )
|
||||
{
|
||||
if ( vis.rgVisClusters[ i ].viewcluster != vis.rgVisClusters[ i ].oldviewcluster )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// No changes
|
||||
if ( i >= vis.nClusters && !forcenovis && ( vis.nClusters == vis.oldnClusters ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Update vis frame marker
|
||||
r_visframecount++;
|
||||
|
||||
// Update cluster history
|
||||
vis.oldnClusters = vis.nClusters;
|
||||
for ( i = 0; i < vis.nClusters; i++ )
|
||||
{
|
||||
vis.rgVisClusters[ i ].oldviewcluster = vis.rgVisClusters[ i ].viewcluster;
|
||||
// Outside world?
|
||||
//
|
||||
// When using the Portal2 visibility optimization, it is possible that one or more of the
|
||||
// vis origins will be outside of the world. We should still try and compute visibility
|
||||
// from all of the clusters which are valid instead of flagging everything as visible by default.
|
||||
//
|
||||
// When noclip is enabled, we can't use this optimization because we're outside of the world
|
||||
// and yet we want to be able to see everything.
|
||||
if ( vis.rgVisClusters[ i ].viewcluster == -1 && ( !r_portal_use_pvs_optimization.GetBool() || g_bNoClipEnabled ) )
|
||||
{
|
||||
outsideWorld = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_CONVARS
|
||||
if ( r_novis.GetInt() || forcenovis || outsideWorld )
|
||||
{
|
||||
// mark everything
|
||||
for (i=0 ; i<worldmodel->brush.pShared->numleafs ; i++)
|
||||
{
|
||||
worldmodel->brush.pShared->leafs[i].visframe = r_visframecount;
|
||||
}
|
||||
for (i=0 ; i<worldmodel->brush.pShared->numnodes ; i++)
|
||||
{
|
||||
worldmodel->brush.pShared->nodes[i].visframe = r_visframecount;
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// There should always be at least one origin and that's the default render origin in most cases
|
||||
assert( vis.nClusters >= 1 );
|
||||
|
||||
CM_Vis( vis.rgCurrentVis, sizeof( vis.rgCurrentVis ), vis.rgVisClusters[ 0 ].viewcluster, DVIS_PVS );
|
||||
|
||||
// Get cluster count
|
||||
c = ( CM_NumClusters() + 31 ) / 32 ;
|
||||
|
||||
// Merge in any extra clusters
|
||||
for ( i = 1; i < vis.nClusters; i++ )
|
||||
{
|
||||
byte mapVis[ MAX_MAP_CLUSTERS/8 ];
|
||||
|
||||
CM_Vis( mapVis, sizeof( mapVis ), vis.rgVisClusters[ i ].viewcluster, DVIS_PVS );
|
||||
|
||||
// Copy one dword at a time ( could use memcpy )
|
||||
for ( int j = 0 ; j < c ; j++ )
|
||||
{
|
||||
((int *)vis.rgCurrentVis)[ j ] |= ((int *)mapVis)[ j ];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// search the cache for a pre-built list of leaves and nodes that matches
|
||||
// the desired vis setup, and use that to mark the map if found
|
||||
for (i = viscache.Head(); i != viscache.InvalidIndex(); i = viscache.Next(i))
|
||||
{
|
||||
VisCacheEntry &cache = viscache[i];
|
||||
if (cache.nClusters != vis.nClusters) continue;
|
||||
for (c = 0; c < cache.nClusters; ++c)
|
||||
{
|
||||
if (cache.originclusters[c] != vis.rgVisClusters[c].viewcluster)
|
||||
{
|
||||
// NJS: This is a nasty goto, but avoids a nasty branch mispredict below
|
||||
// (if a break and if are used instead)
|
||||
goto next_cache_check;
|
||||
}
|
||||
}
|
||||
|
||||
viscache.LinkToHead( i );
|
||||
VisMark_Cached( cache, *worldmodel->brush.pShared );
|
||||
|
||||
return;
|
||||
|
||||
next_cache_check:;
|
||||
}
|
||||
|
||||
// if we get here, we need to update the cache with a new entry
|
||||
if (viscache.Count() < VISCACHE_SIZE)
|
||||
{
|
||||
viscache.AddToHead();
|
||||
}
|
||||
else
|
||||
{
|
||||
viscache.LinkToHead( viscache.Tail() );
|
||||
}
|
||||
|
||||
// this also will mark the visleafs in order to build the cache data
|
||||
VisCache_Build( viscache[viscache.Head()], *worldmodel->brush.pShared );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Purpose: Setup vis for the specified map
|
||||
// Input : *worldmodel - the map
|
||||
// visorigincount - how many origins to merge together ( usually 1, can be 0 if forcenovis is true )
|
||||
// origins[][3] - array of origins to merge together
|
||||
// forcenovis - if set to true, ignore all origins and just mark everything as visible ( SLOW rendering!!! )
|
||||
//-----------------------------------------------------------------------------
|
||||
void Map_VisSetup( model_t *worldmodel, int visorigincount, const Vector origins[], bool forcenovis /*=false*/, unsigned int &returnFlags )
|
||||
{
|
||||
assert( visorigincount <= MAX_VIS_LEAVES );
|
||||
|
||||
// Don't crash if the client .dll tries to do something weird/dumb
|
||||
vis.nClusters = MIN( visorigincount, MAX_VIS_LEAVES );
|
||||
vis.bForceFullSky = false;
|
||||
vis.bSkyVisible = false;
|
||||
returnFlags = 0;
|
||||
for ( int i = 0; i < vis.nClusters; i++ )
|
||||
{
|
||||
int leafIndex = CM_PointLeafnum( origins[ i ] );
|
||||
int flags = CM_LeafFlags( leafIndex );
|
||||
if ( flags & ( LEAF_FLAGS_SKY | LEAF_FLAGS_SKY2D ) )
|
||||
{
|
||||
vis.bSkyVisible = true;
|
||||
}
|
||||
if ( flags & LEAF_FLAGS_RADIAL )
|
||||
{
|
||||
vis.bForceFullSky = true;
|
||||
returnFlags |= IVRenderView::VIEW_SETUP_VIS_EX_RETURN_FLAGS_USES_RADIAL_VIS;
|
||||
}
|
||||
vis.rgVisClusters[ i ].viewcluster = CM_LeafCluster( leafIndex );
|
||||
VectorCopy( origins[ i ], vis.rgVisClusters[ i ].origin );
|
||||
}
|
||||
|
||||
if ( !vis.bSkyVisible )
|
||||
{
|
||||
vis.bForceFullSky = false;
|
||||
}
|
||||
|
||||
Map_VisMark( forcenovis, worldmodel );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Clear / reset vis data
|
||||
//-----------------------------------------------------------------------------
|
||||
void Map_VisClear( void )
|
||||
{
|
||||
vis.nClusters = 1;
|
||||
vis.oldnClusters = 1;
|
||||
for ( int i = 0; i < MAX_VIS_LEAVES; i++ )
|
||||
{
|
||||
vis.rgVisClusters[ i ].oldviewcluster = -2;
|
||||
VectorClear( vis.rgVisClusters[ i ].origin );
|
||||
vis.rgVisClusters[ i ].viewcluster = -2;
|
||||
}
|
||||
viscache.RemoveAll();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Returns the current vis bitfield
|
||||
// Output : byte
|
||||
//-----------------------------------------------------------------------------
|
||||
byte *Map_VisCurrent( void )
|
||||
{
|
||||
return vis.rgCurrentVis;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Returns the first viewcluster ( usually it's the only )
|
||||
// Output : int
|
||||
//-----------------------------------------------------------------------------
|
||||
int Map_VisCurrentCluster( void )
|
||||
{
|
||||
// BUGBUG: The client DLL can hit this assert during a level transition
|
||||
// because the temporary entities do visibility calculations during the
|
||||
// wrong part of the frame loop (i.e. before a view has been set up!)
|
||||
Assert( vis.rgVisClusters[ 0 ].viewcluster >= 0 );
|
||||
if ( vis.rgVisClusters[ 0 ].viewcluster < 0 )
|
||||
{
|
||||
static int visclusterwarningcount = 0;
|
||||
|
||||
if ( ++visclusterwarningcount <= 5 )
|
||||
{
|
||||
ConDMsg( "Map_VisCurrentCluster() < 0!\n" );
|
||||
}
|
||||
}
|
||||
return vis.rgVisClusters[ 0 ].viewcluster;
|
||||
}
|
||||
|
||||
bool Map_VisSkyVisible()
|
||||
{
|
||||
return vis.bSkyVisible;
|
||||
}
|
||||
|
||||
bool Map_VisForceFullSky()
|
||||
{
|
||||
return vis.bForceFullSky;
|
||||
}
|
Reference in New Issue
Block a user