You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3219 lines
106 KiB
3219 lines
106 KiB
//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
|
|
//
|
|
// Purpose:
|
|
//
|
|
// $NoKeywords: $
|
|
//
|
|
// @TODO: The features and implementation of these class classes require overly
|
|
// broad mutexing. Needs to be addressed. (toml 3-20-06)
|
|
//
|
|
//===========================================================================//
|
|
|
|
#include "mathlib/vector.h"
|
|
#include "utlhash.h"
|
|
#include "utllinkedlist.h"
|
|
#include "utllinkedlist.h"
|
|
#include "ispatialpartitioninternal.h"
|
|
#include "bsptreedata.h"
|
|
#include "worldsize.h"
|
|
#include "cmodel.h"
|
|
#include "sys_dll.h"
|
|
#include "collisionutils.h"
|
|
#include "debugoverlay.h"
|
|
#include "tier0/vprof.h"
|
|
#include "tier1/utlbuffer.h"
|
|
#include "filesystem_engine.h"
|
|
#include "filesystem.h"
|
|
#include "tier1/convar.h"
|
|
#include "tier1/memstack.h"
|
|
#include "enginethreads.h"
|
|
#include "datacache/imdlcache.h"
|
|
#include "tier2/renderutils.h"
|
|
#include "bitvec.h"
|
|
#include "host.h"
|
|
#include "tier1/mempool.h"
|
|
|
|
#ifdef _PS3
|
|
#include "tls_ps3.h"
|
|
#endif
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
class CVoxelTree;
|
|
class CIntersectSweptBox;
|
|
|
|
#define SPHASH_LEVEL_SKIP 2
|
|
|
|
#define SPHASH_VOXEL_SIZE 256 // must power of 2
|
|
#define SPHASH_VOXEL_SHIFT 8 // shift for voxel size
|
|
|
|
#define SPHASH_VOXEL_LARGE 65536.0f
|
|
|
|
#define SPHASH_HANDLELIST_BLOCK 256
|
|
#define SPHASH_LEAFLIST_BLOCK 512
|
|
#define SPHASH_ENTITYLIST_BLOCK 256
|
|
#define SPHASH_BUCKET_COUNT 512
|
|
|
|
#define SPHASH_EPS 0.03125f
|
|
|
|
enum PartitionTrees_t
|
|
{
|
|
CLIENT_TREE,
|
|
SERVER_TREE,
|
|
NUM_TREES,
|
|
};
|
|
|
|
class CPartitionVisitor;
|
|
|
|
#if defined( _X360 )
|
|
#pragma bitfield_order( push, lsb_to_msb )
|
|
#elif defined( _PS3 )
|
|
#pragma ms_struct on
|
|
#pragma reverse_bitfields on
|
|
#endif
|
|
union Voxel_t
|
|
{
|
|
struct
|
|
{
|
|
unsigned int x:11;
|
|
unsigned int y:11;
|
|
unsigned int z:10;
|
|
} bitsVoxel;
|
|
unsigned int uiVoxel;
|
|
};
|
|
#if defined( _X360 )
|
|
#pragma bitfield_order( pop )
|
|
#elif defined( _PS3 )
|
|
#pragma ms_struct off
|
|
#pragma reverse_bitfields off
|
|
#endif
|
|
|
|
enum EntityInfoFlags_t
|
|
{
|
|
ENTITY_HIDDEN = ( 1 << 0 ),
|
|
IN_CLIENT_TREE = ( 1 << 1 ),
|
|
IN_SERVER_TREE = ( 1 << 2 ),
|
|
};
|
|
|
|
|
|
struct EntityInfo_t
|
|
{
|
|
Vector m_vecMin; // Min/Max of entity
|
|
Voxel_t m_voxelMin;
|
|
Vector m_vecMax;
|
|
Voxel_t m_voxelMax;
|
|
IHandleEntity * m_pHandleEntity; // Entity handle.
|
|
unsigned short m_fList; // Which lists is it in?
|
|
uint8 m_flags;
|
|
char m_nLevel[NUM_TREES]; // Which level voxel tree is it in?
|
|
unsigned short m_nVisitBit[NUM_TREES];
|
|
intp m_iLeafList[NUM_TREES]; // Index into the leaf pool - leaf list for entity (m_aLeafList).
|
|
};
|
|
|
|
|
|
struct LeafListData_t
|
|
{
|
|
UtlHashFixedHandle_t m_hVoxel; // Voxel handle the entity is in.
|
|
intp m_iEntity; // Entity list index for voxel
|
|
};
|
|
|
|
typedef CUtlFixedLinkedList<LeafListData_t> CLeafList;
|
|
|
|
typedef CVarBitVec CPartitionVisits;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Used when rendering the various levels of the voxel hash
|
|
//-----------------------------------------------------------------------------
|
|
static Color s_pVoxelColor[9] =
|
|
{
|
|
Color( 255, 0, 0, 255 ),
|
|
Color( 0, 255, 0, 255 ),
|
|
Color( 0, 0, 255, 255 ),
|
|
Color( 255, 0, 255, 255 ),
|
|
Color( 255, 255, 0, 255 ),
|
|
Color( 0, 255, 255, 255 ),
|
|
Color( 255, 255, 255, 255 ),
|
|
Color( 192, 192, 0, 255 ),
|
|
Color( 128, 128, 128, 255 ),
|
|
};
|
|
|
|
|
|
// bounds of the spatial partition
|
|
static Vector s_PartitionMin( MIN_COORD_FLOAT, MIN_COORD_FLOAT, MIN_COORD_FLOAT );
|
|
static Vector s_PartitionMax( MAX_COORD_FLOAT, MAX_COORD_FLOAT, MAX_COORD_FLOAT );
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Divide voxel coordinates by 2
|
|
//-----------------------------------------------------------------------------
|
|
inline Voxel_t ConvertToNextLevel( Voxel_t v )
|
|
{
|
|
// Just need to divide by 2 and eliminate the low bits of y and z that shifted
|
|
// into the x and y fields
|
|
Voxel_t res;
|
|
res.uiVoxel = ( ( v.uiVoxel >> SPHASH_LEVEL_SKIP ) & 0xFFCFF9FF );
|
|
return res;
|
|
}
|
|
|
|
class CSpatialEntry
|
|
{
|
|
public:
|
|
SpatialPartitionHandle_t m_handle;
|
|
uint16 m_nListMask;
|
|
};
|
|
//-----------------------------------------------------------------------------
|
|
// A single voxel hash
|
|
//-----------------------------------------------------------------------------
|
|
class CVoxelHash
|
|
{
|
|
public:
|
|
// Constructor, destructor
|
|
CVoxelHash();
|
|
~CVoxelHash();
|
|
|
|
// Call this to clear out the spatial partition and to re-initialize it given a particular world size (ISpatialPartitionInternal)
|
|
void Init( CVoxelTree *pPartition, const Vector& worldmin, const Vector& worldmax, int nLevel );
|
|
void Shutdown();
|
|
|
|
// Gets all entities in a particular volume...
|
|
// returns false if the enumerator broke early
|
|
bool EnumerateElementsInBox( SpatialPartitionListMask_t listMask, Voxel_t vmin, Voxel_t vmax, const Vector& mins, const Vector& maxs, IPartitionEnumerator* pIterator );
|
|
bool EnumerateElementsAlongRay( SpatialPartitionListMask_t listMask, const Ray_t& ray, const Vector &vecInvDelta, const Vector &vecEnd, IPartitionEnumerator* pIterator );
|
|
bool EnumerateElementsAtPoint( SpatialPartitionListMask_t listMask, Voxel_t v, const Vector& pt, IPartitionEnumerator* pIterator );
|
|
|
|
// Inserts/Removes a handle from the tree.
|
|
void InsertIntoTree( SpatialPartitionHandle_t hPartition, Voxel_t voxelMin, Voxel_t voxelMax );
|
|
void RemoveFromTree( SpatialPartitionHandle_t hPartition );
|
|
void UpdateListMask( SpatialPartitionHandle_t hPartition );
|
|
|
|
// Debug!
|
|
void RenderAllObjectsInTree( float flTime );
|
|
void RenderObjectsInPlayerLeafs( const Vector &vecPlayerMin, const Vector &vecPlayerMax, float flTime );
|
|
void RenderVoxel( Voxel_t voxel, float flTime );
|
|
void RenderObjectInVoxel( SpatialPartitionHandle_t hPartition, CPartitionVisitor *pVisitor, float flTime );
|
|
void RenderObjectsInVoxel( Voxel_t voxel, CPartitionVisitor *pVisitor, bool bRenderVoxel, float flTime );
|
|
|
|
// Computes the voxel count in 1 dimension at a particular level of the tree
|
|
static int ComputeVoxelCountAtLevel( int nLevel );
|
|
|
|
// Gets the voxel size for this hash
|
|
int VoxelSize( ) const;
|
|
inline float VoxelSizeF( ) const { return m_flVoxelSize; }
|
|
|
|
int EntityCount();
|
|
|
|
// Rendering methods
|
|
void RenderGrid();
|
|
|
|
// Converts point into voxel index
|
|
inline Voxel_t VoxelIndexFromPoint( const Vector &vecWorldPoint );
|
|
inline void VoxelIndexFromPoint( const Vector &vecWorldPoint, int pPoint[3] );
|
|
|
|
#if defined(_X360) || defined(_PS3)
|
|
inline Voxel_t VoxelIndexFromPoint( const fltx4 &vecWorldPoint );
|
|
inline void VoxelIndexFromPoint( const fltx4 &vecWorldPoint, int pPoint[3] );
|
|
#endif
|
|
|
|
// Setup ray for iteration
|
|
void LeafListRaySetup( const Ray_t &ray, const Vector &vecEnd, const Vector &vecInvDelta, Voxel_t voxel, int *pStep, float *pMax, float *pDelta );
|
|
void LeafListExtrudedRaySetup( const Ray_t &ray, const Vector &vecInvDelta, const Vector &vecMin, const Vector &vecMax, int iVoxelMin[3], int iVoxelMax[3], int *pStep, float *pMin, float *pMax, float *pDelta );
|
|
|
|
// Main enumeration method
|
|
template <class T> bool EnumerateElementsInVoxel( Voxel_t voxel, const T &intersectTest, SpatialPartitionListMask_t listMask, IPartitionEnumerator* pIterator );
|
|
|
|
// Enumeration method when only 1 voxel is ever visited
|
|
template <class T> bool EnumerateElementsInSingleVoxel( Voxel_t voxel, const T &intersectTest, SpatialPartitionListMask_t listMask, IPartitionEnumerator* pIterator );
|
|
|
|
bool EnumerateElementsAlongRay_ExtrudedRaySlice( SpatialPartitionListMask_t listMask, IPartitionEnumerator *pIterator, const CIntersectSweptBox &intersectSweptBox, int voxelMin[3], int voxelMax[3], int iAxis, int *pStep );
|
|
private:
|
|
bool EnumerateElementsAlongRay_Ray( SpatialPartitionListMask_t listMask, const Ray_t &ray, const Vector &vecInvDelta, const Vector &vecEnd, IPartitionEnumerator* pIterator );
|
|
bool EnumerateElementsAlongRay_ExtrudedRay( SpatialPartitionListMask_t listMask, const Ray_t &ray, const Vector &vecInvDelta, const Vector &vecEnd, IPartitionEnumerator* pIterator );
|
|
|
|
inline void PackVoxel( int iX, int iY, int iZ, Voxel_t &voxel );
|
|
|
|
typedef CUtlHashFixed<intp, SPHASH_BUCKET_COUNT, CUtlHashFixedGenericHash<SPHASH_BUCKET_COUNT> > CHashTable;
|
|
|
|
Vector m_vecVoxelOrigin; // Voxel space (hash) origin.
|
|
CHashTable m_aVoxelHash; // Voxel tree (hash) - data = entity list head handle (m_aEntityList)
|
|
int m_nVoxelDelta[3]; // Voxel world - width(Dx), height(Dy), depth(Dz)
|
|
CUtlFixedLinkedList<CSpatialEntry> m_aEntityList; // Pool - Linked list(multilist) of entities per leaf.
|
|
CVoxelTree *m_pTree;
|
|
int m_nLevel;
|
|
float m_flVoxelSize;
|
|
uint m_nLevelShift;
|
|
};
|
|
|
|
class CSpatialPartition;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
class CVoxelTree
|
|
{
|
|
public:
|
|
// constructor, destructor
|
|
CVoxelTree();
|
|
virtual ~CVoxelTree();
|
|
|
|
// Inherited from ISpatialPartition
|
|
virtual void Init( CSpatialPartition *pOwner, int iTree, const Vector& worldmin, const Vector& worldmax );
|
|
|
|
virtual void ElementMoved( SpatialPartitionHandle_t handle, const Vector& mins, const Vector& maxs );
|
|
virtual void EnumerateElementsInBox( SpatialPartitionListMask_t listMask, const Vector& mins, const Vector& maxs, bool coarseTest, IPartitionEnumerator* pIterator );
|
|
virtual void EnumerateElementsInSphere( SpatialPartitionListMask_t listMask, const Vector& origin, float radius, bool coarseTest, IPartitionEnumerator* pIterator );
|
|
virtual void EnumerateElementsAlongRay( SpatialPartitionListMask_t listMask, const Ray_t& ray, bool coarseTest, IPartitionEnumerator* pIterator );
|
|
virtual void EnumerateElementsAtPoint( SpatialPartitionListMask_t listMask, const Vector& pt, bool coarseTest, IPartitionEnumerator* pIterator );
|
|
|
|
virtual void RenderAllObjectsInTree( float flTime );
|
|
virtual void RenderObjectsInPlayerLeafs( const Vector &vecPlayerMin, const Vector &vecPlayerMax, float flTime );
|
|
|
|
virtual void ReportStats( const char *pFileName );
|
|
virtual void DrawDebugOverlays();
|
|
|
|
EntityInfo_t &EntityInfo( SpatialPartitionHandle_t hPartition );
|
|
CLeafList &LeafList();
|
|
|
|
int GetTreeId() const;
|
|
|
|
CPartitionVisits *BeginVisit();
|
|
CPartitionVisits *GetVisits();
|
|
void EndVisit( CPartitionVisits * );
|
|
|
|
// Shut down the allocated memory
|
|
void Shutdown( void );
|
|
|
|
// Insert into the appropriate tree
|
|
void InsertIntoTree( SpatialPartitionHandle_t hPartition, const Vector& mins, const Vector& maxs, bool bReinsert );
|
|
|
|
// Remove from appropriate tree
|
|
void RemoveFromTree( SpatialPartitionHandle_t hPartition );
|
|
void UpdateListMask( SpatialPartitionHandle_t hPartition );
|
|
|
|
void LockForWrite() { m_lock.LockForWrite(); }
|
|
void UnlockWrite() { m_lock.UnlockWrite(); }
|
|
|
|
void LockForRead() { m_lock.LockForRead(); }
|
|
void UnlockRead() { m_lock.UnlockRead(); }
|
|
|
|
// Ray casting
|
|
bool EnumerateElementsAlongRay_Ray( SpatialPartitionListMask_t listMask, const Ray_t &ray, const Vector &vecInvDelta, const Vector &vecEnd, IPartitionEnumerator *pIterator );
|
|
bool EnumerateElementsAlongRay_ExtrudedRay( SpatialPartitionListMask_t listMask,
|
|
const Ray_t &ray, const Vector &vecInvDelta, const Vector &vecEnd, IPartitionEnumerator *pIterator );
|
|
|
|
bool EnumerateRayStartVoxels( SpatialPartitionListMask_t listMask, IPartitionEnumerator *pIterator, CIntersectSweptBox &intersectSweptBox, int voxelBounds[4][2][3] );
|
|
|
|
// Purpose:
|
|
void ComputeSweptRayBounds( const Ray_t &ray, const Vector &vecStartMin, const Vector &vecStartMax, Vector *pVecMin, Vector *pVecMax );
|
|
|
|
private:
|
|
|
|
int m_nLevelCount;
|
|
CVoxelHash* m_pVoxelHash;
|
|
CLeafList m_aLeafList; // Pool - Linked list(multilist) of leaves per entity.
|
|
int m_TreeId;
|
|
CPartitionVisits * m_pVisits[MAX_THREADS_SUPPORTED];
|
|
CSpatialPartition * m_pOwner;
|
|
CUtlVector<unsigned short> m_AvailableVisitBits;
|
|
unsigned short m_nNextVisitBit;
|
|
CTSPool<CPartitionVisits> m_FreeVisits;
|
|
CThreadSpinRWLock m_lock;
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// The spatial partition
|
|
//-----------------------------------------------------------------------------
|
|
|
|
class CSpatialPartition : public ISpatialPartitionInternal
|
|
{
|
|
public:
|
|
CSpatialPartition();
|
|
~CSpatialPartition();
|
|
|
|
enum
|
|
{
|
|
MAX_QUERY_CALLBACK = 3
|
|
};
|
|
|
|
// Inherited from ISpatialPartition
|
|
virtual void Init( const Vector& worldmin, const Vector& worldmax );
|
|
void Shutdown( void );
|
|
|
|
virtual SpatialPartitionHandle_t CreateHandle( IHandleEntity *pHandleEntity );
|
|
virtual SpatialPartitionHandle_t CreateHandle( IHandleEntity *pHandleEntity, SpatialPartitionListMask_t listMask, const Vector& mins, const Vector& maxs );
|
|
virtual void DestroyHandle( SpatialPartitionHandle_t handle );
|
|
|
|
virtual void Insert( SpatialPartitionListMask_t listMask, SpatialPartitionHandle_t handle );
|
|
virtual void Remove( SpatialPartitionListMask_t listMask, SpatialPartitionHandle_t handle );
|
|
virtual void RemoveAndInsert( SpatialPartitionListMask_t removeMask, SpatialPartitionListMask_t insertMask, SpatialPartitionHandle_t handle );
|
|
virtual void Remove( SpatialPartitionHandle_t handle );
|
|
|
|
virtual SpatialTempHandle_t HideElement( SpatialPartitionHandle_t handle );
|
|
virtual void UnhideElement( SpatialPartitionHandle_t handle, SpatialTempHandle_t tempHandle );
|
|
|
|
virtual void InstallQueryCallback( IPartitionQueryCallback *pCallback );
|
|
virtual void RemoveQueryCallback( IPartitionQueryCallback *pCallback );
|
|
|
|
virtual void SuppressLists( SpatialPartitionListMask_t nListMask, bool bSuppress );
|
|
virtual SpatialPartitionListMask_t GetSuppressedLists( void );
|
|
|
|
virtual void RenderLeafsForRayTraceStart( float flTime ) { }
|
|
virtual void RenderLeafsForRayTraceEnd( void ) { }
|
|
virtual void RenderLeafsForHullTraceStart( float flTime ) { }
|
|
virtual void RenderLeafsForHullTraceEnd( void ) { }
|
|
virtual void RenderLeafsForBoxStart( float flTime ) { }
|
|
virtual void RenderLeafsForBoxEnd( void ) { }
|
|
virtual void RenderLeafsForSphereStart( float flTime ) { }
|
|
virtual void RenderLeafsForSphereEnd( void ) { }
|
|
|
|
virtual void RenderObjectsInBox( const Vector &vecMin, const Vector &vecMax, float flTime );
|
|
virtual void RenderObjectsInSphere( const Vector &vecCenter, float flRadius, float flTime );
|
|
virtual void RenderObjectsAlongRay( const Ray_t& ray, float flTime );
|
|
|
|
virtual void ElementMoved( SpatialPartitionHandle_t handle, const Vector& mins, const Vector& maxs );
|
|
virtual void EnumerateElementsInBox( SpatialPartitionListMask_t listMask, const Vector& mins, const Vector& maxs, bool coarseTest, IPartitionEnumerator* pIterator );
|
|
virtual void EnumerateElementsInSphere( SpatialPartitionListMask_t listMask, const Vector& origin, float radius, bool coarseTest, IPartitionEnumerator* pIterator );
|
|
virtual void EnumerateElementsAlongRay( SpatialPartitionListMask_t listMask, const Ray_t& ray, bool coarseTest, IPartitionEnumerator* pIterator );
|
|
virtual void EnumerateElementsAtPoint( SpatialPartitionListMask_t listMask, const Vector& pt, bool coarseTest, IPartitionEnumerator* pIterator );
|
|
|
|
virtual void RenderAllObjectsInTree( float flTime );
|
|
virtual void RenderObjectsInPlayerLeafs( const Vector &vecPlayerMin, const Vector &vecPlayerMax, float flTime );
|
|
virtual void ReportStats( const char *pFileName );
|
|
virtual void DrawDebugOverlays();
|
|
|
|
// Gets entity info (for enumerations).
|
|
EntityInfo_t &EntityInfo( SpatialPartitionHandle_t hPartition );
|
|
|
|
virtual void InsertIntoTree( SpatialPartitionHandle_t hPartition, const Vector& mins, const Vector& maxs );
|
|
virtual void RemoveFromTree( SpatialPartitionHandle_t hPartition );
|
|
|
|
CVoxelTree * VoxelTree( SpatialPartitionListMask_t listMask );
|
|
CVoxelTree * VoxelTreeForHandle( SpatialPartitionHandle_t handle );
|
|
|
|
protected:
|
|
void UpdateListMask( SpatialPartitionHandle_t hPartition, uint16 nListMask );
|
|
// Invokes the pre-query callbacks.
|
|
void InvokeQueryCallbacks( SpatialPartitionListMask_t listMask, bool = false );
|
|
|
|
typedef CUtlLinkedList<EntityInfo_t, SpatialPartitionHandle_t, false, SpatialPartitionHandle_t, CUtlMemoryStack<UtlLinkedListElem_t< EntityInfo_t, SpatialPartitionHandle_t >, SpatialPartitionHandle_t, 0xffff, 1024> > CHandleList;
|
|
|
|
private:
|
|
CHandleList m_aHandles; // Stores all unique elements (1 per entity in tree).
|
|
CThreadFastMutex m_HandlesMutex;
|
|
|
|
CVoxelTree m_VoxelTrees[NUM_TREES];
|
|
|
|
IPartitionQueryCallback *m_pQueryCallback[MAX_QUERY_CALLBACK]; // Query callbacks.
|
|
int m_nQueryCallbackCount; // Number of query callbacks.
|
|
|
|
// Debug!
|
|
SpatialPartitionListMask_t m_nSuppressedListMask;
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Spatial partition inline methods
|
|
//-----------------------------------------------------------------------------
|
|
// Gets entity info (for enumerations).
|
|
inline EntityInfo_t &CSpatialPartition::EntityInfo( SpatialPartitionHandle_t hPartition )
|
|
{
|
|
return m_aHandles[hPartition];
|
|
}
|
|
|
|
inline EntityInfo_t &CVoxelTree::EntityInfo( SpatialPartitionHandle_t hPartition )
|
|
{
|
|
return m_pOwner->EntityInfo( hPartition );
|
|
}
|
|
|
|
inline CLeafList &CVoxelTree::LeafList()
|
|
{
|
|
return m_aLeafList;
|
|
}
|
|
|
|
inline int CVoxelTree::GetTreeId() const
|
|
{
|
|
return m_TreeId;
|
|
}
|
|
|
|
inline CPartitionVisits *CVoxelTree::GetVisits()
|
|
{
|
|
int nThread = g_nThreadID;
|
|
return m_pVisits[nThread];
|
|
}
|
|
|
|
inline CPartitionVisits *CVoxelTree::BeginVisit()
|
|
{
|
|
int nThread = g_nThreadID;
|
|
CPartitionVisits *pPrev = m_pVisits[nThread];
|
|
CPartitionVisits *pVisits = m_FreeVisits.GetObject();
|
|
if ( pVisits->GetNumBits() < m_nNextVisitBit )
|
|
{
|
|
pVisits->Resize( m_nNextVisitBit, true );
|
|
}
|
|
else
|
|
{
|
|
pVisits->ClearAll();
|
|
}
|
|
m_pVisits[g_nThreadID] = pVisits;
|
|
return pPrev;
|
|
}
|
|
|
|
inline void CVoxelTree::EndVisit( CPartitionVisits *pPrev )
|
|
{
|
|
int nThread = g_nThreadID;
|
|
m_FreeVisits.PutObject( m_pVisits[nThread] );
|
|
m_pVisits[nThread] = pPrev;
|
|
}
|
|
|
|
inline CVoxelTree *CSpatialPartition::VoxelTree( SpatialPartitionListMask_t listMask )
|
|
{
|
|
int iTree = ( ( listMask & PARTITION_ALL_CLIENT_EDICTS ) == 0 ) ? SERVER_TREE : CLIENT_TREE;
|
|
return &m_VoxelTrees[iTree];
|
|
}
|
|
|
|
inline CVoxelTree *CSpatialPartition::VoxelTreeForHandle( SpatialPartitionHandle_t handle )
|
|
{
|
|
return VoxelTree( m_aHandles[handle].m_fList );
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Constructor, destructor
|
|
//-----------------------------------------------------------------------------
|
|
CVoxelHash::CVoxelHash( )
|
|
{
|
|
}
|
|
|
|
CVoxelHash::~CVoxelHash()
|
|
{
|
|
Shutdown();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Create a voxel index from a world point - is the hash key.
|
|
// Input: vecWorldPoint - world point to get voxel index for
|
|
// Output: voxel index
|
|
//-----------------------------------------------------------------------------
|
|
inline void CVoxelHash::PackVoxel( int iX, int iY, int iZ, Voxel_t &voxel )
|
|
{
|
|
Assert( ( iX >= -( 1 << 10 ) ) && ( iX <= ( 1 << 10 ) ) );
|
|
Assert( ( iY >= -( 1 << 10 ) ) && ( iY <= ( 1 << 10 ) ) );
|
|
Assert( ( iZ >= -( 1 << 9 ) ) && ( iZ <= ( 1 << 9 ) ) );
|
|
voxel.bitsVoxel.x = iX;
|
|
voxel.bitsVoxel.y = iY;
|
|
voxel.bitsVoxel.z = iZ;
|
|
}
|
|
|
|
|
|
#if defined(_X360) || defined(_PS3)
|
|
|
|
// NOTE: This isn't supportable on SSE but it isn't necessary either
|
|
inline double FloatConvertToIntegerFormat( double flVal )
|
|
{
|
|
#if defined( _PS3 )
|
|
return __builtin_fctiwz( flVal );
|
|
#else
|
|
return __fctiwz( flVal );
|
|
#endif
|
|
}
|
|
|
|
inline fltx4 ConvertToSignedIntegerSIMD( fltx4 fl4Data )
|
|
{
|
|
#if defined(_X360)
|
|
return __vctsxs( fl4Data, 0 ); // NOTE: 0 is power of 2 to scale by
|
|
#else
|
|
return (fltx4)vec_cts( fl4Data, 0 );
|
|
#endif
|
|
}
|
|
|
|
inline fltx4 ShiftRightSIMD( const fltx4 &fl4Data, const fltx4 &fl4Shift )
|
|
{
|
|
#if defined(_X360)
|
|
return __vsrw( fl4Data, fl4Shift );
|
|
#else
|
|
return (fltx4)vec_sr( (u32x4)fl4Data, (u32x4)fl4Shift );
|
|
#endif
|
|
}
|
|
|
|
union doublecnv_t
|
|
{
|
|
double m_flConverted;
|
|
int32 m_nConverted[2];
|
|
};
|
|
|
|
// SIMD Versions - need more code changes to fully support this but there are enough benefits to use it on some of the code
|
|
inline void CVoxelHash::VoxelIndexFromPoint( const fltx4 &fl4WorldPoint, int pPoint[3] )
|
|
{
|
|
fltx4 fl4Shift = (fltx4)ReplicateIX4( m_nLevelShift );
|
|
fltx4 fl4VoxelOrigin = LoadUnaligned3SIMD( m_vecVoxelOrigin.Base() );
|
|
fltx4 fl4LocalOrigin = SubSIMD( fl4WorldPoint, fl4VoxelOrigin );
|
|
fltx4 fl4OriginInt = ConvertToSignedIntegerSIMD( fl4LocalOrigin );
|
|
fl4OriginInt = ShiftRightSIMD( fl4OriginInt, fl4Shift );
|
|
StoreUnaligned3SIMD( (float *)pPoint, fl4OriginInt );
|
|
}
|
|
|
|
inline void CVoxelHash::VoxelIndexFromPoint( const Vector &vecWorldPoint, int pPoint[3] )
|
|
{
|
|
return VoxelIndexFromPoint( LoadUnaligned3SIMD( vecWorldPoint.Base() ), pPoint );
|
|
}
|
|
|
|
|
|
inline Voxel_t CVoxelHash::VoxelIndexFromPoint( const Vector &vecWorldPoint )
|
|
{
|
|
Voxel_t voxel;
|
|
|
|
// This code manually schedules the float->int conversion to avoid LHS on PPC
|
|
// First we convert the float to int format within a float register
|
|
// then we write it back to memory
|
|
volatile union doublecnv_t cnvX, cnvY, cnvZ;
|
|
cnvX.m_flConverted = FloatConvertToIntegerFormat( vecWorldPoint.x - m_vecVoxelOrigin.x );
|
|
cnvY.m_flConverted = FloatConvertToIntegerFormat( vecWorldPoint.y - m_vecVoxelOrigin.y );
|
|
cnvZ.m_flConverted = FloatConvertToIntegerFormat( vecWorldPoint.z - m_vecVoxelOrigin.z );
|
|
// now we load that value back into an integer register. This will LHS if there aren't enough
|
|
// cycles between the stores and the loads but this will allow the compiler to reorder the operations
|
|
// and when the conversions are implicit they don't get reordered (load instruction immediately follows the store)
|
|
int nX = cnvX.m_nConverted[1];
|
|
int nY = cnvY.m_nConverted[1];
|
|
int nZ = cnvZ.m_nConverted[1];
|
|
voxel.bitsVoxel.x = nX >> m_nLevelShift;
|
|
voxel.bitsVoxel.y = nY >> m_nLevelShift;
|
|
voxel.bitsVoxel.z = nZ >> m_nLevelShift;
|
|
|
|
return voxel;
|
|
}
|
|
|
|
inline Voxel_t CVoxelHash::VoxelIndexFromPoint( const fltx4 &fl4WorldPoint )
|
|
{
|
|
Voxel_t voxel;
|
|
|
|
fltx4 fl4Shift = (fltx4)ReplicateIX4( m_nLevelShift );
|
|
fltx4 fl4VoxelOrigin = LoadUnaligned3SIMD( m_vecVoxelOrigin.Base() );
|
|
fltx4 fl4LocalOrigin = SubSIMD( fl4WorldPoint, fl4VoxelOrigin );
|
|
fltx4 fl4OriginInt = ConvertToSignedIntegerSIMD( fl4LocalOrigin );
|
|
fl4OriginInt = ShiftRightSIMD( fl4OriginInt, fl4Shift );
|
|
|
|
// UNDONE: Can probably pack these with shift, permute, or
|
|
int32 ALIGN16 tmp[4];
|
|
StoreAlignedIntSIMD( tmp, fl4OriginInt );
|
|
voxel.bitsVoxel.x = tmp[0];
|
|
voxel.bitsVoxel.y = tmp[1];
|
|
voxel.bitsVoxel.z = tmp[2];
|
|
|
|
return voxel;
|
|
}
|
|
|
|
#else
|
|
|
|
inline void CVoxelHash::VoxelIndexFromPoint( const Vector &vecWorldPoint, int pPoint[3] )
|
|
{
|
|
pPoint[0] = static_cast<int>( vecWorldPoint.x - m_vecVoxelOrigin.x ) >> m_nLevelShift;
|
|
pPoint[1] = static_cast<int>( vecWorldPoint.y - m_vecVoxelOrigin.y ) >> m_nLevelShift;
|
|
pPoint[2] = static_cast<int>( vecWorldPoint.z - m_vecVoxelOrigin.z ) >> m_nLevelShift;
|
|
}
|
|
|
|
|
|
inline Voxel_t CVoxelHash::VoxelIndexFromPoint( const Vector &vecWorldPoint )
|
|
{
|
|
Voxel_t voxel;
|
|
|
|
voxel.bitsVoxel.x = static_cast<int>( vecWorldPoint.x - m_vecVoxelOrigin.x ) >> m_nLevelShift;
|
|
voxel.bitsVoxel.y = static_cast<int>( vecWorldPoint.y - m_vecVoxelOrigin.y ) >> m_nLevelShift;
|
|
voxel.bitsVoxel.z = static_cast<int>( vecWorldPoint.z - m_vecVoxelOrigin.z ) >> m_nLevelShift;
|
|
|
|
return voxel;
|
|
}
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Computes the voxel count at a particular level of the tree
|
|
//-----------------------------------------------------------------------------
|
|
int CVoxelHash::ComputeVoxelCountAtLevel( int nLevel )
|
|
{
|
|
int nVoxelCount = COORD_EXTENT >> SPHASH_VOXEL_SHIFT;
|
|
nVoxelCount >>= ( SPHASH_LEVEL_SKIP * nLevel );
|
|
return ( nVoxelCount > 0 ) ? nVoxelCount : 1;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Gets the voxel size for this hash
|
|
//-----------------------------------------------------------------------------
|
|
inline int CVoxelHash::VoxelSize( ) const
|
|
{
|
|
return SPHASH_VOXEL_SIZE << ( SPHASH_LEVEL_SKIP * m_nLevel );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input: worldmin -
|
|
// worldmax -
|
|
//-----------------------------------------------------------------------------
|
|
void CVoxelHash::Init( CVoxelTree *pPartition, const Vector &worldmin, const Vector &worldmax, int nLevel )
|
|
{
|
|
m_pTree = pPartition;
|
|
m_nLevel = nLevel;
|
|
m_flVoxelSize = VoxelSize();
|
|
m_nLevelShift = ( SPHASH_VOXEL_SHIFT + SPHASH_LEVEL_SKIP * nLevel );
|
|
|
|
// Setup the hash.
|
|
MEM_ALLOC_CREDIT();
|
|
|
|
int nVoxelCount = ComputeVoxelCountAtLevel( nLevel );
|
|
m_vecVoxelOrigin.Init( MIN_COORD_FLOAT, MIN_COORD_FLOAT, MIN_COORD_FLOAT );
|
|
int nHashBucketCount = SPHASH_BUCKET_COUNT >> nLevel;
|
|
if ( nHashBucketCount < 16 )
|
|
{
|
|
nHashBucketCount = 16;
|
|
}
|
|
m_nVoxelDelta[0] = nVoxelCount;
|
|
m_nVoxelDelta[1] = nVoxelCount;
|
|
m_nVoxelDelta[2] = nVoxelCount;
|
|
Assert( ( m_nVoxelDelta[0] >= 0 ) && ( m_nVoxelDelta[0] <= ( 1 << 10 ) ) );
|
|
Assert( ( m_nVoxelDelta[1] >= 0 ) && ( m_nVoxelDelta[1] <= ( 1 << 10 ) ) );
|
|
Assert( ( m_nVoxelDelta[2] >= 0 ) && ( m_nVoxelDelta[2] <= ( 1 << 9 ) ) );
|
|
|
|
m_aVoxelHash.RemoveAll();
|
|
|
|
// Setup the entity list pool.
|
|
int nGrowSize = SPHASH_ENTITYLIST_BLOCK >> nLevel;
|
|
if ( nGrowSize < 16 )
|
|
{
|
|
nGrowSize = 16;
|
|
}
|
|
m_aEntityList.Purge();
|
|
m_aEntityList.SetGrowSize( nGrowSize );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Shutdown
|
|
//-----------------------------------------------------------------------------
|
|
void CVoxelHash::Shutdown( void )
|
|
{
|
|
m_aEntityList.Purge();
|
|
m_aVoxelHash.Purge();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Insert the object into the voxel hash.
|
|
//-----------------------------------------------------------------------------
|
|
void CVoxelHash::InsertIntoTree( SpatialPartitionHandle_t hPartition, Voxel_t voxelMin, Voxel_t voxelMax )
|
|
{
|
|
EntityInfo_t &info = m_pTree->EntityInfo( hPartition );
|
|
CLeafList &leafList = m_pTree->LeafList();
|
|
int treeId = m_pTree->GetTreeId();
|
|
|
|
uint16 nListMask = m_pTree->EntityInfo( hPartition ).m_fList;
|
|
|
|
// Set the voxel level
|
|
info.m_nLevel[m_pTree->GetTreeId()] = m_nLevel;
|
|
|
|
Assert( (m_nLevel == 4) ||
|
|
(voxelMax.bitsVoxel.x - voxelMin.bitsVoxel.x <= 1) &&
|
|
(voxelMax.bitsVoxel.y - voxelMin.bitsVoxel.y <= 1) &&
|
|
(voxelMax.bitsVoxel.z - voxelMin.bitsVoxel.z <= 1) );
|
|
|
|
// Add the object to all the voxels it intersects.
|
|
Voxel_t voxel;
|
|
unsigned int iX, iY, iZ;
|
|
for ( iX = voxelMin.bitsVoxel.x; iX <= voxelMax.bitsVoxel.x; ++iX )
|
|
{
|
|
voxel.bitsVoxel.x = iX;
|
|
for ( iY = voxelMin.bitsVoxel.y; iY <= voxelMax.bitsVoxel.y; ++iY )
|
|
{
|
|
voxel.bitsVoxel.y = iY;
|
|
for ( iZ = voxelMin.bitsVoxel.z; iZ <= voxelMax.bitsVoxel.z; ++iZ )
|
|
{
|
|
voxel.bitsVoxel.z = iZ;
|
|
|
|
#if 0
|
|
// Debug!
|
|
RenderVoxel( voxel );
|
|
#endif
|
|
|
|
// Entity list.
|
|
intp iEntity = m_aEntityList.Alloc( true );
|
|
m_aEntityList[iEntity].m_handle = hPartition;
|
|
m_aEntityList[iEntity].m_nListMask = nListMask;
|
|
|
|
UtlHashFixedHandle_t hHash = m_aVoxelHash.Find( voxel.uiVoxel );
|
|
if ( hHash == m_aVoxelHash.InvalidHandle() )
|
|
{
|
|
// Add voxel(leaf) to hash.
|
|
hHash = m_aVoxelHash.FastInsert( voxel.uiVoxel, iEntity );
|
|
}
|
|
else
|
|
{
|
|
intp iHead = m_aVoxelHash.Element( hHash );
|
|
m_aEntityList.LinkBefore( iHead, iEntity );
|
|
m_aVoxelHash[hHash] = iEntity;
|
|
}
|
|
|
|
// Leaf list.
|
|
intp iLeafList = leafList.Alloc( true );
|
|
leafList[iLeafList].m_hVoxel = hHash;
|
|
leafList[iLeafList].m_iEntity = iEntity;
|
|
|
|
if ( info.m_iLeafList[treeId] == leafList.InvalidIndex() )
|
|
{
|
|
info.m_iLeafList[treeId] = iLeafList;
|
|
}
|
|
else
|
|
{
|
|
intp iHead = info.m_iLeafList[treeId];
|
|
leafList.LinkBefore( iHead, iLeafList );
|
|
info.m_iLeafList[treeId] = iLeafList;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Removes the object into the voxel hash.
|
|
//-----------------------------------------------------------------------------
|
|
void CVoxelHash::RemoveFromTree( SpatialPartitionHandle_t hPartition )
|
|
{
|
|
EntityInfo_t &data = m_pTree->EntityInfo( hPartition );
|
|
CLeafList &leafList = m_pTree->LeafList();
|
|
int treeId = m_pTree->GetTreeId();
|
|
|
|
intp iLeaf = data.m_iLeafList[treeId];
|
|
intp iNext;
|
|
while ( iLeaf != leafList.InvalidIndex() )
|
|
{
|
|
// Get the next voxel - if any.
|
|
iNext = leafList.Next( iLeaf );
|
|
|
|
UtlHashFixedHandle_t hHash = leafList[iLeaf].m_hVoxel;
|
|
if ( hHash == m_aVoxelHash.InvalidHandle() )
|
|
{
|
|
iLeaf = iNext;
|
|
continue;
|
|
}
|
|
|
|
// Get the head of the entity list for the voxel.
|
|
intp iEntity = leafList[iLeaf].m_iEntity;
|
|
intp iEntityHead = m_aVoxelHash[hHash];
|
|
|
|
if ( iEntityHead == iEntity )
|
|
{
|
|
intp iEntityNext = m_aEntityList.Next( iEntityHead );
|
|
if ( iEntityNext == m_aEntityList.InvalidIndex() )
|
|
{
|
|
m_aVoxelHash.Remove( hHash );
|
|
}
|
|
else
|
|
{
|
|
m_aVoxelHash[hHash] = iEntityNext;
|
|
}
|
|
}
|
|
|
|
// Remove the entity from the entity list for the voxel.
|
|
m_aEntityList.Remove( iEntity );
|
|
|
|
// Remove from the leaf list.
|
|
leafList.Remove( iLeaf );
|
|
iLeaf = iNext;
|
|
}
|
|
|
|
data.m_iLeafList[treeId] = leafList.InvalidIndex();
|
|
}
|
|
|
|
void CVoxelHash::UpdateListMask( SpatialPartitionHandle_t hPartition )
|
|
{
|
|
EntityInfo_t &data = m_pTree->EntityInfo( hPartition );
|
|
uint16 nListMask = data.m_fList;
|
|
|
|
Voxel_t vmin = data.m_voxelMin;
|
|
Voxel_t vmax = data.m_voxelMax;
|
|
|
|
// single voxel
|
|
if ( vmin.uiVoxel == vmax.uiVoxel )
|
|
{
|
|
UtlHashFixedHandle_t hHash = m_aVoxelHash.Find( vmin.uiVoxel );
|
|
if ( hHash != m_aVoxelHash.InvalidHandle() )
|
|
{
|
|
for ( intp i = m_aVoxelHash.Element( hHash ); i != m_aEntityList.InvalidIndex(); i = m_aEntityList.Next(i) )
|
|
{
|
|
SpatialPartitionHandle_t handle = m_aEntityList[i].m_handle;
|
|
if ( handle != hPartition )
|
|
continue;
|
|
|
|
m_aEntityList[i].m_nListMask = nListMask;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// spans voxels
|
|
Voxel_t vdelta;
|
|
vdelta.uiVoxel = vmax.uiVoxel - vmin.uiVoxel;
|
|
int cx = vdelta.bitsVoxel.x;
|
|
int cy = vdelta.bitsVoxel.y;
|
|
int cz = vdelta.bitsVoxel.z;
|
|
Voxel_t voxel;
|
|
voxel.bitsVoxel.x = vmin.bitsVoxel.x;
|
|
for ( int iX = 0; iX <= cx; ++iX, ++voxel.bitsVoxel.x )
|
|
{
|
|
voxel.bitsVoxel.y = vmin.bitsVoxel.y;
|
|
for ( int iY = 0; iY <= cy; ++iY, ++voxel.bitsVoxel.y )
|
|
{
|
|
voxel.bitsVoxel.z = vmin.bitsVoxel.z;
|
|
for ( int iZ = 0; iZ <= cz; ++iZ, ++voxel.bitsVoxel.z )
|
|
{
|
|
UtlHashFixedHandle_t hHash = m_aVoxelHash.Find( voxel.uiVoxel );
|
|
if ( hHash != m_aVoxelHash.InvalidHandle() )
|
|
{
|
|
for ( intp i = m_aVoxelHash.Element( hHash ); i != m_aEntityList.InvalidIndex(); i = m_aEntityList.Next(i) )
|
|
{
|
|
if ( m_aEntityList[i].m_handle != hPartition )
|
|
continue;
|
|
|
|
m_aEntityList[i].m_nListMask = nListMask;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
inline void ClampStartPoint( Ray_t &ray, const Vector &vecEnd )
|
|
{
|
|
float flDistStart, flT;
|
|
for ( int iAxis = 0; iAxis < 3; ++iAxis )
|
|
{
|
|
if ( fabs(ray.m_Delta[iAxis]) < 1e-10 )
|
|
continue;
|
|
|
|
if ( ray.m_Delta[iAxis] > 0.0f )
|
|
{
|
|
if ( ray.m_Start[iAxis] < MIN_COORD_FLOAT )
|
|
{
|
|
// Add some bloat inward.
|
|
flDistStart = ( MIN_COORD_FLOAT + 5.0f ) - ray.m_Start[iAxis];
|
|
flT = flDistStart / ray.m_Delta[iAxis];
|
|
VectorMA( ray.m_Start, flT, ray.m_Delta, ray.m_Start );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( ray.m_Start[iAxis] > MAX_COORD_FLOAT )
|
|
{
|
|
// Add some bloat inward.
|
|
flDistStart = ray.m_Start[iAxis] - ( MAX_COORD_FLOAT - 5.0f );
|
|
flT = flDistStart / -ray.m_Delta[iAxis];
|
|
VectorMA( ray.m_Start, flT, ray.m_Delta, ray.m_Start );
|
|
}
|
|
}
|
|
}
|
|
|
|
VectorSubtract( vecEnd, ray.m_Start, ray.m_Delta );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
inline void ClampEndPoint( Ray_t &ray, Vector &vecEnd )
|
|
{
|
|
float flDistStart, flT;
|
|
for ( int iAxis = 0; iAxis < 3; ++iAxis )
|
|
{
|
|
if ( fabs(ray.m_Delta[iAxis]) < 1e-10 )
|
|
continue;
|
|
|
|
if ( ray.m_Delta[iAxis] < 0.0f )
|
|
{
|
|
if ( vecEnd[iAxis] < MIN_COORD_FLOAT )
|
|
{
|
|
// Add some bloat inward.
|
|
flDistStart = ray.m_Start[iAxis] - ( MIN_COORD_FLOAT + 5.0f );
|
|
flT = flDistStart / -ray.m_Delta[iAxis];
|
|
VectorMA( ray.m_Start, flT, ray.m_Delta, vecEnd );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( vecEnd[iAxis] > MAX_COORD_FLOAT )
|
|
{
|
|
// Add some bloat inward.
|
|
flDistStart = ray.m_Start[iAxis] - ( -MAX_COORD_FLOAT + 5.0f );
|
|
flT = flDistStart / ray.m_Delta[iAxis];
|
|
VectorMA( ray.m_Start, flT, ray.m_Delta, vecEnd );
|
|
}
|
|
}
|
|
}
|
|
|
|
VectorSubtract( vecEnd, ray.m_Start, ray.m_Delta );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Intersection classes
|
|
//-----------------------------------------------------------------------------
|
|
class CPartitionVisitor
|
|
{
|
|
public:
|
|
CPartitionVisitor( CVoxelTree *pPartition )
|
|
{
|
|
m_pVisits = pPartition->GetVisits();
|
|
m_iTree = pPartition->GetTreeId();
|
|
}
|
|
|
|
~CPartitionVisitor()
|
|
{
|
|
}
|
|
|
|
bool Visit( SpatialPartitionHandle_t hPartition, EntityInfo_t &hInfo ) const
|
|
{
|
|
int nVisitBit = hInfo.m_nVisitBit[m_iTree];
|
|
if ( m_pVisits->IsBitSet( nVisitBit ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
m_pVisits->Set( nVisitBit );
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
CPartitionVisits *m_pVisits;
|
|
int m_iTree;
|
|
};
|
|
|
|
|
|
class CIntersectPoint : public CPartitionVisitor
|
|
{
|
|
public:
|
|
CIntersectPoint( CVoxelTree *pPartition, const Vector &pt ) : CPartitionVisitor( pPartition )
|
|
{
|
|
m_f4Point = LoadUnaligned3SIMD( pt.Base() );
|
|
}
|
|
|
|
bool Intersects( const float *pMins, const float *pMaxs ) const
|
|
{
|
|
// Ray intersection test
|
|
Assert( pMins[0] <= pMaxs[0] );
|
|
Assert( pMins[1] <= pMaxs[1] );
|
|
Assert( pMins[2] <= pMaxs[2] );
|
|
|
|
return IsPointInBox( m_f4Point, LoadUnaligned3SIMD( pMins ), LoadUnaligned3SIMD(pMaxs) );
|
|
}
|
|
|
|
private:
|
|
fltx4 m_f4Point;
|
|
};
|
|
|
|
|
|
class CIntersectBox : public CPartitionVisitor
|
|
{
|
|
public:
|
|
CIntersectBox( CVoxelTree *pPartition, const Vector &vecMins, const Vector &vecMaxs ) : CPartitionVisitor( pPartition ), m_vecMins( vecMins ), m_vecMaxs( vecMaxs )
|
|
{
|
|
}
|
|
|
|
bool Intersects( const float *pMins, const float *pMaxs ) const
|
|
{
|
|
// Box intersection test
|
|
Assert( pMins[0] <= pMaxs[0] );
|
|
Assert( pMins[1] <= pMaxs[1] );
|
|
Assert( pMins[2] <= pMaxs[2] );
|
|
|
|
return ( pMins[0] <= m_vecMaxs.x ) && ( pMaxs[0] >= m_vecMins.x ) &&
|
|
( pMins[1] <= m_vecMaxs.y ) && ( pMaxs[1] >= m_vecMins.y ) &&
|
|
( pMins[2] <= m_vecMaxs.z ) && ( pMaxs[2] >= m_vecMins.z );
|
|
}
|
|
|
|
private:
|
|
const Vector &m_vecMins;
|
|
const Vector &m_vecMaxs;
|
|
};
|
|
|
|
class CIntersectRay : public CPartitionVisitor
|
|
{
|
|
public:
|
|
CIntersectRay( CVoxelTree *pPartition, const Ray_t &ray, const Vector &vecInvDelta ) : CPartitionVisitor( pPartition )
|
|
{
|
|
m_f4Start = LoadUnaligned3SIMD( ray.m_Start.Base() );
|
|
m_f4Delta = LoadUnaligned3SIMD( ray.m_Delta.Base() );
|
|
m_f4InvDelta = LoadUnaligned3SIMD( vecInvDelta.Base() );
|
|
}
|
|
|
|
bool Intersects( const float *pMins, const float *pMaxs ) const
|
|
{
|
|
// Ray intersection test
|
|
Assert( pMins[0] <= pMaxs[0] );
|
|
Assert( pMins[1] <= pMaxs[1] );
|
|
Assert( pMins[2] <= pMaxs[2] );
|
|
|
|
fltx4 f4Mins = LoadUnaligned3SIMD( pMins );
|
|
fltx4 f4Maxs = LoadUnaligned3SIMD( pMaxs );
|
|
return IsBoxIntersectingRay( f4Mins, f4Maxs, m_f4Start, m_f4Delta, m_f4InvDelta );
|
|
}
|
|
|
|
private:
|
|
fltx4 m_f4Start;
|
|
fltx4 m_f4Delta;
|
|
fltx4 m_f4InvDelta;
|
|
};
|
|
|
|
|
|
class CIntersectSweptBox : public CPartitionVisitor
|
|
{
|
|
public:
|
|
CIntersectSweptBox( CVoxelTree *pPartition, const Ray_t &ray, const Vector &vecInvDelta ) : CPartitionVisitor( pPartition )
|
|
{
|
|
m_f4Start = LoadUnaligned3SIMD( ray.m_Start.Base() );
|
|
m_f4Delta = LoadUnaligned3SIMD( ray.m_Delta.Base() );
|
|
m_f4InvDelta = LoadUnaligned3SIMD( vecInvDelta.Base() );
|
|
m_f4Extents = LoadUnaligned3SIMD( ray.m_Extents.Base() );
|
|
}
|
|
|
|
bool Intersects( const float *pMins, const float *pMaxs ) const
|
|
{
|
|
// Swept box intersection test
|
|
Assert( pMins[0] <= pMaxs[0] );
|
|
Assert( pMins[1] <= pMaxs[1] );
|
|
Assert( pMins[2] <= pMaxs[2] );
|
|
|
|
fltx4 f4Mins = LoadUnaligned3SIMD( pMins );
|
|
fltx4 f4Maxs = LoadUnaligned3SIMD( pMaxs );
|
|
|
|
// Does the ray intersect the box?
|
|
return IsBoxIntersectingRay( SubSIMD(f4Mins, m_f4Extents), AddSIMD(f4Maxs, m_f4Extents), m_f4Start, m_f4Delta, m_f4InvDelta );
|
|
}
|
|
|
|
private:
|
|
fltx4 m_f4Start;
|
|
fltx4 m_f4Delta;
|
|
fltx4 m_f4InvDelta;
|
|
fltx4 m_f4Extents;
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
template <class T>
|
|
bool CVoxelHash::EnumerateElementsInVoxel( Voxel_t voxel, const T &intersectTest, SpatialPartitionListMask_t listMask, IPartitionEnumerator* pIterator )
|
|
{
|
|
// If the voxel doesn't exist, nothing to iterate over
|
|
UtlHashFixedHandle_t hHash = m_aVoxelHash.Find( voxel.uiVoxel );
|
|
if ( hHash == m_aVoxelHash.InvalidHandle() )
|
|
return true;
|
|
|
|
for ( intp i = m_aVoxelHash.Element( hHash ); i != m_aEntityList.InvalidIndex(); i = m_aEntityList.Next(i) )
|
|
{
|
|
SpatialPartitionHandle_t handle = m_aEntityList[i].m_handle;
|
|
SpatialPartitionListMask_t nListMask = m_aEntityList[i].m_nListMask;
|
|
if ( handle == PARTITION_INVALID_HANDLE )
|
|
continue;
|
|
|
|
// Keep going if this dude isn't in the list
|
|
if ( !( listMask & nListMask ) )
|
|
continue;
|
|
|
|
EntityInfo_t &hInfo = m_pTree->EntityInfo( handle );
|
|
Assert( hInfo.m_fList == nListMask );
|
|
|
|
if ( hInfo.m_flags & ENTITY_HIDDEN )
|
|
continue;
|
|
|
|
// Has this handle already been visited?
|
|
if ( !intersectTest.Visit( handle, hInfo ) )
|
|
continue;
|
|
|
|
// Intersection test
|
|
if ( !intersectTest.Intersects( hInfo.m_vecMin.Base(), hInfo.m_vecMax.Base() ) )
|
|
continue;
|
|
|
|
// Okay, this one is good...
|
|
if ( pIterator->EnumElement( hInfo.m_pHandleEntity ) == ITERATION_STOP )
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Enumeration method when only 1 voxel is ever visited
|
|
//-----------------------------------------------------------------------------
|
|
template <class T>
|
|
bool CVoxelHash::EnumerateElementsInSingleVoxel( Voxel_t voxel, const T &intersectTest,
|
|
SpatialPartitionListMask_t listMask, IPartitionEnumerator* pIterator )
|
|
{
|
|
// NOTE: We don't have to do the enum id checking, nor do we have to up the
|
|
// nesting level, since this only visits 1 voxel.
|
|
intp iEntityList;
|
|
UtlHashFixedHandle_t hHash = m_aVoxelHash.Find( voxel.uiVoxel );
|
|
if ( hHash != m_aVoxelHash.InvalidHandle() )
|
|
{
|
|
iEntityList = m_aVoxelHash.Element( hHash );
|
|
while ( iEntityList != m_aEntityList.InvalidIndex() )
|
|
{
|
|
SpatialPartitionHandle_t handle = m_aEntityList[iEntityList].m_handle;
|
|
SpatialPartitionListMask_t nListMask = m_aEntityList[iEntityList].m_nListMask;
|
|
iEntityList = m_aEntityList.Next( iEntityList );
|
|
if ( handle == PARTITION_INVALID_HANDLE )
|
|
continue;
|
|
|
|
// Keep going if this dude isn't in the list
|
|
if ( !( listMask & nListMask ) )
|
|
continue;
|
|
|
|
EntityInfo_t &hInfo = m_pTree->EntityInfo( handle );
|
|
if ( hInfo.m_flags & ENTITY_HIDDEN )
|
|
continue;
|
|
|
|
// Keep going if there's no collision
|
|
if ( !intersectTest.Intersects( hInfo.m_vecMin.Base(), hInfo.m_vecMax.Base() ) )
|
|
continue;
|
|
|
|
// Okay, this one is good...
|
|
if ( pIterator->EnumElement( hInfo.m_pHandleEntity ) == ITERATION_STOP )
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
bool CVoxelHash::EnumerateElementsInBox( SpatialPartitionListMask_t listMask,
|
|
Voxel_t vmin, Voxel_t vmax, const Vector& mins, const Vector& maxs, IPartitionEnumerator* pIterator )
|
|
{
|
|
VPROF( "BoxTest/SphereTest" );
|
|
|
|
Assert( mins.x <= maxs.x );
|
|
Assert( mins.y <= maxs.y );
|
|
Assert( mins.z <= maxs.z );
|
|
|
|
// Create the intersection object
|
|
bool bSingleVoxel = ( vmin.uiVoxel == vmax.uiVoxel );
|
|
CIntersectBox rect( m_pTree, mins, maxs );
|
|
|
|
// In the same voxel
|
|
if ( bSingleVoxel )
|
|
return EnumerateElementsInSingleVoxel( vmin, rect, listMask, pIterator );
|
|
|
|
// Iterate over all voxels
|
|
Voxel_t vdelta;
|
|
vdelta.uiVoxel = vmax.uiVoxel - vmin.uiVoxel;
|
|
int cx = vdelta.bitsVoxel.x;
|
|
int cy = vdelta.bitsVoxel.y;
|
|
int cz = vdelta.bitsVoxel.z;
|
|
|
|
// Hijack what can feel like infinite iteration over voxels
|
|
#if defined( _GAMECONSOLE ) && defined( _DEBUG )
|
|
if ( uint64( cx ) * uint64( cy ) * uint64( cz ) > 10000ull )
|
|
{
|
|
Assert( !"CVoxelHash::EnumerateElementsInBox: box too large" );
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
Voxel_t voxel;
|
|
voxel.bitsVoxel.x = vmin.bitsVoxel.x;
|
|
for ( int iX = 0; iX <= cx; ++iX, ++voxel.bitsVoxel.x )
|
|
{
|
|
voxel.bitsVoxel.y = vmin.bitsVoxel.y;
|
|
for ( int iY = 0; iY <= cy; ++iY, ++voxel.bitsVoxel.y )
|
|
{
|
|
voxel.bitsVoxel.z = vmin.bitsVoxel.z;
|
|
for ( int iZ = 0; iZ <= cz; ++iZ, ++voxel.bitsVoxel.z )
|
|
{
|
|
if ( !EnumerateElementsInVoxel( voxel, rect, listMask, pIterator ) )
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
bool CVoxelHash::EnumerateElementsAlongRay( SpatialPartitionListMask_t listMask,
|
|
const Ray_t &ray, const Vector &vecInvDelta, const Vector &vecEnd, IPartitionEnumerator *pIterator )
|
|
{
|
|
Assert( ray.m_IsSwept );
|
|
|
|
// Two different methods
|
|
if ( ray.m_IsRay )
|
|
return EnumerateElementsAlongRay_Ray( listMask, ray, vecInvDelta, vecEnd, pIterator );
|
|
return EnumerateElementsAlongRay_ExtrudedRay( listMask, ray, vecInvDelta, vecEnd, pIterator );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
bool CVoxelHash::EnumerateElementsAlongRay_Ray( SpatialPartitionListMask_t listMask,
|
|
const Ray_t &ray, const Vector &vecInvDelta, const Vector &vecEnd, IPartitionEnumerator *pIterator )
|
|
{
|
|
#ifdef _DEBUG
|
|
// For drawing debug objects.
|
|
static int nRenderRayEnumId = 4;
|
|
#endif
|
|
|
|
// Find the voxel start + end
|
|
Voxel_t voxelStart = VoxelIndexFromPoint( ray.m_Start );
|
|
Voxel_t voxelEnd = VoxelIndexFromPoint( vecEnd );
|
|
|
|
bool bSingleVoxel = ( voxelStart.uiVoxel == voxelEnd.uiVoxel );
|
|
CIntersectRay intersectRay( m_pTree, ray, vecInvDelta );
|
|
|
|
// Optimization: Look for single voxel rays
|
|
if ( bSingleVoxel )
|
|
return EnumerateElementsInSingleVoxel( voxelStart, intersectRay, listMask, pIterator );
|
|
|
|
Voxel_t voxelCurrent;
|
|
voxelCurrent.uiVoxel = voxelStart.uiVoxel;
|
|
|
|
// Setup.
|
|
int nStep[3];
|
|
float tMax[3];
|
|
float tDelta[3];
|
|
LeafListRaySetup( ray, vecEnd, vecInvDelta, voxelStart, nStep, tMax, tDelta );
|
|
|
|
// Walk the voxels and visit all elements in each voxel
|
|
while ( 1 )
|
|
{
|
|
if ( !EnumerateElementsInVoxel( voxelCurrent, intersectRay, listMask, pIterator ) )
|
|
return false;
|
|
|
|
if ( tMax[0] >= 1.0f && tMax[1] >= 1.0f && tMax[2] >= 1.0f )
|
|
break;
|
|
|
|
if ( tMax[0] < tMax[1] )
|
|
{
|
|
if ( tMax[0] < tMax[2] )
|
|
{
|
|
voxelCurrent.bitsVoxel.x += nStep[0];
|
|
tMax[0] += tDelta[0];
|
|
}
|
|
else
|
|
{
|
|
voxelCurrent.bitsVoxel.z += nStep[2];
|
|
tMax[2] += tDelta[2];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( tMax[1] < tMax[2] )
|
|
{
|
|
voxelCurrent.bitsVoxel.y += nStep[1];
|
|
tMax[1] += tDelta[1];
|
|
}
|
|
else
|
|
{
|
|
voxelCurrent.bitsVoxel.z += nStep[2];
|
|
tMax[2] += tDelta[2];
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CVoxelHash::LeafListRaySetup( const Ray_t &ray, const Vector &vecEnd,
|
|
const Vector &vecInvDelta, Voxel_t voxel, int *pStep, float *pMax, float *pDelta )
|
|
{
|
|
int iVoxel[3];
|
|
iVoxel[0] = voxel.bitsVoxel.x;
|
|
iVoxel[1] = voxel.bitsVoxel.y;
|
|
iVoxel[2] = voxel.bitsVoxel.z;
|
|
|
|
// Setup.
|
|
float flDist, flDistStart, flDistEnd, flRecipDist;
|
|
Vector vecVoxelStart, vecVoxelEnd;
|
|
VectorSubtract( ray.m_Start, m_vecVoxelOrigin, vecVoxelStart );
|
|
VectorSubtract( vecEnd, m_vecVoxelOrigin, vecVoxelEnd );
|
|
|
|
for ( int iAxis = 0; iAxis < 3; ++iAxis )
|
|
{
|
|
if ( vecVoxelStart[iAxis] == vecVoxelEnd[iAxis] )
|
|
{
|
|
pStep[iAxis] = 0;
|
|
pMax[iAxis] = SPHASH_VOXEL_LARGE;
|
|
pDelta[iAxis] = SPHASH_VOXEL_LARGE;
|
|
continue;
|
|
}
|
|
|
|
if ( ray.m_Delta[iAxis] < 0.0f )
|
|
{
|
|
pStep[iAxis] = -1;
|
|
flDist = ( iVoxel[iAxis] ) * VoxelSize();
|
|
flDistStart = vecVoxelStart[iAxis] - flDist;
|
|
flDistEnd = vecVoxelEnd[iAxis] - flDist;
|
|
flRecipDist = -vecInvDelta[iAxis];
|
|
}
|
|
else
|
|
{
|
|
pStep[iAxis] = 1;
|
|
flDist = ( iVoxel[iAxis] + 1 ) * -VoxelSize();
|
|
flDistStart = -vecVoxelStart[iAxis] - flDist;
|
|
flDistEnd = -vecVoxelEnd[iAxis] - flDist;
|
|
flRecipDist = vecInvDelta[iAxis];
|
|
}
|
|
|
|
if ( ( flDistStart > 0.0f ) && ( flDistEnd > 0.0f ) )
|
|
{
|
|
pMax[iAxis] = SPHASH_VOXEL_LARGE;
|
|
pDelta[iAxis] = SPHASH_VOXEL_LARGE;
|
|
}
|
|
else
|
|
{
|
|
pMax[iAxis] = flDistStart * flRecipDist;
|
|
pDelta[iAxis] = VoxelSize() * flRecipDist;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Computes the min index of 3 numbers
|
|
//-----------------------------------------------------------------------------
|
|
inline int MinIndex( float v1, float v2, float v3 )
|
|
{
|
|
if ( v1 < v2 )
|
|
return ( v1 < v3 ) ? 0 : 2;
|
|
return ( v2 < v3 ) ? 1 : 2;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
bool CVoxelHash::EnumerateElementsAlongRay_ExtrudedRay( SpatialPartitionListMask_t listMask,
|
|
const Ray_t &ray, const Vector &vecInvDelta, const Vector &vecEnd, IPartitionEnumerator *pIterator )
|
|
{
|
|
// Check the starting position, then proceed with the sweep.
|
|
Vector vecMin, vecMax;
|
|
VectorSubtract( ray.m_Start, ray.m_Extents, vecMin );
|
|
VectorAdd( ray.m_Start, ray.m_Extents, vecMax );
|
|
|
|
// Visit each voxel in the box and enumerate its elements.
|
|
int voxelMin[3], voxelMax[3];
|
|
VoxelIndexFromPoint( vecMin, voxelMin );
|
|
VoxelIndexFromPoint( vecMax, voxelMax );
|
|
|
|
CIntersectSweptBox intersectSweptBox( m_pTree, ray, vecInvDelta );
|
|
|
|
// Iterate over all voxels that intersect the box around the starting ray point
|
|
Voxel_t voxel;
|
|
int iX, iY, iZ;
|
|
for ( iX = voxelMin[0]; iX <= voxelMax[0]; ++iX )
|
|
{
|
|
voxel.bitsVoxel.x = iX;
|
|
for ( iY = voxelMin[1]; iY <= voxelMax[1]; ++iY )
|
|
{
|
|
voxel.bitsVoxel.y = iY;
|
|
for ( iZ = voxelMin[2]; iZ <= voxelMax[2]; ++iZ )
|
|
{
|
|
voxel.bitsVoxel.z = iZ;
|
|
if ( !EnumerateElementsInVoxel( voxel, intersectSweptBox, listMask, pIterator ) )
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Early out: Check to see if the range of voxels at the endpoint
|
|
// is the same as the range at the start point. If so, we're done.
|
|
Vector vecEndMin, vecEndMax;
|
|
VectorSubtract( vecEnd, ray.m_Extents, vecEndMin );
|
|
VectorAdd( vecEnd, ray.m_Extents, vecEndMax );
|
|
|
|
int endVoxelMin[3], endVoxelMax[3];
|
|
VoxelIndexFromPoint( vecEndMin, endVoxelMin );
|
|
VoxelIndexFromPoint( vecEndMax, endVoxelMax );
|
|
if ( (endVoxelMin[0] >= voxelMin[0]) && (endVoxelMin[1] >= voxelMin[1]) && (endVoxelMin[2] >= voxelMin[2]) &&
|
|
(endVoxelMax[0] <= voxelMax[0]) && (endVoxelMax[1] <= voxelMax[1]) && (endVoxelMax[2] <= voxelMax[2]) )
|
|
return true;
|
|
|
|
// Setup.
|
|
int nStep[3];
|
|
float tMax[3]; // amount of change in t along ray until we hit the next new voxel
|
|
float tMin[3]; // amount of change in t along ray until we leave the last voxel
|
|
float tDelta[3];
|
|
LeafListExtrudedRaySetup( ray, vecInvDelta, vecMin, vecMax, voxelMin, voxelMax, nStep, tMin, tMax, tDelta );
|
|
|
|
// Walk the voxels and create the leaf list.
|
|
int iAxis, iMinAxis;
|
|
while ( tMax[0] < 1.0f || tMax[1] < 1.0f || tMax[2] < 1.0f )
|
|
{
|
|
iAxis = MinIndex( tMax[0], tMax[1], tMax[2] );
|
|
iMinAxis = MinIndex( tMin[0], tMin[1], tMin[2] );
|
|
|
|
if ( tMin[iMinAxis] < tMax[iAxis] )
|
|
{
|
|
tMin[iMinAxis] += tDelta[iMinAxis];
|
|
if ( nStep[iMinAxis] > 0 )
|
|
{
|
|
voxelMin[iMinAxis] += nStep[iMinAxis];
|
|
}
|
|
else
|
|
{
|
|
voxelMax[iMinAxis] += nStep[iMinAxis];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
tMax[iAxis] += tDelta[iAxis];
|
|
if ( nStep[iAxis] > 0 )
|
|
{
|
|
voxelMax[iAxis] += nStep[iAxis];
|
|
}
|
|
else
|
|
{
|
|
voxelMin[iAxis] += nStep[iAxis];
|
|
}
|
|
|
|
if ( !EnumerateElementsAlongRay_ExtrudedRaySlice( listMask, pIterator, intersectSweptBox, voxelMin, voxelMax, iAxis, nStep ) )
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CVoxelHash::LeafListExtrudedRaySetup( const Ray_t &ray, const Vector &vecInvDelta,
|
|
const Vector &vecMin, const Vector &vecMax,
|
|
int iVoxelMin[3], int iVoxelMax[3],
|
|
int *pStep, float *pMin, float *pMax, float *pDelta )
|
|
{
|
|
float flDist, flDistStart, flRecipDist, flDistMinStart;
|
|
|
|
Vector vecVoxelMin, vecVoxelMax;
|
|
VectorSubtract( vecMin, m_vecVoxelOrigin, vecVoxelMin );
|
|
VectorSubtract( vecMax, m_vecVoxelOrigin, vecVoxelMax );
|
|
|
|
for ( int iAxis = 0; iAxis < 3; ++iAxis )
|
|
{
|
|
if ( ray.m_Delta[iAxis] == 0.0f )
|
|
{
|
|
pMax[iAxis] = SPHASH_VOXEL_LARGE;
|
|
pMin[iAxis] = SPHASH_VOXEL_LARGE;
|
|
pDelta[iAxis] = SPHASH_VOXEL_LARGE;
|
|
continue;
|
|
}
|
|
|
|
if ( ray.m_Delta[iAxis] < 0.0f )
|
|
{
|
|
pStep[iAxis] = -1;
|
|
flDistStart = vecVoxelMin[iAxis] - ( ( iVoxelMin[iAxis] ) * VoxelSize() );
|
|
flDistMinStart = vecVoxelMax[iAxis] - ( ( iVoxelMax[iAxis] ) * VoxelSize() );
|
|
flDist = -ray.m_Delta[iAxis];
|
|
flRecipDist = -vecInvDelta[iAxis];
|
|
}
|
|
else
|
|
{
|
|
pStep[iAxis] = 1;
|
|
flDistStart = -vecVoxelMax[iAxis] - ( ( iVoxelMax[iAxis] + 1 ) * -VoxelSize() );
|
|
flDistMinStart = -vecVoxelMin[iAxis] - ( ( iVoxelMin[iAxis] + 1 ) * -VoxelSize() );
|
|
flDist = ray.m_Delta[iAxis];
|
|
flRecipDist = vecInvDelta[iAxis];
|
|
}
|
|
|
|
if ( flDistStart > flDist )
|
|
{
|
|
pMax[iAxis] = SPHASH_VOXEL_LARGE;
|
|
pDelta[iAxis] = SPHASH_VOXEL_LARGE;
|
|
}
|
|
else
|
|
{
|
|
pMax[iAxis] = flDistStart * flRecipDist;
|
|
pDelta[iAxis] = VoxelSize() * flRecipDist;
|
|
}
|
|
|
|
if ( flDistMinStart > flDist )
|
|
{
|
|
pMin[iAxis] = SPHASH_VOXEL_LARGE;
|
|
}
|
|
else
|
|
{
|
|
pMin[iAxis] = flDistMinStart * flRecipDist;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
bool CVoxelHash::EnumerateElementsAlongRay_ExtrudedRaySlice( SpatialPartitionListMask_t listMask,
|
|
IPartitionEnumerator *pIterator, const CIntersectSweptBox &intersectSweptBox,
|
|
int voxelMin[3], int voxelMax[3], int iAxis, int *pStep )
|
|
{
|
|
int mins[3] = { voxelMin[0], voxelMin[1], voxelMin[2] };
|
|
int maxs[3] = { voxelMax[0], voxelMax[1], voxelMax[2] };
|
|
if ( pStep[iAxis] < 0.0f )
|
|
{
|
|
maxs[iAxis] = mins[iAxis];
|
|
}
|
|
else
|
|
{
|
|
mins[iAxis] = maxs[iAxis];
|
|
}
|
|
|
|
// Create leaf cache.
|
|
Voxel_t voxel;
|
|
int iX, iY, iZ;
|
|
for ( iX = mins[0]; iX <= maxs[0]; ++iX )
|
|
{
|
|
voxel.bitsVoxel.x = iX;
|
|
for ( iY = mins[1]; iY <= maxs[1]; ++iY )
|
|
{
|
|
voxel.bitsVoxel.y = iY;
|
|
for ( iZ = mins[2]; iZ <= maxs[2]; ++iZ )
|
|
{
|
|
voxel.bitsVoxel.z = iZ;
|
|
if ( !EnumerateElementsInVoxel( voxel, intersectSweptBox, listMask, pIterator ) )
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
bool CVoxelHash::EnumerateElementsAtPoint( SpatialPartitionListMask_t listMask,
|
|
Voxel_t v, const Vector& pt, IPartitionEnumerator* pIterator )
|
|
{
|
|
// NOTE: We don't have to do the enum id checking, nor do we have to up the
|
|
// nesting level, since this only visits 1 voxel.
|
|
intp iEntityList;
|
|
UtlHashFixedHandle_t hHash = m_aVoxelHash.Find( v.uiVoxel );
|
|
if ( hHash != m_aVoxelHash.InvalidHandle() )
|
|
{
|
|
iEntityList = m_aVoxelHash.Element( hHash );
|
|
while ( iEntityList != m_aEntityList.InvalidIndex() )
|
|
{
|
|
SpatialPartitionHandle_t handle = m_aEntityList[iEntityList].m_handle;
|
|
SpatialPartitionListMask_t nListMask = m_aEntityList[iEntityList].m_nListMask;
|
|
iEntityList = m_aEntityList.Next( iEntityList );
|
|
if ( handle == PARTITION_INVALID_HANDLE )
|
|
continue;
|
|
|
|
// Keep going if this dude isn't in the list
|
|
if ( !( listMask & nListMask ) )
|
|
continue;
|
|
|
|
EntityInfo_t &hInfo = m_pTree->EntityInfo( handle );
|
|
if ( hInfo.m_flags & ENTITY_HIDDEN )
|
|
continue;
|
|
|
|
// Keep going if there's no collision
|
|
if ( !IsPointInBox( pt, hInfo.m_vecMin, hInfo.m_vecMax ) )
|
|
continue;
|
|
|
|
// Okay, this one is good...
|
|
if ( pIterator->EnumElement( hInfo.m_pHandleEntity ) == ITERATION_STOP )
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Debug! Render a voxel - blue.
|
|
//-----------------------------------------------------------------------------
|
|
void CVoxelHash::RenderVoxel( Voxel_t voxel, float flTime )
|
|
{
|
|
#ifndef DEDICATED
|
|
Vector vecMin, vecMax;
|
|
vecMin.x = ( voxel.bitsVoxel.x * VoxelSize() ) + m_vecVoxelOrigin.x;
|
|
vecMin.y = ( voxel.bitsVoxel.y * VoxelSize() ) + m_vecVoxelOrigin.y;
|
|
vecMin.z = ( voxel.bitsVoxel.z * VoxelSize() ) + m_vecVoxelOrigin.z;
|
|
|
|
vecMax.x = ( ( voxel.bitsVoxel.x + 1 ) * VoxelSize() ) + m_vecVoxelOrigin.x;
|
|
vecMax.y = ( ( voxel.bitsVoxel.y + 1 ) * VoxelSize() ) + m_vecVoxelOrigin.y;
|
|
vecMax.z = ( ( voxel.bitsVoxel.z + 1 ) * VoxelSize() ) + m_vecVoxelOrigin.z;
|
|
|
|
CDebugOverlay::AddBoxOverlay( vec3_origin, vecMin, vecMax, vec3_angle, s_pVoxelColor[m_nLevel][0], s_pVoxelColor[m_nLevel][1], s_pVoxelColor[m_nLevel][2], 30, flTime );
|
|
|
|
// Add outline.
|
|
Vector vecPoints[8];
|
|
vecPoints[0].Init( vecMin.x, vecMin.y, vecMin.z );
|
|
vecPoints[1].Init( vecMin.x, vecMax.y, vecMin.z );
|
|
vecPoints[2].Init( vecMax.x, vecMax.y, vecMin.z );
|
|
vecPoints[3].Init( vecMax.x, vecMin.y, vecMin.z );
|
|
vecPoints[4].Init( vecMin.x, vecMin.y, vecMax.z );
|
|
vecPoints[5].Init( vecMin.x, vecMax.y, vecMax.z );
|
|
vecPoints[6].Init( vecMax.x, vecMax.y, vecMax.z );
|
|
vecPoints[7].Init( vecMax.x, vecMin.y, vecMax.z );
|
|
|
|
CDebugOverlay::AddLineOverlay( vecPoints[0], vecPoints[1], s_pVoxelColor[m_nLevel][0], s_pVoxelColor[m_nLevel][1], s_pVoxelColor[m_nLevel][2], 255, true, flTime );
|
|
CDebugOverlay::AddLineOverlay( vecPoints[1], vecPoints[2], s_pVoxelColor[m_nLevel][0], s_pVoxelColor[m_nLevel][1], s_pVoxelColor[m_nLevel][2], 255, true, flTime );
|
|
CDebugOverlay::AddLineOverlay( vecPoints[2], vecPoints[3], s_pVoxelColor[m_nLevel][0], s_pVoxelColor[m_nLevel][1], s_pVoxelColor[m_nLevel][2], 255, true, flTime );
|
|
CDebugOverlay::AddLineOverlay( vecPoints[3], vecPoints[0], s_pVoxelColor[m_nLevel][0], s_pVoxelColor[m_nLevel][1], s_pVoxelColor[m_nLevel][2], 255, true, flTime );
|
|
|
|
CDebugOverlay::AddLineOverlay( vecPoints[4], vecPoints[5], s_pVoxelColor[m_nLevel][0], s_pVoxelColor[m_nLevel][1], s_pVoxelColor[m_nLevel][2], 255, true, flTime );
|
|
CDebugOverlay::AddLineOverlay( vecPoints[5], vecPoints[6], s_pVoxelColor[m_nLevel][0], s_pVoxelColor[m_nLevel][1], s_pVoxelColor[m_nLevel][2], 255, true, flTime );
|
|
CDebugOverlay::AddLineOverlay( vecPoints[6], vecPoints[7], s_pVoxelColor[m_nLevel][0], s_pVoxelColor[m_nLevel][1], s_pVoxelColor[m_nLevel][2], 255, true, flTime );
|
|
CDebugOverlay::AddLineOverlay( vecPoints[7], vecPoints[4], s_pVoxelColor[m_nLevel][0], s_pVoxelColor[m_nLevel][1], s_pVoxelColor[m_nLevel][2], 255, true, flTime );
|
|
|
|
CDebugOverlay::AddLineOverlay( vecPoints[0], vecPoints[4], s_pVoxelColor[m_nLevel][0], s_pVoxelColor[m_nLevel][1], s_pVoxelColor[m_nLevel][2], 255, true, flTime );
|
|
CDebugOverlay::AddLineOverlay( vecPoints[3], vecPoints[7], s_pVoxelColor[m_nLevel][0], s_pVoxelColor[m_nLevel][1], s_pVoxelColor[m_nLevel][2], 255, true, flTime );
|
|
CDebugOverlay::AddLineOverlay( vecPoints[1], vecPoints[5], s_pVoxelColor[m_nLevel][0], s_pVoxelColor[m_nLevel][1], s_pVoxelColor[m_nLevel][2], 255, true, flTime );
|
|
CDebugOverlay::AddLineOverlay( vecPoints[2], vecPoints[6], s_pVoxelColor[m_nLevel][0], s_pVoxelColor[m_nLevel][1], s_pVoxelColor[m_nLevel][2], 255, true, flTime );
|
|
#endif
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Debug! Render an object in a voxel - green.
|
|
//-----------------------------------------------------------------------------
|
|
void CVoxelHash::RenderObjectInVoxel( SpatialPartitionHandle_t hPartition, CPartitionVisitor *pVisitor, float flTime )
|
|
{
|
|
#ifndef DEDICATED
|
|
// Add outline.
|
|
if ( hPartition == PARTITION_INVALID_HANDLE )
|
|
return;
|
|
|
|
EntityInfo_t &info = m_pTree->EntityInfo( hPartition );
|
|
if ( !pVisitor->Visit( hPartition, info ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
CDebugOverlay::AddBoxOverlay( vec3_origin, info.m_vecMin, info.m_vecMax, vec3_angle, s_pVoxelColor[m_nLevel][0], s_pVoxelColor[m_nLevel][1], s_pVoxelColor[m_nLevel][2], 75, flTime );
|
|
|
|
// Add outline.
|
|
Vector vecMin, vecMax;
|
|
vecMin = info.m_vecMin;
|
|
vecMax = info.m_vecMax;
|
|
|
|
Vector vecPoints[8];
|
|
vecPoints[0].Init( vecMin.x, vecMin.y, vecMin.z );
|
|
vecPoints[1].Init( vecMin.x, vecMax.y, vecMin.z );
|
|
vecPoints[2].Init( vecMax.x, vecMax.y, vecMin.z );
|
|
vecPoints[3].Init( vecMax.x, vecMin.y, vecMin.z );
|
|
vecPoints[4].Init( vecMin.x, vecMin.y, vecMax.z );
|
|
vecPoints[5].Init( vecMin.x, vecMax.y, vecMax.z );
|
|
vecPoints[6].Init( vecMax.x, vecMax.y, vecMax.z );
|
|
vecPoints[7].Init( vecMax.x, vecMin.y, vecMax.z );
|
|
|
|
CDebugOverlay::AddLineOverlay( vecPoints[0], vecPoints[1], s_pVoxelColor[m_nLevel][0], s_pVoxelColor[m_nLevel][1], s_pVoxelColor[m_nLevel][2], 255, true, flTime );
|
|
CDebugOverlay::AddLineOverlay( vecPoints[1], vecPoints[2], s_pVoxelColor[m_nLevel][0], s_pVoxelColor[m_nLevel][1], s_pVoxelColor[m_nLevel][2], 255, true, flTime );
|
|
CDebugOverlay::AddLineOverlay( vecPoints[2], vecPoints[3], s_pVoxelColor[m_nLevel][0], s_pVoxelColor[m_nLevel][1], s_pVoxelColor[m_nLevel][2], 255, true, flTime );
|
|
CDebugOverlay::AddLineOverlay( vecPoints[3], vecPoints[0], s_pVoxelColor[m_nLevel][0], s_pVoxelColor[m_nLevel][1], s_pVoxelColor[m_nLevel][2], 255, true, flTime );
|
|
|
|
CDebugOverlay::AddLineOverlay( vecPoints[4], vecPoints[5], s_pVoxelColor[m_nLevel][0], s_pVoxelColor[m_nLevel][1], s_pVoxelColor[m_nLevel][2], 255, true, flTime );
|
|
CDebugOverlay::AddLineOverlay( vecPoints[5], vecPoints[6], s_pVoxelColor[m_nLevel][0], s_pVoxelColor[m_nLevel][1], s_pVoxelColor[m_nLevel][2], 255, true, flTime );
|
|
CDebugOverlay::AddLineOverlay( vecPoints[6], vecPoints[7], s_pVoxelColor[m_nLevel][0], s_pVoxelColor[m_nLevel][1], s_pVoxelColor[m_nLevel][2], 255, true, flTime );
|
|
CDebugOverlay::AddLineOverlay( vecPoints[7], vecPoints[4], s_pVoxelColor[m_nLevel][0], s_pVoxelColor[m_nLevel][1], s_pVoxelColor[m_nLevel][2], 255, true, flTime );
|
|
|
|
CDebugOverlay::AddLineOverlay( vecPoints[0], vecPoints[4], s_pVoxelColor[m_nLevel][0], s_pVoxelColor[m_nLevel][1], s_pVoxelColor[m_nLevel][2], 255, true, flTime );
|
|
CDebugOverlay::AddLineOverlay( vecPoints[3], vecPoints[7], s_pVoxelColor[m_nLevel][0], s_pVoxelColor[m_nLevel][1], s_pVoxelColor[m_nLevel][2], 255, true, flTime );
|
|
CDebugOverlay::AddLineOverlay( vecPoints[1], vecPoints[5], s_pVoxelColor[m_nLevel][0], s_pVoxelColor[m_nLevel][1], s_pVoxelColor[m_nLevel][2], 255, true, flTime );
|
|
CDebugOverlay::AddLineOverlay( vecPoints[2], vecPoints[6], s_pVoxelColor[m_nLevel][0], s_pVoxelColor[m_nLevel][1], s_pVoxelColor[m_nLevel][2], 255, true, flTime );
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Debug! Render the objects in a voxel (optionally, render the voxel!).
|
|
//-----------------------------------------------------------------------------
|
|
void CVoxelHash::RenderObjectsInVoxel( Voxel_t voxel, CPartitionVisitor *pVisitor, bool bRenderVoxel, float flTime )
|
|
{
|
|
UtlHashFixedHandle_t hHash = m_aVoxelHash.Find( voxel.uiVoxel );
|
|
if ( hHash == m_aVoxelHash.InvalidHandle() )
|
|
return;
|
|
|
|
intp iEntityList = m_aVoxelHash.Element( hHash );
|
|
while ( iEntityList != m_aEntityList.InvalidIndex() )
|
|
{
|
|
SpatialPartitionHandle_t hPartition = m_aEntityList[iEntityList].m_handle;
|
|
RenderObjectInVoxel( hPartition, pVisitor, flTime );
|
|
iEntityList = m_aEntityList.Next( iEntityList );
|
|
}
|
|
|
|
if ( bRenderVoxel )
|
|
{
|
|
RenderVoxel( voxel, flTime );
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Returns number of entities in the hash
|
|
//-----------------------------------------------------------------------------
|
|
int CVoxelHash::EntityCount()
|
|
{
|
|
int nCount = 0;
|
|
int nBucketCount = SPHASH_BUCKET_COUNT;
|
|
for ( int iBucket = 0; iBucket < nBucketCount; ++iBucket )
|
|
{
|
|
if ( m_aVoxelHash.m_aBuckets[iBucket].Count() == 0 )
|
|
continue;
|
|
|
|
UtlPtrLinkedListIndex_t hHash = m_aVoxelHash.m_aBuckets[iBucket].Head();
|
|
|
|
while ( hHash != m_aVoxelHash.m_aBuckets[iBucket].InvalidIndex() )
|
|
{
|
|
intp iEntity = m_aVoxelHash.m_aBuckets[iBucket][hHash].m_Data;
|
|
while ( iEntity!= m_aEntityList.InvalidIndex() )
|
|
{
|
|
++nCount;
|
|
iEntity = m_aEntityList.Next( iEntity );
|
|
}
|
|
|
|
hHash = m_aVoxelHash.m_aBuckets[iBucket].Next( hHash );
|
|
}
|
|
}
|
|
return nCount;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Rendering methods
|
|
//-----------------------------------------------------------------------------
|
|
void CVoxelHash::RenderGrid()
|
|
{
|
|
#ifndef DEDICATED
|
|
Vector vecStart, vecEnd;
|
|
for ( int i = 0; i < m_nVoxelDelta[0]; ++i )
|
|
{
|
|
vecStart.x = vecEnd.x = i * VoxelSize() + m_vecVoxelOrigin.x;
|
|
for ( int j = 0; j < m_nVoxelDelta[1]; ++j )
|
|
{
|
|
vecStart.y = vecEnd.y = j * VoxelSize() + m_vecVoxelOrigin.y;
|
|
vecStart.z = m_vecVoxelOrigin.z;
|
|
vecEnd.z = m_nVoxelDelta[2] * VoxelSize() + m_vecVoxelOrigin.z;
|
|
|
|
RenderLine( vecStart, vecEnd, s_pVoxelColor[m_nLevel], true );
|
|
}
|
|
}
|
|
|
|
for ( int i = 0; i < m_nVoxelDelta[0]; ++i )
|
|
{
|
|
vecStart.x = vecEnd.x = i * VoxelSize() + m_vecVoxelOrigin.x;
|
|
for ( int j = 0; j < m_nVoxelDelta[2]; ++j )
|
|
{
|
|
vecStart.z = vecEnd.z = j * VoxelSize() + m_vecVoxelOrigin.z;
|
|
vecStart.y = m_vecVoxelOrigin.y;
|
|
vecEnd.y = m_nVoxelDelta[2] * VoxelSize() + m_vecVoxelOrigin.y;
|
|
|
|
RenderLine( vecStart, vecEnd, s_pVoxelColor[m_nLevel], true );
|
|
}
|
|
}
|
|
|
|
for ( int i = 0; i < m_nVoxelDelta[1]; ++i )
|
|
{
|
|
vecStart.y = vecEnd.y = i * VoxelSize() + m_vecVoxelOrigin.y;
|
|
for ( int j = 0; j < m_nVoxelDelta[2]; ++j )
|
|
{
|
|
vecStart.z = vecEnd.z = j * VoxelSize() + m_vecVoxelOrigin.z;
|
|
vecStart.x = m_vecVoxelOrigin.z;
|
|
vecEnd.x = m_nVoxelDelta[2] * VoxelSize() + m_vecVoxelOrigin.x;
|
|
|
|
RenderLine( vecStart, vecEnd, s_pVoxelColor[m_nLevel], true );
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Debug! Render boxes around objects in tree.
|
|
//-----------------------------------------------------------------------------
|
|
void CVoxelHash::RenderAllObjectsInTree( float flTime )
|
|
{
|
|
int nBucketCount = SPHASH_BUCKET_COUNT;
|
|
|
|
CPartitionVisits *pPrevVisits = m_pTree->BeginVisit();
|
|
CPartitionVisitor visitor( m_pTree );
|
|
|
|
for ( int iBucket = 0; iBucket < nBucketCount; ++iBucket )
|
|
{
|
|
if ( m_aVoxelHash.m_aBuckets[iBucket].Count() == 0 )
|
|
continue;
|
|
|
|
UtlPtrLinkedListIndex_t hHash = m_aVoxelHash.m_aBuckets[iBucket].Head();
|
|
|
|
while ( hHash != m_aVoxelHash.m_aBuckets[iBucket].InvalidIndex() )
|
|
{
|
|
intp iEntity = m_aVoxelHash.m_aBuckets[iBucket][hHash].m_Data;
|
|
while ( iEntity!= m_aEntityList.InvalidIndex() )
|
|
{
|
|
SpatialPartitionHandle_t hPartition = m_aEntityList[iEntity].m_handle;
|
|
RenderObjectInVoxel( hPartition, &visitor, flTime );
|
|
iEntity = m_aEntityList.Next( iEntity );
|
|
}
|
|
|
|
hHash = m_aVoxelHash.m_aBuckets[iBucket].Next( hHash );
|
|
}
|
|
}
|
|
|
|
m_pTree->EndVisit( pPrevVisits );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CVoxelHash::RenderObjectsInPlayerLeafs( const Vector &vecPlayerMin, const Vector &vecPlayerMax, float flTime )
|
|
{
|
|
// Visit each voxel in the box and enumerate its elements.
|
|
Voxel_t voxelMin, voxelMax;
|
|
voxelMin = VoxelIndexFromPoint( vecPlayerMin );
|
|
voxelMax = VoxelIndexFromPoint( vecPlayerMax );
|
|
|
|
CPartitionVisits *pPrevVisits = m_pTree->BeginVisit();
|
|
|
|
// Create leaf cache.
|
|
Voxel_t voxel;
|
|
unsigned int iX, iY, iZ;
|
|
CPartitionVisitor visitor( m_pTree );
|
|
for ( iX = voxelMin.bitsVoxel.x; iX <= voxelMax.bitsVoxel.x; ++iX )
|
|
{
|
|
for ( iY = voxelMin.bitsVoxel.y; iY <= voxelMax.bitsVoxel.y; ++iY )
|
|
{
|
|
for ( iZ = voxelMin.bitsVoxel.z; iZ <= voxelMax.bitsVoxel.z; ++iZ )
|
|
{
|
|
voxel.bitsVoxel.x = iX;
|
|
voxel.bitsVoxel.y = iY;
|
|
voxel.bitsVoxel.z = iZ;
|
|
RenderObjectsInVoxel( voxel, &visitor, false, flTime );
|
|
}
|
|
}
|
|
}
|
|
|
|
m_pTree->EndVisit( pPrevVisits );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Constructor
|
|
//-----------------------------------------------------------------------------
|
|
|
|
CVoxelTree::CVoxelTree() : m_pVoxelHash( NULL ), m_pOwner( NULL ), m_nNextVisitBit( 0 )
|
|
{
|
|
// Compute max number of levels
|
|
m_nLevelCount = 0;
|
|
while ( CVoxelHash::ComputeVoxelCountAtLevel( m_nLevelCount ) > 2 )
|
|
{
|
|
++m_nLevelCount;
|
|
}
|
|
++m_nLevelCount; // Account for the level where count = 2;
|
|
|
|
// Various optimizations I've made require 4 levels
|
|
Assert( m_nLevelCount == 4 );
|
|
|
|
m_pVoxelHash = new CVoxelHash[m_nLevelCount];
|
|
|
|
m_AvailableVisitBits.EnsureCapacity( 2048 );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
CVoxelTree::~CVoxelTree()
|
|
{
|
|
delete[] m_pVoxelHash;
|
|
}
|
|
|
|
|
|
void ClampVector( Vector &out, const Vector &mins, const Vector &maxs )
|
|
{
|
|
for ( int i = 0; i < 3; i++ )
|
|
{
|
|
out[i] = clamp(out[i], mins[i], maxs[i]);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input: worldmin -
|
|
// worldmax -
|
|
//-----------------------------------------------------------------------------
|
|
void CVoxelTree::Init( CSpatialPartition *pOwner, int iTree, const Vector &worldmin, const Vector &worldmax )
|
|
{
|
|
m_pOwner = pOwner;
|
|
m_TreeId = iTree;
|
|
|
|
// Reset the enumeration id.
|
|
memset( m_pVisits, 0, sizeof( m_pVisits ) );
|
|
|
|
for ( int i = 0; i < m_nLevelCount; ++i )
|
|
{
|
|
m_pVoxelHash[i].Init( this, worldmin, worldmax, i );
|
|
}
|
|
|
|
// Setup the leaf list pool.
|
|
m_aLeafList.Purge();
|
|
m_aLeafList.SetGrowSize( SPHASH_LEAFLIST_BLOCK );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CVoxelTree::Shutdown( void )
|
|
{
|
|
m_aLeafList.Purge();
|
|
for ( int i = 0; i < m_nLevelCount; ++i )
|
|
{
|
|
m_pVoxelHash[i].Shutdown();
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Insert into the appropriate tree
|
|
//-----------------------------------------------------------------------------
|
|
void CVoxelTree::InsertIntoTree( SpatialPartitionHandle_t hPartition, const Vector& mins, const Vector& maxs, bool bReinsert )
|
|
{
|
|
Assert( hPartition != PARTITION_INVALID_HANDLE );
|
|
|
|
EntityInfo_t &info = EntityInfo( hPartition );
|
|
|
|
// Bloat by an eps before inserting the object into the tree.
|
|
Vector vecMin( mins.x - SPHASH_EPS, mins.y - SPHASH_EPS, mins.z - SPHASH_EPS );
|
|
Vector vecMax( maxs.x + SPHASH_EPS, maxs.y + SPHASH_EPS, maxs.z + SPHASH_EPS );
|
|
|
|
ClampVector(vecMin, s_PartitionMin, s_PartitionMax);
|
|
ClampVector(vecMax, s_PartitionMin, s_PartitionMax);
|
|
Vector vecSize;
|
|
VectorSubtract( vecMax, vecMin, vecSize );
|
|
|
|
int nLevel;
|
|
for ( nLevel = 0; nLevel < m_nLevelCount - 1; ++nLevel )
|
|
{
|
|
float flVoxelSize = m_pVoxelHash[nLevel].VoxelSizeF();
|
|
if ( (flVoxelSize > vecSize.x) && (flVoxelSize > vecSize.y) && (flVoxelSize > vecSize.z) )
|
|
break;
|
|
}
|
|
|
|
// Add the object to the tree.
|
|
Voxel_t voxelMin, voxelMax;
|
|
voxelMin = m_pVoxelHash[nLevel].VoxelIndexFromPoint( vecMin );
|
|
voxelMax = m_pVoxelHash[nLevel].VoxelIndexFromPoint( vecMax );
|
|
|
|
bool bDoInsert = true;
|
|
if ( bReinsert )
|
|
{
|
|
// on reinsert we need to either remove/insert or not do anything
|
|
// if the entity spans the same bounding box of voxels no remove/insert is necessary
|
|
if ( info.m_voxelMin.uiVoxel == voxelMin.uiVoxel && info.m_voxelMax.uiVoxel == voxelMax.uiVoxel )
|
|
{
|
|
bDoInsert = false;
|
|
}
|
|
else
|
|
{
|
|
// Remove entity from voxel hash.
|
|
RemoveFromTree( hPartition );
|
|
}
|
|
}
|
|
// Set/update the entity bounding box.
|
|
info.m_vecMin = vecMin;
|
|
info.m_vecMax = vecMax;
|
|
|
|
if ( bDoInsert )
|
|
{
|
|
bool bWasReading = ( m_pVisits[g_nThreadID] != NULL );
|
|
if ( bWasReading )
|
|
{
|
|
// If we're recursing in this thread, need to release our read lock to allow ourselves to write
|
|
UnlockRead();
|
|
}
|
|
m_lock.LockForWrite();
|
|
|
|
// if these have changed we need to insert
|
|
info.m_voxelMin = voxelMin;
|
|
info.m_voxelMax = voxelMax;
|
|
if ( m_AvailableVisitBits.Count() )
|
|
{
|
|
info.m_nVisitBit[m_TreeId] = m_AvailableVisitBits.Tail();
|
|
m_AvailableVisitBits.Remove( m_AvailableVisitBits.Count() - 1 );
|
|
}
|
|
else
|
|
{
|
|
info.m_nVisitBit[m_TreeId] = m_nNextVisitBit++;
|
|
}
|
|
m_pVoxelHash[nLevel].InsertIntoTree( hPartition, voxelMin, voxelMax );
|
|
m_lock.UnlockWrite();
|
|
if ( bWasReading )
|
|
{
|
|
LockForRead();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Remove from appropriate tree
|
|
//-----------------------------------------------------------------------------
|
|
void CVoxelTree::RemoveFromTree( SpatialPartitionHandle_t hPartition )
|
|
{
|
|
Assert( hPartition != PARTITION_INVALID_HANDLE );
|
|
EntityInfo_t &info = EntityInfo( hPartition );
|
|
int nLevel = info.m_nLevel[GetTreeId()];
|
|
if ( nLevel >= 0 )
|
|
{
|
|
bool bWasReading = ( m_pVisits[g_nThreadID] != NULL );
|
|
if ( bWasReading )
|
|
{
|
|
// If we're recursing in this thread, need to release our read lock to allow ourselves to write
|
|
UnlockRead();
|
|
}
|
|
|
|
m_lock.LockForWrite();
|
|
m_pVoxelHash[nLevel].RemoveFromTree( hPartition );
|
|
m_AvailableVisitBits.AddToTail( info.m_nVisitBit[m_TreeId] );
|
|
info.m_nVisitBit[m_TreeId] = (unsigned short)-1;
|
|
m_lock.UnlockWrite();
|
|
|
|
if ( bWasReading )
|
|
{
|
|
LockForRead();
|
|
}
|
|
}
|
|
}
|
|
|
|
void CVoxelTree::UpdateListMask( SpatialPartitionHandle_t hPartition )
|
|
{
|
|
EntityInfo_t &info = EntityInfo( hPartition );
|
|
int nLevel = info.m_nLevel[GetTreeId()];
|
|
if ( nLevel >= 0 )
|
|
{
|
|
m_lock.LockForRead();
|
|
m_pVoxelHash[nLevel].UpdateListMask( hPartition );
|
|
m_lock.UnlockRead();
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Called when an element moves
|
|
//-----------------------------------------------------------------------------
|
|
void CVoxelTree::ElementMoved( SpatialPartitionHandle_t hPartition, const Vector& mins, const Vector& maxs )
|
|
{
|
|
if ( hPartition != PARTITION_INVALID_HANDLE )
|
|
{
|
|
// If it doesn't already exist in the tree - add it.
|
|
EntityInfo_t &info = EntityInfo( hPartition );
|
|
if ( info.m_iLeafList[GetTreeId()] == CLeafList::InvalidIndex() )
|
|
{
|
|
InsertIntoTree( hPartition, mins, maxs, false );
|
|
return;
|
|
}
|
|
|
|
// Re-insert entity into voxel hash.
|
|
InsertIntoTree( hPartition, mins, maxs, true );
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CVoxelTree::EnumerateElementsInBox( SpatialPartitionListMask_t listMask,
|
|
const Vector& vecMins, const Vector& vecMaxs,
|
|
bool coarseTest, IPartitionEnumerator* pIterator )
|
|
{
|
|
VPROF( "BoxTest/SphereTest" );
|
|
|
|
// If this assertion fails, you're using a list at a point where the spatial partition elements aren't set up!
|
|
// Assert( ( listMask & m_nSuppressedListMask ) == 0 );
|
|
|
|
// Early-out.
|
|
if ( listMask == 0 )
|
|
return;
|
|
|
|
// Clamp bounds to extant space
|
|
Vector mins, maxs;
|
|
VectorMax( vecMins, s_PartitionMin, mins );
|
|
VectorMin( mins, s_PartitionMax, mins );
|
|
|
|
VectorMax( vecMaxs, s_PartitionMin, maxs );
|
|
VectorMin( maxs, s_PartitionMax, maxs );
|
|
|
|
// Callbacks.
|
|
CPartitionVisits *pPrevVisits = BeginVisit();
|
|
|
|
m_lock.LockForRead();
|
|
Voxel_t vs = m_pVoxelHash[0].VoxelIndexFromPoint( mins );
|
|
Voxel_t ve = m_pVoxelHash[0].VoxelIndexFromPoint( maxs );
|
|
if ( !m_pVoxelHash[0].EnumerateElementsInBox( listMask, vs, ve, mins, maxs, pIterator ) )
|
|
{
|
|
m_lock.UnlockRead();
|
|
EndVisit( pPrevVisits );
|
|
return;
|
|
}
|
|
|
|
vs = ConvertToNextLevel( vs );
|
|
ve = ConvertToNextLevel( ve );
|
|
if ( !m_pVoxelHash[1].EnumerateElementsInBox( listMask, vs, ve, mins, maxs, pIterator ) )
|
|
{
|
|
m_lock.UnlockRead();
|
|
EndVisit( pPrevVisits );
|
|
return;
|
|
}
|
|
|
|
vs = ConvertToNextLevel( vs );
|
|
ve = ConvertToNextLevel( ve );
|
|
if ( !m_pVoxelHash[2].EnumerateElementsInBox( listMask, vs, ve, mins, maxs, pIterator ) )
|
|
{
|
|
m_lock.UnlockRead();
|
|
EndVisit( pPrevVisits );
|
|
return;
|
|
}
|
|
|
|
vs = ConvertToNextLevel( vs );
|
|
ve = ConvertToNextLevel( ve );
|
|
m_pVoxelHash[3].EnumerateElementsInBox( listMask, vs, ve, mins, maxs, pIterator );
|
|
|
|
m_lock.UnlockRead();
|
|
EndVisit( pPrevVisits );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CVoxelTree::EnumerateElementsInSphere( SpatialPartitionListMask_t listMask,
|
|
const Vector& origin, float radius, bool coarseTest, IPartitionEnumerator* pIterator )
|
|
{
|
|
// Otherwise they might as well just walk the entire ent list!!!
|
|
Assert( radius <= MAX_COORD_FLOAT );
|
|
|
|
// If the box test is fast enough - forget about the sphere test?
|
|
Vector vecMin( origin.x - radius, origin.y - radius, origin.z - radius );
|
|
Vector vecMax( origin.x + radius, origin.y + radius, origin.z + radius );
|
|
return EnumerateElementsInBox( listMask, vecMin, vecMax, coarseTest, pIterator );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
bool CVoxelTree::EnumerateElementsAlongRay_Ray( SpatialPartitionListMask_t listMask,
|
|
const Ray_t &ray, const Vector &vecInvDelta, const Vector &vecEnd, IPartitionEnumerator *pIterator )
|
|
{
|
|
// Find the voxel start + end
|
|
Voxel_t voxelStart = m_pVoxelHash[0].VoxelIndexFromPoint( ray.m_Start );
|
|
Voxel_t voxelEnd = m_pVoxelHash[0].VoxelIndexFromPoint( vecEnd );
|
|
|
|
bool bSingleVoxel = ( voxelStart.uiVoxel == voxelEnd.uiVoxel );
|
|
CIntersectRay intersectRay( this, ray, vecInvDelta );
|
|
|
|
// Optimization: Look for single voxel rays
|
|
if ( bSingleVoxel )
|
|
{
|
|
if ( !m_pVoxelHash[0].EnumerateElementsInSingleVoxel( voxelStart, intersectRay, listMask, pIterator ) )
|
|
return false;
|
|
voxelStart = ConvertToNextLevel( voxelStart );
|
|
if ( !m_pVoxelHash[1].EnumerateElementsInSingleVoxel( voxelStart, intersectRay, listMask, pIterator ) )
|
|
return false;
|
|
voxelStart = ConvertToNextLevel( voxelStart );
|
|
if ( !m_pVoxelHash[2].EnumerateElementsInSingleVoxel( voxelStart, intersectRay, listMask, pIterator ) )
|
|
return false;
|
|
voxelStart = ConvertToNextLevel( voxelStart );
|
|
return m_pVoxelHash[3].EnumerateElementsInSingleVoxel( voxelStart, intersectRay, listMask, pIterator );
|
|
}
|
|
|
|
Voxel_t voxelCurrent;
|
|
voxelCurrent.uiVoxel = voxelStart.uiVoxel;
|
|
|
|
// Setup.
|
|
int nStep[3];
|
|
float tMax[3];
|
|
float tDelta[3];
|
|
m_pVoxelHash[0].LeafListRaySetup( ray, vecEnd, vecInvDelta, voxelStart, nStep, tMax, tDelta );
|
|
|
|
// Walk the voxels and visit all elements in each voxel
|
|
|
|
// Deal with all levels
|
|
Voxel_t ov1, ov2, ov3;
|
|
ov1.uiVoxel = ov2.uiVoxel = ov3.uiVoxel = 0xFFFFFFFF;
|
|
|
|
Voxel_t v1 = ConvertToNextLevel( voxelCurrent );
|
|
Voxel_t v2 = ConvertToNextLevel( v1 );
|
|
Voxel_t v3 = ConvertToNextLevel( v2 );
|
|
|
|
while ( 1 )
|
|
{
|
|
if ( !m_pVoxelHash[0].EnumerateElementsInVoxel( voxelCurrent, intersectRay, listMask, pIterator ) )
|
|
return false;
|
|
|
|
if ( v1.uiVoxel != ov1.uiVoxel )
|
|
{
|
|
if ( !m_pVoxelHash[1].EnumerateElementsInVoxel( v1, intersectRay, listMask, pIterator ) )
|
|
return false;
|
|
}
|
|
if ( v2.uiVoxel != ov2.uiVoxel )
|
|
{
|
|
if ( !m_pVoxelHash[2].EnumerateElementsInVoxel( v2, intersectRay, listMask, pIterator ) )
|
|
return false;
|
|
}
|
|
if ( v3.uiVoxel != ov3.uiVoxel )
|
|
{
|
|
if ( !m_pVoxelHash[3].EnumerateElementsInVoxel( v3, intersectRay, listMask, pIterator ) )
|
|
return false;
|
|
}
|
|
|
|
if ( tMax[0] >= 1.0f && tMax[1] >= 1.0f && tMax[2] >= 1.0f )
|
|
break;
|
|
|
|
if ( tMax[0] < tMax[1] )
|
|
{
|
|
if ( tMax[0] < tMax[2] )
|
|
{
|
|
voxelCurrent.bitsVoxel.x += nStep[0];
|
|
tMax[0] += tDelta[0];
|
|
}
|
|
else
|
|
{
|
|
voxelCurrent.bitsVoxel.z += nStep[2];
|
|
tMax[2] += tDelta[2];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( tMax[1] < tMax[2] )
|
|
{
|
|
voxelCurrent.bitsVoxel.y += nStep[1];
|
|
tMax[1] += tDelta[1];
|
|
}
|
|
else
|
|
{
|
|
voxelCurrent.bitsVoxel.z += nStep[2];
|
|
tMax[2] += tDelta[2];
|
|
}
|
|
}
|
|
|
|
ov1 = v1; ov2 = v2; ov3 = v3;
|
|
v1 = ConvertToNextLevel( voxelCurrent );
|
|
v2 = ConvertToNextLevel( v1 );
|
|
v3 = ConvertToNextLevel( v2 );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CVoxelTree::ComputeSweptRayBounds( const Ray_t &ray, const Vector &vecStartMin, const Vector &vecStartMax, Vector *pVecMin, Vector *pVecMax )
|
|
{
|
|
if ( ray.m_Delta.x < 0 )
|
|
{
|
|
pVecMin->x = vecStartMin.x + ray.m_Delta.x;
|
|
pVecMax->x = vecStartMax.x;
|
|
}
|
|
else
|
|
{
|
|
pVecMin->x = vecStartMin.x;
|
|
pVecMax->x = vecStartMax.x + ray.m_Delta.x;
|
|
}
|
|
|
|
if ( ray.m_Delta.y < 0 )
|
|
{
|
|
pVecMin->y = vecStartMin.y + ray.m_Delta.y;
|
|
pVecMax->y = vecStartMax.y;
|
|
}
|
|
else
|
|
{
|
|
pVecMin->y = vecStartMin.y;
|
|
pVecMax->y = vecStartMax.y + ray.m_Delta.y;
|
|
}
|
|
|
|
if ( ray.m_Delta.z < 0 )
|
|
{
|
|
pVecMin->z = vecStartMin.z + ray.m_Delta.z;
|
|
pVecMax->z = vecStartMax.z;
|
|
}
|
|
else
|
|
{
|
|
pVecMin->z = vecStartMin.z;
|
|
pVecMax->z = vecStartMax.z + ray.m_Delta.z;
|
|
}
|
|
}
|
|
|
|
bool CVoxelTree::EnumerateRayStartVoxels( SpatialPartitionListMask_t listMask, IPartitionEnumerator *pIterator, CIntersectSweptBox &intersectSweptBox, int voxelBounds[4][2][3] )
|
|
{
|
|
// Iterate over all voxels that intersect the box around the starting ray point
|
|
int nMinX = voxelBounds[0][0][0];
|
|
int nMinY = voxelBounds[0][0][1];
|
|
int nMinZ = voxelBounds[0][0][2];
|
|
|
|
int nMaxX = voxelBounds[0][1][0];
|
|
int nMaxY = voxelBounds[0][1][1];
|
|
int nMaxZ = voxelBounds[0][1][2];
|
|
for ( int i = 0; i < m_nLevelCount; ++i )
|
|
{
|
|
if ( i != 0 )
|
|
{
|
|
nMinX >>= SPHASH_LEVEL_SKIP;
|
|
nMinY >>= SPHASH_LEVEL_SKIP;
|
|
nMinZ >>= SPHASH_LEVEL_SKIP;
|
|
nMaxX >>= SPHASH_LEVEL_SKIP;
|
|
nMaxY >>= SPHASH_LEVEL_SKIP;
|
|
nMaxZ >>= SPHASH_LEVEL_SKIP;
|
|
|
|
voxelBounds[i][0][0] = nMinX;
|
|
voxelBounds[i][0][1] = nMinY;
|
|
voxelBounds[i][0][2] = nMinZ;
|
|
|
|
voxelBounds[i][1][0] = nMaxX;
|
|
voxelBounds[i][1][1] = nMaxY;
|
|
voxelBounds[i][1][2] = nMaxZ;
|
|
}
|
|
|
|
Voxel_t voxel;
|
|
int iX, iY, iZ;
|
|
for ( iX = nMinX; iX <= nMaxX; ++iX )
|
|
{
|
|
voxel.bitsVoxel.x = iX;
|
|
for ( iY = nMinY; iY <= nMaxY; ++iY )
|
|
{
|
|
voxel.bitsVoxel.y = iY;
|
|
for ( iZ = nMinZ; iZ <= nMaxZ; ++iZ )
|
|
{
|
|
voxel.bitsVoxel.z = iZ;
|
|
if ( !m_pVoxelHash[i].EnumerateElementsInVoxel( voxel, intersectSweptBox, listMask, pIterator ) )
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
bool CVoxelTree::EnumerateElementsAlongRay_ExtrudedRay( SpatialPartitionListMask_t listMask,
|
|
const Ray_t &ray, const Vector &vecInvDelta, const Vector &vecEnd, IPartitionEnumerator *pIterator )
|
|
{
|
|
// Check the starting position, then proceed with the sweep.
|
|
Vector vecMin, vecMax;
|
|
VectorSubtract( ray.m_Start, ray.m_Extents, vecMin );
|
|
VectorAdd( ray.m_Start, ray.m_Extents, vecMax );
|
|
|
|
// Visit each voxel in the box and enumerate its elements.
|
|
// Indexed as voxelBounds[level][min/max][x/y/z]
|
|
int voxelBounds[4][2][3];
|
|
m_pVoxelHash[0].VoxelIndexFromPoint( vecMin, voxelBounds[0][0] );
|
|
m_pVoxelHash[0].VoxelIndexFromPoint( vecMax, voxelBounds[0][1] );
|
|
|
|
CIntersectSweptBox intersectSweptBox( this, ray, vecInvDelta );
|
|
|
|
if ( !EnumerateRayStartVoxels( listMask, pIterator, intersectSweptBox, voxelBounds ) )
|
|
return false;
|
|
|
|
// Early out: Check to see if the range of voxels at the endpoint
|
|
// is the same as the range at the start point. If so, we're done.
|
|
#if defined(_X360) || defined(_PS3)
|
|
fltx4 fl4RayEnd = LoadUnaligned3SIMD(vecEnd.Base());
|
|
fltx4 fl4Extents = LoadAlignedSIMD(ray.m_Extents.Base());
|
|
fltx4 vecEndMin = SubSIMD( fl4RayEnd, fl4Extents );
|
|
fltx4 vecEndMax = AddSIMD( fl4RayEnd, fl4Extents );
|
|
#else
|
|
Vector vecEndMin, vecEndMax;
|
|
VectorSubtract( vecEnd, ray.m_Extents, vecEndMin );
|
|
VectorAdd( vecEnd, ray.m_Extents, vecEndMax );
|
|
#endif
|
|
|
|
int endVoxelMin[3], endVoxelMax[3];
|
|
m_pVoxelHash[0].VoxelIndexFromPoint( vecEndMin, endVoxelMin );
|
|
m_pVoxelHash[0].VoxelIndexFromPoint( vecEndMax, endVoxelMax );
|
|
if ( (endVoxelMin[0] >= voxelBounds[0][0][0]) && (endVoxelMin[1] >= voxelBounds[0][0][1]) && (endVoxelMin[2] >= voxelBounds[0][0][2]) &&
|
|
(endVoxelMax[0] <= voxelBounds[0][1][0]) && (endVoxelMax[1] <= voxelBounds[0][1][1]) && (endVoxelMax[2] <= voxelBounds[0][1][2]) )
|
|
return true;
|
|
|
|
// Setup.
|
|
int nStep[3];
|
|
float tMax[3]; // amount of change in t along ray until we hit the next new voxel
|
|
float tMin[3]; // amount of change in t along ray until we leave the last voxel
|
|
float tDelta[3];
|
|
m_pVoxelHash[0].LeafListExtrudedRaySetup( ray, vecInvDelta, vecMin, vecMax, voxelBounds[0][0], voxelBounds[0][1], nStep, tMin, tMax, tDelta );
|
|
|
|
int nLastVoxel1[3];
|
|
int nLastVoxel2[3];
|
|
int nLastVoxel3[3];
|
|
for ( int i = 0; i < 3; ++i )
|
|
{
|
|
int nIndex = ( nStep[i] > 0 ) ? 1 : 0;
|
|
nLastVoxel1[i] = voxelBounds[1][nIndex][i];
|
|
nLastVoxel2[i] = voxelBounds[2][nIndex][i];
|
|
nLastVoxel3[i] = voxelBounds[3][nIndex][i];
|
|
}
|
|
|
|
// Walk the voxels and create the leaf list.
|
|
int iAxis, iMinAxis;
|
|
while ( tMax[0] < 1.0f || tMax[1] < 1.0f || tMax[2] < 1.0f )
|
|
{
|
|
iAxis = MinIndex( tMax[0], tMax[1], tMax[2] );
|
|
iMinAxis = MinIndex( tMin[0], tMin[1], tMin[2] );
|
|
|
|
if ( tMin[iMinAxis] < tMax[iAxis] )
|
|
{
|
|
tMin[iMinAxis] += tDelta[iMinAxis];
|
|
int nIndex = ( nStep[iMinAxis] > 0 ) ? 0 : 1;
|
|
voxelBounds[0][nIndex][iMinAxis] += nStep[iMinAxis];
|
|
voxelBounds[1][nIndex][iMinAxis] = voxelBounds[0][nIndex][iMinAxis] >> SPHASH_LEVEL_SKIP;
|
|
voxelBounds[2][nIndex][iMinAxis] = voxelBounds[0][nIndex][iMinAxis] >> (2 * SPHASH_LEVEL_SKIP);
|
|
voxelBounds[3][nIndex][iMinAxis] = voxelBounds[0][nIndex][iMinAxis] >> (3 * SPHASH_LEVEL_SKIP);
|
|
}
|
|
else
|
|
{
|
|
tMax[iAxis] += tDelta[iAxis];
|
|
int nIndex = ( nStep[iAxis] > 0 ) ? 1 : 0;
|
|
voxelBounds[0][nIndex][iAxis] += nStep[iAxis];
|
|
voxelBounds[1][nIndex][iAxis] = voxelBounds[0][nIndex][iAxis] >> SPHASH_LEVEL_SKIP;
|
|
voxelBounds[2][nIndex][iAxis] = voxelBounds[0][nIndex][iAxis] >> (2 * SPHASH_LEVEL_SKIP);
|
|
voxelBounds[3][nIndex][iAxis] = voxelBounds[0][nIndex][iAxis] >> (3 * SPHASH_LEVEL_SKIP);
|
|
|
|
if ( !m_pVoxelHash[0].EnumerateElementsAlongRay_ExtrudedRaySlice( listMask, pIterator, intersectSweptBox, voxelBounds[0][0], voxelBounds[0][1], iAxis, nStep ) )
|
|
return false;
|
|
|
|
if ( nLastVoxel1[iAxis] != voxelBounds[1][nIndex][iAxis] )
|
|
{
|
|
nLastVoxel1[iAxis] = voxelBounds[1][nIndex][iAxis];
|
|
if ( !m_pVoxelHash[1].EnumerateElementsAlongRay_ExtrudedRaySlice( listMask, pIterator, intersectSweptBox, voxelBounds[1][0], voxelBounds[1][1], iAxis, nStep ) )
|
|
return false;
|
|
}
|
|
|
|
if ( nLastVoxel2[iAxis] != voxelBounds[2][nIndex][iAxis] )
|
|
{
|
|
nLastVoxel2[iAxis] = voxelBounds[2][nIndex][iAxis];
|
|
if ( !m_pVoxelHash[2].EnumerateElementsAlongRay_ExtrudedRaySlice( listMask, pIterator, intersectSweptBox, voxelBounds[2][0], voxelBounds[2][1], iAxis, nStep ) )
|
|
return false;
|
|
}
|
|
|
|
if ( nLastVoxel3[iAxis] != voxelBounds[3][nIndex][iAxis] )
|
|
{
|
|
nLastVoxel3[iAxis] = voxelBounds[3][nIndex][iAxis];
|
|
if ( !m_pVoxelHash[3].EnumerateElementsAlongRay_ExtrudedRaySlice( listMask, pIterator, intersectSweptBox, voxelBounds[3][0], voxelBounds[3][1], iAxis, nStep ) )
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
#ifndef _PS3
|
|
#define THINK_TRACE_COUNTER_COMPILE_FUNCTIONS_ENGINE
|
|
#include "engine/thinktracecounter.h"
|
|
#endif
|
|
|
|
#ifdef THINK_TRACE_COUNTER_COMPILED
|
|
ConVar think_trace_limit( "think_trace_limit", "0", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY, "Break into the debugger if this many or more traces are performed in a single think function. Negative numbers mean that the same think function may be broken into many times (once per [x] may traces), positive numbers mean each think will break only once." );
|
|
CTHREADLOCALINT g_DebugTracesRemainingBeforeTrap(0);
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CVoxelTree::EnumerateElementsAlongRay( SpatialPartitionListMask_t listMask,
|
|
const Ray_t &ray, bool coarseTest, IPartitionEnumerator *pIterator )
|
|
{
|
|
VPROF("EnumerateElementsAlongRay");
|
|
#ifdef THINK_TRACE_COUNTER_COMPILED
|
|
if ( DEBUG_THINK_TRACE_COUNTER_ALLOWED() && think_trace_limit.GetInt() != 0 && g_DebugTracesRemainingBeforeTrap > 0 )
|
|
{
|
|
if ( --g_DebugTracesRemainingBeforeTrap <= 0 )
|
|
{
|
|
if ( Plat_IsInDebugSession() )
|
|
{
|
|
|
|
DebuggerBreakIfDebugging();
|
|
if ( think_trace_limit.GetInt() < 0 )
|
|
{
|
|
g_DebugTracesRemainingBeforeTrap = -think_trace_limit.GetInt();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AssertMsg1( false, "Performed %d traces in a single think function!\n", think_trace_limit.GetInt() );
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if ( !ray.m_IsSwept )
|
|
{
|
|
Vector vecMin, vecMax;
|
|
VectorSubtract( ray.m_Start, ray.m_Extents, vecMin );
|
|
VectorAdd( ray.m_Start, ray.m_Extents, vecMax );
|
|
return EnumerateElementsInBox( listMask, vecMin, vecMax, coarseTest, pIterator );
|
|
}
|
|
|
|
// If this assertion fails, you're using a list at a point where the spatial partition elements aren't set up!
|
|
// Assert( ( listMask & m_nSuppressedListMask ) == 0 );
|
|
|
|
// Early-out.
|
|
if ( listMask == 0 )
|
|
return;
|
|
|
|
// Calculate the end of the ray
|
|
Vector vecEnd;
|
|
Vector vecInvDelta;
|
|
Ray_t clippedRay = ray;
|
|
VectorAdd( clippedRay.m_Start, clippedRay.m_Delta, vecEnd );
|
|
|
|
bool bStartIn = IsPointInBox( ray.m_Start, s_PartitionMin, s_PartitionMax );
|
|
bool bEndIn = IsPointInBox( vecEnd, s_PartitionMin, s_PartitionMax );
|
|
if ( !bStartIn && !bEndIn )
|
|
return;
|
|
|
|
// Callbacks.
|
|
if ( !bStartIn )
|
|
{
|
|
ClampStartPoint( clippedRay, vecEnd );
|
|
}
|
|
else if ( !bEndIn )
|
|
{
|
|
ClampEndPoint( clippedRay, vecEnd );
|
|
}
|
|
|
|
vecInvDelta[0] = ( clippedRay.m_Delta[0] != 0.0f ) ? 1.0f / clippedRay.m_Delta[0] : FLT_MAX;
|
|
vecInvDelta[1] = ( clippedRay.m_Delta[1] != 0.0f ) ? 1.0f / clippedRay.m_Delta[1] : FLT_MAX;
|
|
vecInvDelta[2] = ( clippedRay.m_Delta[2] != 0.0f ) ? 1.0f / clippedRay.m_Delta[2] : FLT_MAX;
|
|
|
|
CPartitionVisits *pPrevVisits = BeginVisit();
|
|
|
|
m_lock.LockForRead();
|
|
if ( ray.m_IsRay )
|
|
{
|
|
EnumerateElementsAlongRay_Ray( listMask, clippedRay, vecInvDelta, vecEnd, pIterator );
|
|
}
|
|
else
|
|
{
|
|
EnumerateElementsAlongRay_ExtrudedRay( listMask, clippedRay, vecInvDelta, vecEnd, pIterator );
|
|
}
|
|
|
|
m_lock.UnlockRead();
|
|
EndVisit( pPrevVisits );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CVoxelTree::EnumerateElementsAtPoint( SpatialPartitionListMask_t listMask,
|
|
const Vector& pt, bool coarseTest, IPartitionEnumerator* pIterator )
|
|
{
|
|
// If this assertion fails, you're using a list at a point where the spatial partition elements aren't set up!
|
|
// Assert( ( listMask & m_nSuppressedListMask ) == 0 );
|
|
|
|
// Early-out.
|
|
if ( listMask == 0 )
|
|
return;
|
|
|
|
m_lock.LockForRead();
|
|
// Callbacks.
|
|
Voxel_t v = m_pVoxelHash[0].VoxelIndexFromPoint( pt );
|
|
if ( !m_pVoxelHash[0].EnumerateElementsAtPoint( listMask, v, pt, pIterator ) )
|
|
{
|
|
m_lock.UnlockRead();
|
|
return;
|
|
}
|
|
|
|
v = ConvertToNextLevel( v );
|
|
if ( !m_pVoxelHash[1].EnumerateElementsAtPoint( listMask, v, pt, pIterator ) )
|
|
{
|
|
m_lock.UnlockRead();
|
|
return;
|
|
}
|
|
|
|
v = ConvertToNextLevel( v );
|
|
if ( !m_pVoxelHash[2].EnumerateElementsAtPoint( listMask, v, pt, pIterator ) )
|
|
{
|
|
m_lock.UnlockRead();
|
|
return;
|
|
}
|
|
|
|
v = ConvertToNextLevel( v );
|
|
m_pVoxelHash[3].EnumerateElementsAtPoint( listMask, v, pt, pIterator );
|
|
m_lock.UnlockRead();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Debug! Render boxes around objects in tree.
|
|
//-----------------------------------------------------------------------------
|
|
void CVoxelTree::RenderAllObjectsInTree( float flTime )
|
|
{
|
|
MDLCACHE_CRITICAL_SECTION_(g_pMDLCache);
|
|
m_lock.LockForRead();
|
|
for ( int i = 0; i < m_nLevelCount; ++i )
|
|
{
|
|
m_pVoxelHash[i].RenderAllObjectsInTree( flTime );
|
|
}
|
|
m_lock.UnlockRead();
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CVoxelTree::RenderObjectsInPlayerLeafs( const Vector &vecPlayerMin, const Vector &vecPlayerMax, float flTime )
|
|
{
|
|
MDLCACHE_CRITICAL_SECTION_(g_pMDLCache);
|
|
m_lock.LockForRead();
|
|
for ( int i = 0; i < m_nLevelCount; ++i )
|
|
{
|
|
m_pVoxelHash[i].RenderObjectsInPlayerLeafs( vecPlayerMin, vecPlayerMax, flTime );
|
|
}
|
|
m_lock.UnlockRead();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Expose CSpatialPartition to the game + client DLL.
|
|
//-----------------------------------------------------------------------------
|
|
static CSpatialPartition g_SpatialPartition;
|
|
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CSpatialPartition, ISpatialPartition, INTERFACEVERSION_SPATIALPARTITION, g_SpatialPartition );
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Expose ISpatialPartitionInternal to the engine.
|
|
//-----------------------------------------------------------------------------
|
|
ISpatialPartitionInternal *SpatialPartition()
|
|
{
|
|
return &g_SpatialPartition;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Constructor
|
|
//-----------------------------------------------------------------------------
|
|
CSpatialPartition::CSpatialPartition()
|
|
{
|
|
m_nQueryCallbackCount = 0;
|
|
m_aHandles.SetAllocOwner( "CSpatialPartition::m_aHandles" );
|
|
}
|
|
|
|
|
|
CSpatialPartition::~CSpatialPartition()
|
|
{
|
|
Shutdown();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input: worldmin -
|
|
// worldmax -
|
|
//-----------------------------------------------------------------------------
|
|
void CSpatialPartition::Init( const Vector &worldmin, const Vector &worldmax )
|
|
{
|
|
// Clear the handle list and ensure some new memory.
|
|
MEM_ALLOC_CREDIT();
|
|
m_aHandles.Purge();
|
|
m_aHandles.EnsureCapacity( SPHASH_HANDLELIST_BLOCK );
|
|
|
|
for ( int i = 0; i < NUM_TREES; i++ )
|
|
{
|
|
m_VoxelTrees[i].Init( this, i, worldmin, worldmax );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CSpatialPartition::Shutdown( void )
|
|
{
|
|
for ( int i = 0; i < NUM_TREES; i++ )
|
|
{
|
|
m_VoxelTrees[i].Shutdown();
|
|
}
|
|
m_aHandles.Purge();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Add a callback to the query callback list. Functions get called
|
|
// right before a query occurs.
|
|
// Input: pCallback - pointer to the callback function to add
|
|
//-----------------------------------------------------------------------------
|
|
void CSpatialPartition::InstallQueryCallback( IPartitionQueryCallback *pCallback )
|
|
{
|
|
// Verify data.
|
|
Assert( pCallback && m_nQueryCallbackCount < MAX_QUERY_CALLBACK );
|
|
if ( !pCallback || ( m_nQueryCallbackCount >= MAX_QUERY_CALLBACK ) )
|
|
return;
|
|
|
|
m_pQueryCallback[m_nQueryCallbackCount] = pCallback;
|
|
++m_nQueryCallbackCount;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Remove a callback from the query callback list.
|
|
// Input: pCallback - pointer to the callback function to remove
|
|
//-----------------------------------------------------------------------------
|
|
void CSpatialPartition::RemoveQueryCallback( IPartitionQueryCallback *pCallback )
|
|
{
|
|
// Verify data.
|
|
if ( !pCallback )
|
|
return;
|
|
|
|
for ( int iQuery = m_nQueryCallbackCount; --iQuery >= 0; )
|
|
{
|
|
if ( m_pQueryCallback[iQuery] == pCallback )
|
|
{
|
|
--m_nQueryCallbackCount;
|
|
m_pQueryCallback[iQuery] = m_pQueryCallback[m_nQueryCallbackCount];
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Invokes the pre-query callbacks.
|
|
//-----------------------------------------------------------------------------
|
|
void CSpatialPartition::InvokeQueryCallbacks( SpatialPartitionListMask_t listMask, bool bDone )
|
|
{
|
|
for ( int iQuery = 0; iQuery < m_nQueryCallbackCount; ++iQuery )
|
|
{
|
|
if ( !bDone )
|
|
{
|
|
m_pQueryCallback[iQuery]->OnPreQuery( listMask );
|
|
}
|
|
else
|
|
{
|
|
m_pQueryCallback[iQuery]->OnPostQuery( listMask );
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Create spatial partition object handle.
|
|
// Input: pHandleEntity - entity handle of the object to create a spatial partition handle for
|
|
//-----------------------------------------------------------------------------
|
|
SpatialPartitionHandle_t CSpatialPartition::CreateHandle( IHandleEntity *pHandleEntity )
|
|
{
|
|
m_HandlesMutex.Lock();
|
|
SpatialPartitionHandle_t hPartition = m_aHandles.AddToTail();
|
|
m_HandlesMutex.Unlock();
|
|
m_aHandles[hPartition].m_pHandleEntity = pHandleEntity;
|
|
m_aHandles[hPartition].m_vecMin.Init( FLT_MAX, FLT_MAX, FLT_MAX );
|
|
m_aHandles[hPartition].m_vecMax.Init( FLT_MIN, FLT_MIN, FLT_MIN );
|
|
m_aHandles[hPartition].m_fList = 0;
|
|
m_aHandles[hPartition].m_flags = 0;
|
|
|
|
for ( int i = 0; i < NUM_TREES; i++ )
|
|
{
|
|
m_aHandles[hPartition].m_nVisitBit[i] = 0xffff;
|
|
m_aHandles[hPartition].m_nLevel[i] = (uint8)-1;
|
|
m_aHandles[hPartition].m_iLeafList[i] = CLeafList::InvalidIndex();
|
|
}
|
|
|
|
return hPartition;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Destroy spatial partition object handle.
|
|
// Input: handle - handle of the spatial partition object handle to destroy
|
|
//-----------------------------------------------------------------------------
|
|
void CSpatialPartition::DestroyHandle( SpatialPartitionHandle_t hPartition )
|
|
{
|
|
if ( hPartition != PARTITION_INVALID_HANDLE )
|
|
{
|
|
RemoveFromTree( hPartition );
|
|
m_HandlesMutex.Lock();
|
|
// memset( &m_aHandles[hPartition], 0xcd, sizeof(EntityInfo_t) );
|
|
m_aHandles.Remove( hPartition );
|
|
m_HandlesMutex.Unlock();
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Create spatial partition object handle and insert it into the tree.
|
|
// Input: pHandleEntity - entity handle of the object to create a spatial partition handle for
|
|
// listMask -
|
|
// mins -
|
|
// maxs -
|
|
//-----------------------------------------------------------------------------
|
|
SpatialPartitionHandle_t CSpatialPartition::CreateHandle( IHandleEntity *pHandleEntity,
|
|
SpatialPartitionListMask_t listMask,
|
|
const Vector &mins, const Vector &maxs )
|
|
{
|
|
// CDebugOverlay::AddBoxOverlay( vec3_origin, mins, maxs, vec3_angle, 0, 255, 0, 75, 3600 );
|
|
|
|
SpatialPartitionHandle_t hPartition = CreateHandle( pHandleEntity );
|
|
Insert( listMask, hPartition );
|
|
InsertIntoTree( hPartition, mins, maxs );
|
|
|
|
return hPartition;
|
|
}
|
|
|
|
|
|
void CSpatialPartition::UpdateListMask( SpatialPartitionHandle_t hPartition, uint16 nListMask )
|
|
{
|
|
EntityInfo_t &entityInfo = EntityInfo( hPartition );
|
|
if ( entityInfo.m_fList != nListMask )
|
|
{
|
|
entityInfo.m_fList = nListMask;
|
|
|
|
if ( entityInfo.m_flags & IN_CLIENT_TREE )
|
|
{
|
|
m_VoxelTrees[CLIENT_TREE].UpdateListMask( hPartition );
|
|
}
|
|
|
|
if ( entityInfo.m_flags & IN_SERVER_TREE )
|
|
{
|
|
m_VoxelTrees[SERVER_TREE].UpdateListMask( hPartition );
|
|
}
|
|
}
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Insert object handle into group(s).
|
|
// Input: listId - list(s) to insert the object handle into
|
|
// handle - object handle to be inserted into list
|
|
//-----------------------------------------------------------------------------
|
|
void CSpatialPartition::Insert( SpatialPartitionListMask_t listId, SpatialPartitionHandle_t handle )
|
|
{
|
|
Assert( m_aHandles.IsValidIndex( handle ) );
|
|
Assert( listId <= USHRT_MAX );
|
|
UpdateListMask( handle, m_aHandles[handle].m_fList | listId );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Remove object handle from group(s).
|
|
// Input: listId - list(s) to remove the object handle from
|
|
// handle - object handle to be removed from list
|
|
//-----------------------------------------------------------------------------
|
|
void CSpatialPartition::Remove( SpatialPartitionListMask_t listId, SpatialPartitionHandle_t handle )
|
|
{
|
|
Assert( m_aHandles.IsValidIndex( handle ) );
|
|
Assert( listId <= USHRT_MAX );
|
|
UpdateListMask( handle, m_aHandles[handle].m_fList & ~listId );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CSpatialPartition::RemoveAndInsert( SpatialPartitionListMask_t removeMask, SpatialPartitionListMask_t insertMask,
|
|
SpatialPartitionHandle_t handle )
|
|
{
|
|
Assert( m_aHandles.IsValidIndex( handle ) );
|
|
Assert( removeMask <= USHRT_MAX );
|
|
Assert( insertMask <= USHRT_MAX );
|
|
uint16 nOriginalListMask = m_aHandles[handle].m_fList;
|
|
uint16 nListMask = (nOriginalListMask & ~removeMask) | insertMask;
|
|
UpdateListMask( handle, nListMask );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Remove object handle from all groups.
|
|
// Input: handle - object handle to be removed from all lists
|
|
//-----------------------------------------------------------------------------
|
|
void CSpatialPartition::Remove( SpatialPartitionHandle_t handle )
|
|
{
|
|
Assert( m_aHandles.IsValidIndex( handle ) );
|
|
UpdateListMask( handle, 0 );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Fast way to re-add (set) a group that was removed (and saved) via the
|
|
// HideElement call.
|
|
//-----------------------------------------------------------------------------
|
|
void CSpatialPartition::UnhideElement( SpatialPartitionHandle_t handle, SpatialTempHandle_t tempHandle )
|
|
{
|
|
Assert( m_aHandles.IsValidIndex( handle ) );
|
|
|
|
m_HandlesMutex.Lock();
|
|
m_aHandles[handle].m_flags &= ~ENTITY_HIDDEN;
|
|
m_HandlesMutex.Unlock();
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Remove handle quickly saving the old list data, to be restored later
|
|
// via the UnhideElement call.
|
|
//-----------------------------------------------------------------------------
|
|
SpatialTempHandle_t CSpatialPartition::HideElement( SpatialPartitionHandle_t handle )
|
|
{
|
|
Assert( m_aHandles.IsValidIndex( handle ) );
|
|
m_HandlesMutex.Lock();
|
|
m_aHandles[handle].m_flags |= ENTITY_HIDDEN;
|
|
m_HandlesMutex.Unlock();
|
|
|
|
return 1;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: (Debugging) Suppress queries on particular lists.
|
|
// Input: nListMask - lists to suppress/unsuppress
|
|
// bSuppress - (true/false) suppress/unsuppress
|
|
//-----------------------------------------------------------------------------
|
|
void CSpatialPartition::SuppressLists( SpatialPartitionListMask_t nListMask, bool bSuppress )
|
|
{
|
|
if ( bSuppress )
|
|
{
|
|
m_nSuppressedListMask |= nListMask;
|
|
}
|
|
else
|
|
{
|
|
m_nSuppressedListMask &= ~nListMask;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: (Debugging) Get the suppression list.
|
|
// Output: spatial partition suppression list
|
|
//-----------------------------------------------------------------------------
|
|
SpatialPartitionListMask_t CSpatialPartition::GetSuppressedLists( void )
|
|
{
|
|
return m_nSuppressedListMask;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CSpatialPartition::ElementMoved( SpatialPartitionHandle_t handle, const Vector& mins, const Vector& maxs )
|
|
{
|
|
EntityInfo_t &entityInfo = EntityInfo( handle );
|
|
SpatialPartitionListMask_t listMask = entityInfo.m_fList;
|
|
|
|
|
|
if ( CLIENT_TREE != SERVER_TREE )
|
|
{
|
|
if ( listMask & PARTITION_ALL_CLIENT_EDICTS )
|
|
{
|
|
m_VoxelTrees[CLIENT_TREE].ElementMoved( handle, mins, maxs );
|
|
entityInfo.m_flags |= IN_CLIENT_TREE;
|
|
}
|
|
|
|
if ( listMask & ~PARTITION_ALL_CLIENT_EDICTS )
|
|
{
|
|
m_VoxelTrees[SERVER_TREE].ElementMoved( handle, mins, maxs );
|
|
entityInfo.m_flags |= IN_SERVER_TREE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_VoxelTrees[CLIENT_TREE].ElementMoved( handle, mins, maxs );
|
|
entityInfo.m_flags |= IN_CLIENT_TREE;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CSpatialPartition::EnumerateElementsInBox( SpatialPartitionListMask_t listMask, const Vector& mins, const Vector& maxs, bool coarseTest, IPartitionEnumerator* pIterator )
|
|
{
|
|
MDLCACHE_CRITICAL_SECTION_(g_pMDLCache);
|
|
CVoxelTree *pTree = VoxelTree( listMask );
|
|
InvokeQueryCallbacks( listMask );
|
|
pTree->EnumerateElementsInBox( listMask, mins, maxs, coarseTest, pIterator );
|
|
InvokeQueryCallbacks( listMask, true );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CSpatialPartition::EnumerateElementsInSphere( SpatialPartitionListMask_t listMask, const Vector& origin, float radius, bool coarseTest, IPartitionEnumerator* pIterator )
|
|
{
|
|
MDLCACHE_CRITICAL_SECTION_(g_pMDLCache);
|
|
CVoxelTree *pTree = VoxelTree( listMask );
|
|
InvokeQueryCallbacks( listMask );
|
|
pTree->EnumerateElementsInSphere( listMask, origin, radius, coarseTest, pIterator );
|
|
InvokeQueryCallbacks( listMask, true );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CSpatialPartition::EnumerateElementsAlongRay( SpatialPartitionListMask_t listMask, const Ray_t& ray, bool coarseTest, IPartitionEnumerator* pIterator )
|
|
{
|
|
MDLCACHE_CRITICAL_SECTION_(g_pMDLCache);
|
|
CVoxelTree *pTree = VoxelTree( listMask );
|
|
InvokeQueryCallbacks( listMask );
|
|
pTree->EnumerateElementsAlongRay( listMask, ray, coarseTest, pIterator );
|
|
InvokeQueryCallbacks( listMask, true );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CSpatialPartition::EnumerateElementsAtPoint( SpatialPartitionListMask_t listMask, const Vector& pt, bool coarseTest, IPartitionEnumerator* pIterator )
|
|
{
|
|
MDLCACHE_CRITICAL_SECTION_(g_pMDLCache);
|
|
CVoxelTree *pTree = VoxelTree( listMask );
|
|
InvokeQueryCallbacks( listMask );
|
|
pTree->EnumerateElementsAtPoint( listMask, pt, coarseTest, pIterator );
|
|
InvokeQueryCallbacks( listMask, true );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CSpatialPartition::InsertIntoTree( SpatialPartitionHandle_t hPartition, const Vector& mins, const Vector& maxs )
|
|
{
|
|
EntityInfo_t &entityInfo = EntityInfo( hPartition );
|
|
SpatialPartitionListMask_t listMask = entityInfo.m_fList;
|
|
|
|
if ( CLIENT_TREE != SERVER_TREE )
|
|
{
|
|
if ( ( listMask & PARTITION_ALL_CLIENT_EDICTS ) && !( entityInfo.m_flags & IN_CLIENT_TREE ) )
|
|
{
|
|
m_VoxelTrees[CLIENT_TREE].InsertIntoTree( hPartition, mins, maxs, false );
|
|
entityInfo.m_flags |= IN_CLIENT_TREE;
|
|
}
|
|
|
|
if ( ( listMask & ~PARTITION_ALL_CLIENT_EDICTS ) && !( entityInfo.m_flags & IN_SERVER_TREE ) )
|
|
{
|
|
m_VoxelTrees[SERVER_TREE].InsertIntoTree( hPartition, mins, maxs, false );
|
|
entityInfo.m_flags |= IN_SERVER_TREE;
|
|
}
|
|
}
|
|
else if ( !( entityInfo.m_flags & IN_CLIENT_TREE ) )
|
|
{
|
|
m_VoxelTrees[CLIENT_TREE].InsertIntoTree( hPartition, mins, maxs, false );
|
|
entityInfo.m_flags |= IN_CLIENT_TREE;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CSpatialPartition::RemoveFromTree( SpatialPartitionHandle_t hPartition )
|
|
{
|
|
EntityInfo_t &entityInfo = EntityInfo( hPartition );
|
|
|
|
if ( entityInfo.m_flags & IN_CLIENT_TREE )
|
|
{
|
|
m_VoxelTrees[CLIENT_TREE].RemoveFromTree( hPartition );
|
|
entityInfo.m_flags &= ~IN_CLIENT_TREE;
|
|
}
|
|
|
|
if ( entityInfo.m_flags & IN_SERVER_TREE )
|
|
{
|
|
m_VoxelTrees[SERVER_TREE].RemoveFromTree( hPartition );
|
|
entityInfo.m_flags &= ~IN_SERVER_TREE;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CSpatialPartition::RenderObjectsInBox( const Vector &vecMin, const Vector &vecMax, float flTime )
|
|
{
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CSpatialPartition::RenderObjectsInSphere( const Vector &vecCenter, float flRadius, float flTime )
|
|
{
|
|
}
|
|
|
|
|
|
void CSpatialPartition::RenderObjectsAlongRay( const Ray_t& ray, float flTime )
|
|
{
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Report stats
|
|
//-----------------------------------------------------------------------------
|
|
void CSpatialPartition::RenderAllObjectsInTree( float flTime )
|
|
{
|
|
for ( int i = 0; i < NUM_TREES; i++ )
|
|
{
|
|
m_VoxelTrees[i].RenderAllObjectsInTree( flTime );
|
|
}
|
|
}
|
|
|
|
void CSpatialPartition::RenderObjectsInPlayerLeafs( const Vector &vecPlayerMin, const Vector &vecPlayerMax, float flTime )
|
|
{
|
|
for ( int i = 0; i < NUM_TREES; i++ )
|
|
{
|
|
m_VoxelTrees[i].RenderObjectsInPlayerLeafs( vecPlayerMin, vecPlayerMax, flTime );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Report stats
|
|
//-----------------------------------------------------------------------------
|
|
void CVoxelTree::ReportStats( const char *pFileName )
|
|
{
|
|
Msg( "Histogram : Entities per level\n" );
|
|
for ( int i = 0; i < m_nLevelCount; ++i )
|
|
{
|
|
Msg( "\t%d - %d\n", i, m_pVoxelHash[i].EntityCount() );
|
|
}
|
|
}
|
|
|
|
void CSpatialPartition::ReportStats( const char *pFileName )
|
|
{
|
|
Msg( "Handle Count %d (%d bytes)\n", m_aHandles.Count(), m_aHandles.Count() * ( sizeof(EntityInfo_t) + 2 * sizeof(SpatialPartitionHandle_t) ) );
|
|
for ( int i = 0; i < NUM_TREES; i++ )
|
|
{
|
|
m_VoxelTrees[i].ReportStats( pFileName );
|
|
}
|
|
}
|
|
|
|
static ConVar r_partition_level( "r_partition_level", "-1", FCVAR_CHEAT, "Displays a particular level of the spatial partition system. Use -1 to disable it." );
|
|
|
|
void CVoxelTree::DrawDebugOverlays()
|
|
{
|
|
int nLevel = r_partition_level.GetInt();
|
|
if ( nLevel < 0 )
|
|
return;
|
|
|
|
m_lock.LockForRead();
|
|
for ( int i = 0; i < m_nLevelCount; ++i )
|
|
{
|
|
if ( ( nLevel >= 0 ) && ( nLevel != i ) )
|
|
continue;
|
|
|
|
m_pVoxelHash[i].RenderGrid();
|
|
m_pVoxelHash[i].RenderAllObjectsInTree( 0.01f );
|
|
}
|
|
m_lock.UnlockRead();
|
|
}
|
|
|
|
void CSpatialPartition::DrawDebugOverlays()
|
|
{
|
|
for ( int i = 0; i < NUM_TREES; i++ )
|
|
{
|
|
m_VoxelTrees[i].DrawDebugOverlays();
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
ISpatialPartition *CreateSpatialPartition( const Vector& worldmin, const Vector& worldmax )
|
|
{
|
|
CSpatialPartition *pResult = new CSpatialPartition;
|
|
pResult->Init( worldmin, worldmax );
|
|
return pResult;
|
|
}
|
|
|
|
void DestroySpatialPartition( ISpatialPartition *pPartition )
|
|
{
|
|
Assert( pPartition != (ISpatialPartition*)&g_SpatialPartition );
|
|
delete pPartition;
|
|
}
|