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.
1546 lines
47 KiB
1546 lines
47 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
//=============================================================================// |
|
|
|
#include "cmodel.h" |
|
#include "dispcoll_common.h" |
|
#include "collisionutils.h" |
|
#include "tier1/strtools.h" |
|
#include "tier0/vprof.h" |
|
#include "tier1/fmtstr.h" |
|
#include "tier1/utlhash.h" |
|
#include "tier1/generichash.h" |
|
#include "tier0/fasttimer.h" |
|
#include "vphysics/virtualmesh.h" |
|
#include "tier1/datamanager.h" |
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
//============================================================================= |
|
// Cache |
|
|
|
#ifdef ENGINE_DLL |
|
CDataManager<CDispCollTree, CDispCollTree *, bool, CThreadFastMutex> g_DispCollTriCache( 2048*1024 ); |
|
#endif |
|
|
|
|
|
struct DispCollPlaneIndex_t |
|
{ |
|
Vector vecPlane; |
|
int index; |
|
}; |
|
|
|
class CPlaneIndexHashFuncs |
|
{ |
|
public: |
|
CPlaneIndexHashFuncs( int ) {} |
|
|
|
// Compare |
|
bool operator()( const DispCollPlaneIndex_t &lhs, const DispCollPlaneIndex_t &rhs ) const |
|
{ |
|
return ( lhs.vecPlane == rhs.vecPlane || lhs.vecPlane == -rhs.vecPlane ); |
|
} |
|
|
|
// Hash |
|
unsigned int operator()( const DispCollPlaneIndex_t &item ) const |
|
{ |
|
return HashItem( item.vecPlane ) ^ HashItem( -item.vecPlane ); |
|
} |
|
}; |
|
|
|
CUtlHash<DispCollPlaneIndex_t, CPlaneIndexHashFuncs, CPlaneIndexHashFuncs> g_DispCollPlaneIndexHash( 512 ); |
|
|
|
|
|
//============================================================================= |
|
// Displacement Collision Triangle |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CDispCollTri::CDispCollTri() |
|
{ |
|
Init(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CDispCollTri::Init( void ) |
|
{ |
|
m_vecNormal.Init(); |
|
m_flDist = 0.0f; |
|
m_TriData[0].m_IndexDummy = m_TriData[1].m_IndexDummy = m_TriData[2].m_IndexDummy = 0; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CDispCollTri::CalcPlane( CDispVector<Vector> &m_aVerts ) |
|
{ |
|
Vector vecEdges[2]; |
|
vecEdges[0] = m_aVerts[GetVert( 1 )] - m_aVerts[GetVert( 0 )]; |
|
vecEdges[1] = m_aVerts[GetVert( 2 )] - m_aVerts[GetVert( 0 )]; |
|
|
|
m_vecNormal = vecEdges[1].Cross( vecEdges[0] ); |
|
VectorNormalize( m_vecNormal ); |
|
m_flDist = m_vecNormal.Dot( m_aVerts[GetVert( 0 )] ); |
|
|
|
// Calculate the signbits for the plane - fast test. |
|
m_ucSignBits = 0; |
|
m_ucPlaneType = PLANE_ANYZ; |
|
for ( int iAxis = 0; iAxis < 3 ; ++iAxis ) |
|
{ |
|
if ( m_vecNormal[iAxis] < 0.0f ) |
|
{ |
|
m_ucSignBits |= 1 << iAxis; |
|
} |
|
|
|
if ( m_vecNormal[iAxis] == 1.0f ) |
|
{ |
|
m_ucPlaneType = iAxis; |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
//----------------------------------------------------------------------------- |
|
inline void FindMin( float v1, float v2, float v3, int &iMin ) |
|
{ |
|
float flMin = v1; |
|
iMin = 0; |
|
if( v2 < flMin ) { flMin = v2; iMin = 1; } |
|
if( v3 < flMin ) { flMin = v3; iMin = 2; } |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
//----------------------------------------------------------------------------- |
|
inline void FindMax( float v1, float v2, float v3, int &iMax ) |
|
{ |
|
float flMax = v1; |
|
iMax = 0; |
|
if( v2 > flMax ) { flMax = v2; iMax = 1; } |
|
if( v3 > flMax ) { flMax = v3; iMax = 2; } |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CDispCollTri::FindMinMax( CDispVector<Vector> &m_aVerts ) |
|
{ |
|
int iMin, iMax; |
|
FindMin( m_aVerts[GetVert(0)].x, m_aVerts[GetVert(1)].x, m_aVerts[GetVert(2)].x, iMin ); |
|
FindMax( m_aVerts[GetVert(0)].x, m_aVerts[GetVert(1)].x, m_aVerts[GetVert(2)].x, iMax ); |
|
SetMin( 0, iMin ); |
|
SetMax( 0, iMax ); |
|
|
|
FindMin( m_aVerts[GetVert(0)].y, m_aVerts[GetVert(1)].y, m_aVerts[GetVert(2)].y, iMin ); |
|
FindMax( m_aVerts[GetVert(0)].y, m_aVerts[GetVert(1)].y, m_aVerts[GetVert(2)].y, iMax ); |
|
SetMin( 1, iMin ); |
|
SetMax( 1, iMax ); |
|
|
|
FindMin( m_aVerts[GetVert(0)].z, m_aVerts[GetVert(1)].z, m_aVerts[GetVert(2)].z, iMin ); |
|
FindMax( m_aVerts[GetVert(0)].z, m_aVerts[GetVert(1)].z, m_aVerts[GetVert(2)].z, iMax ); |
|
SetMin( 2, iMin ); |
|
SetMax( 2, iMax ); |
|
} |
|
|
|
|
|
// SIMD Routines for intersecting with the quad tree |
|
FORCEINLINE int IntersectRayWithFourBoxes( const FourVectors &rayStart, const FourVectors &invDelta, const FourVectors &rayExtents, const FourVectors &boxMins, const FourVectors &boxMaxs ) |
|
{ |
|
// SIMD Test ray against all four boxes at once |
|
// each node stores the bboxes of its four children |
|
FourVectors hitMins = boxMins; |
|
hitMins -= rayStart; |
|
FourVectors hitMaxs = boxMaxs; |
|
hitMaxs -= rayStart; |
|
|
|
// adjust for swept box by enlarging the child bounds to shrink the sweep down to a point |
|
hitMins -= rayExtents; |
|
hitMaxs += rayExtents; |
|
|
|
// compute the parametric distance along the ray of intersection in each dimension |
|
hitMins *= invDelta; |
|
hitMaxs *= invDelta; |
|
|
|
// Find the exit parametric intersection distance in each dimesion, for each box |
|
FourVectors exitT = maximum(hitMins,hitMaxs); |
|
// Find the entry parametric intersection distance in each dimesion, for each box |
|
FourVectors entryT = minimum(hitMins,hitMaxs); |
|
|
|
// now find the max overall entry distance across all dimensions for each box |
|
fltx4 minTemp = MaxSIMD(entryT.x, entryT.y); |
|
fltx4 boxEntryT = MaxSIMD(minTemp, entryT.z); |
|
|
|
// now find the min overall exit distance across all dimensions for each box |
|
fltx4 maxTemp = MinSIMD(exitT.x, exitT.y); |
|
fltx4 boxExitT = MinSIMD(maxTemp, exitT.z); |
|
|
|
boxEntryT = MaxSIMD(boxEntryT,Four_Zeros); |
|
boxExitT = MinSIMD(boxExitT,Four_Ones); |
|
|
|
// if entry<=exit for the box, we've got a hit |
|
fltx4 active = CmpLeSIMD(boxEntryT,boxExitT); // mask of which boxes are active |
|
|
|
// hit at least one box? |
|
return TestSignSIMD(active); |
|
} |
|
|
|
// This does 4 simultaneous box intersections |
|
// NOTE: This can be used as a 1 vs 4 test by replicating a single box into the one side |
|
FORCEINLINE int IntersectFourBoxPairs( const FourVectors &mins0, const FourVectors &maxs0, const FourVectors &mins1, const FourVectors &maxs1 ) |
|
{ |
|
// find the max mins and min maxs in each dimension |
|
FourVectors intersectMins = maximum(mins0,mins1); |
|
FourVectors intersectMaxs = minimum(maxs0,maxs1); |
|
|
|
// if intersectMins <= intersectMaxs then the boxes overlap in this dimension |
|
fltx4 overlapX = CmpLeSIMD(intersectMins.x,intersectMaxs.x); |
|
fltx4 overlapY = CmpLeSIMD(intersectMins.y,intersectMaxs.y); |
|
fltx4 overlapZ = CmpLeSIMD(intersectMins.z,intersectMaxs.z); |
|
|
|
// if the boxes overlap in all three dimensions, they intersect |
|
fltx4 tmp = AndSIMD( overlapX, overlapY ); |
|
fltx4 active = AndSIMD( tmp, overlapZ ); |
|
|
|
// hit at least one box? |
|
return TestSignSIMD(active); |
|
} |
|
|
|
// This does 4 simultaneous box vs. sphere intersections |
|
// NOTE: This can be used as a 1 vs 4 test by replicating a single sphere/box into one side |
|
FORCEINLINE int IntersectFourBoxSpherePairs( const FourVectors ¢er, const fltx4 &radiusSq, const FourVectors &mins, const FourVectors &maxs ) |
|
{ |
|
// for each dimension of each box, compute the clamped distance from the mins side to the center (must be >= 0) |
|
FourVectors minDist = mins; |
|
minDist -= center; |
|
FourVectors dist; |
|
dist.x = MaxSIMD(Four_Zeros, minDist.x); |
|
dist.y = MaxSIMD(Four_Zeros, minDist.y); |
|
dist.z = MaxSIMD(Four_Zeros, minDist.z); |
|
|
|
// now compute the distance from the maxs side to the center |
|
FourVectors maxDist = center; |
|
maxDist -= maxs; |
|
// NOTE: Don't need to clamp here because we clamp against the minDist which must be >= 0, so the two clamps |
|
// get folded together |
|
FourVectors totalDist; |
|
totalDist.x = MaxSIMD(dist.x, maxDist.x); |
|
totalDist.y = MaxSIMD(dist.y, maxDist.y); |
|
totalDist.z = MaxSIMD(dist.z, maxDist.z); |
|
// get the total squred distance between each box & sphere center by summing the squares of each |
|
// component/dimension |
|
fltx4 distSq = totalDist * totalDist; |
|
|
|
// if squared distance between each sphere center & box is less than the radiusSquared for that sphere |
|
// we have an intersection |
|
fltx4 active = CmpLeSIMD(distSq,radiusSq); |
|
|
|
// at least one intersection? |
|
return TestSignSIMD(active); |
|
} |
|
|
|
|
|
int FORCEINLINE CDispCollTree::BuildRayLeafList( int iNode, rayleaflist_t &list ) |
|
{ |
|
list.nodeList[0] = iNode; |
|
int listIndex = 0; |
|
list.maxIndex = 0; |
|
while ( listIndex <= list.maxIndex ) |
|
{ |
|
iNode = list.nodeList[listIndex]; |
|
// the rest are all leaves |
|
if ( IsLeafNode(iNode) ) |
|
return listIndex; |
|
listIndex++; |
|
const CDispCollNode &node = m_nodes[iNode]; |
|
int mask = IntersectRayWithFourBoxes( list.rayStart, list.invDelta, list.rayExtents, node.m_mins, node.m_maxs ); |
|
if ( mask ) |
|
{ |
|
int child = Nodes_GetChild( iNode, 0 ); |
|
if ( mask & 1 ) |
|
{ |
|
++list.maxIndex; |
|
list.nodeList[list.maxIndex] = child; |
|
} |
|
if ( mask & 2 ) |
|
{ |
|
++list.maxIndex; |
|
list.nodeList[list.maxIndex] = child+1; |
|
} |
|
if ( mask & 4 ) |
|
{ |
|
++list.maxIndex; |
|
list.nodeList[list.maxIndex] = child+2; |
|
} |
|
if ( mask & 8 ) |
|
{ |
|
++list.maxIndex; |
|
list.nodeList[list.maxIndex] = child+3; |
|
} |
|
Assert(list.maxIndex < MAX_AABB_LIST); |
|
} |
|
} |
|
|
|
return listIndex; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Create the AABB tree. |
|
//----------------------------------------------------------------------------- |
|
bool CDispCollTree::AABBTree_Create( CCoreDispInfo *pDisp ) |
|
{ |
|
// Copy the flags. |
|
m_nFlags = pDisp->GetSurface()->GetFlags(); |
|
|
|
// Copy necessary displacement data. |
|
AABBTree_CopyDispData( pDisp ); |
|
|
|
// Setup/create the leaf nodes first so the recusion can use this data to stop. |
|
AABBTree_CreateLeafs(); |
|
|
|
// Create the bounding box of the displacement surface + the base face. |
|
AABBTree_CalcBounds(); |
|
|
|
// Successful. |
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CDispCollTree::AABBTree_CopyDispData( CCoreDispInfo *pDisp ) |
|
{ |
|
// Displacement size. |
|
m_nPower = pDisp->GetPower(); |
|
|
|
// Displacement base surface data. |
|
CCoreDispSurface *pSurf = pDisp->GetSurface(); |
|
m_nContents = pSurf->GetContents(); |
|
pSurf->GetNormal( m_vecStabDir ); |
|
for ( int iPoint = 0; iPoint < 4; iPoint++ ) |
|
{ |
|
pSurf->GetPoint( iPoint, m_vecSurfPoints[iPoint] ); |
|
} |
|
|
|
// Allocate collision tree data. |
|
{ |
|
MEM_ALLOC_CREDIT(); |
|
m_aVerts.SetSize( GetSize() ); |
|
} |
|
|
|
{ |
|
MEM_ALLOC_CREDIT(); |
|
m_aTris.SetSize( GetTriSize() ); |
|
} |
|
|
|
{ |
|
MEM_ALLOC_CREDIT(); |
|
int numLeaves = (GetWidth()-1) * (GetHeight()-1); |
|
m_leaves.SetCount(numLeaves); |
|
int numNodes = Nodes_CalcCount( m_nPower ); |
|
numNodes -= numLeaves; |
|
m_nodes.SetCount(numNodes); |
|
} |
|
|
|
|
|
// Setup size. |
|
m_nSize = sizeof( this ); |
|
m_nSize += sizeof( Vector ) * GetSize(); |
|
m_nSize += sizeof( CDispCollTri ) * GetTriSize(); |
|
#if OLD_DISP_AABB |
|
m_nSize += sizeof( CDispCollAABBNode ) * Nodes_CalcCount( m_nPower ); |
|
#endif |
|
m_nSize += sizeof(m_nodes[0]) * m_nodes.Count(); |
|
m_nSize += sizeof(m_leaves[0]) * m_leaves.Count(); |
|
m_nSize += sizeof( CDispCollTri* ) * DISPCOLL_TREETRI_SIZE; |
|
|
|
// Copy vertex data. |
|
for ( int iVert = 0; iVert < m_aVerts.Count(); iVert++ ) |
|
{ |
|
pDisp->GetVert( iVert, m_aVerts[iVert] ); |
|
} |
|
|
|
// Copy and setup triangle data. |
|
unsigned short iVerts[3]; |
|
for ( int iTri = 0; iTri < m_aTris.Count(); ++iTri ) |
|
{ |
|
pDisp->GetTriIndices( iTri, iVerts[0], iVerts[1], iVerts[2] ); |
|
m_aTris[iTri].SetVert( 0, iVerts[0] ); |
|
m_aTris[iTri].SetVert( 1, iVerts[1] ); |
|
m_aTris[iTri].SetVert( 2, iVerts[2] ); |
|
m_aTris[iTri].m_uiFlags = pDisp->GetTriTagValue( iTri ); |
|
|
|
// Calculate the surface props and set flags. |
|
float flTotalAlpha = 0.0f; |
|
for ( int iVert = 0; iVert < 3; ++iVert ) |
|
{ |
|
flTotalAlpha += pDisp->GetAlpha( m_aTris[iTri].GetVert( iVert ) ); |
|
} |
|
|
|
if ( flTotalAlpha > DISP_ALPHA_PROP_DELTA ) |
|
{ |
|
m_aTris[iTri].m_uiFlags |= DISPSURF_FLAG_SURFPROP2; |
|
} |
|
else |
|
{ |
|
m_aTris[iTri].m_uiFlags |= DISPSURF_FLAG_SURFPROP1; |
|
} |
|
|
|
// Add the displacement surface flag. |
|
m_aTris[iTri].m_uiFlags |= DISPSURF_FLAG_SURFACE; |
|
|
|
// Calculate the plane normal and the min max. |
|
m_aTris[iTri].CalcPlane( m_aVerts ); |
|
m_aTris[iTri].FindMinMax( m_aVerts ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CDispCollTree::AABBTree_CreateLeafs( void ) |
|
{ |
|
int numLeaves = (GetWidth()-1) * (GetHeight()-1); |
|
m_leaves.SetCount(numLeaves); |
|
int numNodes = Nodes_CalcCount( m_nPower ); |
|
numNodes -= numLeaves; |
|
m_nodes.SetCount(numNodes); |
|
|
|
// Get the width and height of the displacement. |
|
int nWidth = GetWidth() - 1; |
|
int nHeight = GetHeight() - 1; |
|
|
|
for ( int iHgt = 0; iHgt < nHeight; ++iHgt ) |
|
{ |
|
for ( int iWid = 0; iWid < nWidth; ++iWid ) |
|
{ |
|
int iLeaf = Nodes_GetIndexFromComponents( iWid, iHgt ); |
|
int iIndex = iHgt * nWidth + iWid; |
|
int iTri = iIndex * 2; |
|
|
|
m_leaves[iLeaf].m_tris[0] = iTri; |
|
m_leaves[iLeaf].m_tris[1] = iTri + 1; |
|
} |
|
} |
|
} |
|
|
|
#if COMPILER_CLANG |
|
#define NOASAN __attribute__((no_sanitize("address"))) |
|
#else |
|
#define NOASAN |
|
#endif |
|
|
|
void NOASAN CDispCollTree::AABBTree_GenerateBoxes_r( int nodeIndex, Vector *pMins, Vector *pMaxs ) |
|
{ |
|
// leaf |
|
ClearBounds( *pMins, *pMaxs ); |
|
if ( nodeIndex >= m_nodes.Count() ) |
|
{ |
|
int iLeaf = nodeIndex - m_nodes.Count(); |
|
|
|
for ( int iTri = 0; iTri < 2; ++iTri ) |
|
{ |
|
int triIndex = m_leaves[iLeaf].m_tris[iTri]; |
|
const CDispCollTri &tri = m_aTris[triIndex]; |
|
AddPointToBounds( m_aVerts[tri.GetVert( 0 )], *pMins, *pMaxs ); |
|
AddPointToBounds( m_aVerts[tri.GetVert( 1 )], *pMins, *pMaxs ); |
|
AddPointToBounds( m_aVerts[tri.GetVert( 2 )], *pMins, *pMaxs ); |
|
} |
|
} |
|
else // node |
|
{ |
|
Vector childMins[4], childMaxs[4]; |
|
for ( int i = 0; i < 4; i++ ) |
|
{ |
|
int child = Nodes_GetChild( nodeIndex, i ); |
|
AABBTree_GenerateBoxes_r( child, &childMins[i], &childMaxs[i] ); |
|
AddPointToBounds( childMins[i], *pMins, *pMaxs ); |
|
AddPointToBounds( childMaxs[i], *pMins, *pMaxs ); |
|
} |
|
m_nodes[nodeIndex].m_mins.LoadAndSwizzle( childMins[0], childMins[1], childMins[2], childMins[3] ); |
|
m_nodes[nodeIndex].m_maxs.LoadAndSwizzle( childMaxs[0], childMaxs[1], childMaxs[2], childMaxs[3] ); |
|
} |
|
} |
|
|
|
#undef NOASAN |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CDispCollTree::AABBTree_CalcBounds( void ) |
|
{ |
|
// Check data. |
|
if ( ( m_aVerts.Count() == 0 ) || ( m_nodes.Count() == 0 ) ) |
|
return; |
|
|
|
AABBTree_GenerateBoxes_r( 0, &m_mins, &m_maxs ); |
|
|
|
#if INCLUDE_SURFACE_IN_BOUNDS |
|
// Add surface points to bounds. |
|
for ( int iPoint = 0; iPoint < 4; ++iPoint ) |
|
{ |
|
VectorMin( m_vecSurfPoints[iPoint], m_mins, m_mins ); |
|
VectorMax( m_vecSurfPoints[iPoint], m_maxs, m_maxs ); |
|
} |
|
#endif |
|
|
|
// Bloat a little. |
|
for ( int iAxis = 0; iAxis < 3; ++iAxis ) |
|
{ |
|
m_mins[iAxis] -= 1.0f; |
|
m_maxs[iAxis] += 1.0f; |
|
} |
|
} |
|
|
|
static CThreadFastMutex s_CacheMutex; |
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
inline void CDispCollTree::LockCache() |
|
{ |
|
#ifdef ENGINE_DLL |
|
if ( !g_DispCollTriCache.LockResource( m_hCache ) ) |
|
{ |
|
AUTO_LOCK( s_CacheMutex ); |
|
|
|
// Cache may have just been created, so check once more |
|
if ( !g_DispCollTriCache.LockResource( m_hCache ) ) |
|
{ |
|
Cache(); |
|
m_hCache = g_DispCollTriCache.CreateResource( this ); |
|
g_DispCollTriCache.LockResource( m_hCache ); |
|
//Msg( "Adding 0x%x to cache (actual %d) [%d, %d --> %.2f] %d total, %d unique\n", this, GetCacheMemorySize(), GetTriSize(), m_aEdgePlanes.Count(), (float)m_aEdgePlanes.Count()/(float)GetTriSize(), totals, uniques ); |
|
} |
|
} |
|
#else |
|
Cache(); |
|
#endif |
|
} |
|
|
|
inline void CDispCollTree::UnlockCache() |
|
{ |
|
#ifdef ENGINE_DLL |
|
g_DispCollTriCache.UnlockResource( m_hCache ); |
|
#endif |
|
} |
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CDispCollTree::Cache( void ) |
|
{ |
|
if ( m_aTrisCache.Count() == GetTriSize() ) |
|
{ |
|
return; |
|
} |
|
|
|
VPROF( "CDispCollTree::Cache" ); |
|
|
|
// Alloc. |
|
// int nSize = sizeof( CDispCollTriCache ) * GetTriSize(); |
|
int nTriCount = GetTriSize(); |
|
{ |
|
MEM_ALLOC_CREDIT(); |
|
m_aTrisCache.SetSize( nTriCount ); |
|
} |
|
|
|
for ( int iTri = 0; iTri < nTriCount; ++iTri ) |
|
{ |
|
Cache_Create( &m_aTris[iTri], iTri ); |
|
} |
|
|
|
g_DispCollPlaneIndexHash.Purge(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CDispCollTree::AABBTree_Ray( const Ray_t &ray, RayDispOutput_t &output ) |
|
{ |
|
if ( IsBoxIntersectingRay( m_mins, m_maxs, ray.m_Start, ray.m_Delta, DISPCOLL_DIST_EPSILON ) ) |
|
{ |
|
return AABBTree_Ray( ray, ray.InvDelta(), output ); |
|
} |
|
return false; |
|
} |
|
|
|
bool CDispCollTree::AABBTree_Ray( const Ray_t &ray, const Vector &vecInvDelta, RayDispOutput_t &output ) |
|
{ |
|
VPROF( "DispRayTest" ); |
|
|
|
// Check for ray test. |
|
if ( CheckFlags( CCoreDispInfo::SURF_NORAY_COLL ) ) |
|
return false; |
|
|
|
// Check for opacity. |
|
if ( !( m_nContents & MASK_OPAQUE ) ) |
|
return false; |
|
|
|
// Pre-calc the inverse delta for perf. |
|
CDispCollTri *pImpactTri = NULL; |
|
|
|
AABBTree_TreeTrisRayBarycentricTest( ray, vecInvDelta, DISPCOLL_ROOTNODE_INDEX, output, &pImpactTri ); |
|
|
|
if ( pImpactTri ) |
|
{ |
|
// Collision. |
|
output.ndxVerts[0] = pImpactTri->GetVert( 0 ); |
|
output.ndxVerts[1] = pImpactTri->GetVert( 2 ); |
|
output.ndxVerts[2] = pImpactTri->GetVert( 1 ); |
|
|
|
Assert( (output.u <= 1.0f ) && ( output.v <= 1.0f ) ); |
|
Assert( (output.u >= 0.0f ) && ( output.v >= 0.0f ) ); |
|
|
|
return true; |
|
} |
|
|
|
// No collision. |
|
return false; |
|
} |
|
|
|
void CDispCollTree::AABBTree_TreeTrisRayBarycentricTest( const Ray_t &ray, const Vector &vecInvDelta, int iNode, RayDispOutput_t &output, CDispCollTri **pImpactTri ) |
|
{ |
|
rayleaflist_t list; |
|
// NOTE: This part is loop invariant - should be hoisted up as far as possible |
|
list.invDelta.DuplicateVector(vecInvDelta); |
|
list.rayStart.DuplicateVector(ray.m_Start); |
|
Vector ext = ray.m_Extents + Vector(DISPCOLL_DIST_EPSILON,DISPCOLL_DIST_EPSILON,DISPCOLL_DIST_EPSILON); |
|
list.rayExtents.DuplicateVector(ext); |
|
int listIndex = BuildRayLeafList( iNode, list ); |
|
|
|
float flU, flV, flT; |
|
for ( ; listIndex <= list.maxIndex; listIndex++ ) |
|
{ |
|
int leafIndex = list.nodeList[listIndex] - m_nodes.Count(); |
|
CDispCollTri *pTri0 = &m_aTris[m_leaves[leafIndex].m_tris[0]]; |
|
CDispCollTri *pTri1 = &m_aTris[m_leaves[leafIndex].m_tris[1]]; |
|
|
|
if ( ComputeIntersectionBarycentricCoordinates( ray, m_aVerts[pTri0->GetVert( 0 )], m_aVerts[pTri0->GetVert( 2 )], m_aVerts[pTri0->GetVert( 1 )], flU, flV, &flT ) ) |
|
{ |
|
// Make sure it's inside the range |
|
if ( ( flU >= 0.0f ) && ( flV >= 0.0f ) && ( ( flU + flV ) <= 1.0f ) ) |
|
{ |
|
if( ( flT > 0.0f ) && ( flT < output.dist ) ) |
|
{ |
|
(*pImpactTri) = pTri0; |
|
output.u = flU; |
|
output.v = flV; |
|
output.dist = flT; |
|
} |
|
} |
|
} |
|
if ( ComputeIntersectionBarycentricCoordinates( ray, m_aVerts[pTri1->GetVert( 0 )], m_aVerts[pTri1->GetVert( 2 )], m_aVerts[pTri1->GetVert( 1 )], flU, flV, &flT ) ) |
|
{ |
|
// Make sure it's inside the range |
|
if ( ( flU >= 0.0f ) && ( flV >= 0.0f ) && ( ( flU + flV ) <= 1.0f ) ) |
|
{ |
|
if( ( flT > 0.0f ) && ( flT < output.dist ) ) |
|
{ |
|
(*pImpactTri) = pTri1; |
|
output.u = flU; |
|
output.v = flV; |
|
output.dist = flT; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CDispCollTree::AABBTree_Ray( const Ray_t &ray, const Vector &vecInvDelta, CBaseTrace *pTrace, bool bSide ) |
|
{ |
|
VPROF("AABBTree_Ray"); |
|
|
|
// VPROF_BUDGET( "DispRayTraces", VPROF_BUDGETGROUP_DISP_RAYTRACES ); |
|
|
|
// Check for ray test. |
|
if ( CheckFlags( CCoreDispInfo::SURF_NORAY_COLL ) ) |
|
return false; |
|
|
|
// Check for opacity. |
|
if ( !( m_nContents & MASK_OPAQUE ) ) |
|
return false; |
|
|
|
// Pre-calc the inverse delta for perf. |
|
CDispCollTri *pImpactTri = NULL; |
|
|
|
AABBTree_TreeTrisRayTest( ray, vecInvDelta, DISPCOLL_ROOTNODE_INDEX, pTrace, bSide, &pImpactTri ); |
|
|
|
if ( pImpactTri ) |
|
{ |
|
// Collision. |
|
VectorCopy( pImpactTri->m_vecNormal, pTrace->plane.normal ); |
|
pTrace->plane.dist = pImpactTri->m_flDist; |
|
pTrace->dispFlags = pImpactTri->m_uiFlags; |
|
return true; |
|
} |
|
|
|
// No collision. |
|
return false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CDispCollTree::AABBTree_TreeTrisRayTest( const Ray_t &ray, const Vector &vecInvDelta, int iNode, CBaseTrace *pTrace, bool bSide, CDispCollTri **pImpactTri ) |
|
{ |
|
rayleaflist_t list; |
|
// NOTE: This part is loop invariant - should be hoisted up as far as possible |
|
list.invDelta.DuplicateVector(vecInvDelta); |
|
list.rayStart.DuplicateVector(ray.m_Start); |
|
Vector ext = ray.m_Extents + Vector(DISPCOLL_DIST_EPSILON,DISPCOLL_DIST_EPSILON,DISPCOLL_DIST_EPSILON); |
|
list.rayExtents.DuplicateVector(ext); |
|
int listIndex = BuildRayLeafList( iNode, list ); |
|
|
|
for ( ;listIndex <= list.maxIndex; listIndex++ ) |
|
{ |
|
int leafIndex = list.nodeList[listIndex] - m_nodes.Count(); |
|
CDispCollTri *pTri0 = &m_aTris[m_leaves[leafIndex].m_tris[0]]; |
|
CDispCollTri *pTri1 = &m_aTris[m_leaves[leafIndex].m_tris[1]]; |
|
float flFrac = IntersectRayWithTriangle( ray, m_aVerts[pTri0->GetVert( 0 )], m_aVerts[pTri0->GetVert( 2 )], m_aVerts[pTri0->GetVert( 1 )], bSide ); |
|
if( ( flFrac >= 0.0f ) && ( flFrac < pTrace->fraction ) ) |
|
{ |
|
pTrace->fraction = flFrac; |
|
(*pImpactTri) = pTri0; |
|
} |
|
|
|
flFrac = IntersectRayWithTriangle( ray, m_aVerts[pTri1->GetVert( 0 )], m_aVerts[pTri1->GetVert( 2 )], m_aVerts[pTri1->GetVert( 1 )], bSide ); |
|
if( ( flFrac >= 0.0f ) && ( flFrac < pTrace->fraction ) ) |
|
{ |
|
pTrace->fraction = flFrac; |
|
(*pImpactTri) = pTri1; |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
int CDispCollTree::AABBTree_GetTrisInSphere( const Vector ¢er, float radius, unsigned short *pIndexOut, int indexMax ) |
|
{ |
|
return AABBTree_BuildTreeTrisInSphere_r( center, radius, DISPCOLL_ROOTNODE_INDEX, pIndexOut, indexMax ); |
|
} |
|
|
|
int CDispCollTree::AABBTree_BuildTreeTrisInSphere_r( const Vector ¢er, float radius, int iNode, unsigned short *pIndexOut, unsigned short indexMax ) |
|
{ |
|
int nodeList[MAX_AABB_LIST]; |
|
nodeList[0] = iNode; |
|
int nTriCount = 0; |
|
int listIndex = 0; |
|
int maxIndex = 0; |
|
// NOTE: This part is loop invariant - should be hoisted up as far as possible |
|
FourVectors sphereCenters; |
|
sphereCenters.DuplicateVector(center); |
|
float radiusSq = radius * radius; |
|
fltx4 sphereRadSq = ReplicateX4(radiusSq); |
|
while ( listIndex <= maxIndex ) |
|
{ |
|
iNode = nodeList[listIndex]; |
|
listIndex++; |
|
// the rest are all leaves |
|
if ( IsLeafNode(iNode) ) |
|
{ |
|
VPROF("Tris"); |
|
for ( --listIndex; listIndex <= maxIndex; listIndex++ ) |
|
{ |
|
if ( (nTriCount+2) <= indexMax ) |
|
{ |
|
int leafIndex = nodeList[listIndex] - m_nodes.Count(); |
|
pIndexOut[nTriCount] = m_leaves[leafIndex].m_tris[0]; |
|
pIndexOut[nTriCount+1] = m_leaves[leafIndex].m_tris[1]; |
|
nTriCount += 2; |
|
} |
|
} |
|
break; |
|
} |
|
else |
|
{ |
|
const CDispCollNode &node = m_nodes[iNode]; |
|
int mask = IntersectFourBoxSpherePairs( sphereCenters, sphereRadSq, node.m_mins, node.m_maxs ); |
|
if ( mask ) |
|
{ |
|
int child = Nodes_GetChild( iNode, 0 ); |
|
if ( mask & 1 ) |
|
{ |
|
++maxIndex; |
|
nodeList[maxIndex] = child; |
|
} |
|
if ( mask & 2 ) |
|
{ |
|
++maxIndex; |
|
nodeList[maxIndex] = child+1; |
|
} |
|
if ( mask & 4 ) |
|
{ |
|
++maxIndex; |
|
nodeList[maxIndex] = child+2; |
|
} |
|
if ( mask & 8 ) |
|
{ |
|
++maxIndex; |
|
nodeList[maxIndex] = child+3; |
|
} |
|
Assert(maxIndex < MAX_AABB_LIST); |
|
} |
|
} |
|
} |
|
return nTriCount; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CDispCollTree::AABBTree_IntersectAABB( const Vector &absMins, const Vector &absMaxs ) |
|
{ |
|
// Check for hull test. |
|
if ( CheckFlags( CCoreDispInfo::SURF_NOHULL_COLL ) ) |
|
return false; |
|
|
|
cplane_t plane; |
|
Vector center = 0.5f *(absMins + absMaxs); |
|
Vector extents = absMaxs - center; |
|
|
|
int nodeList[MAX_AABB_LIST]; |
|
nodeList[0] = 0; |
|
int listIndex = 0; |
|
int maxIndex = 0; |
|
|
|
// NOTE: This part is loop invariant - should be hoisted up as far as possible |
|
FourVectors mins0; |
|
mins0.DuplicateVector(absMins); |
|
FourVectors maxs0; |
|
maxs0.DuplicateVector(absMaxs); |
|
FourVectors rayExtents; |
|
while ( listIndex <= maxIndex ) |
|
{ |
|
int iNode = nodeList[listIndex]; |
|
listIndex++; |
|
// the rest are all leaves |
|
if ( IsLeafNode(iNode) ) |
|
{ |
|
VPROF("Tris"); |
|
for ( --listIndex; listIndex <= maxIndex; listIndex++ ) |
|
{ |
|
int leafIndex = nodeList[listIndex] - m_nodes.Count(); |
|
CDispCollTri *pTri0 = &m_aTris[m_leaves[leafIndex].m_tris[0]]; |
|
CDispCollTri *pTri1 = &m_aTris[m_leaves[leafIndex].m_tris[1]]; |
|
|
|
VectorCopy( pTri0->m_vecNormal, plane.normal ); |
|
plane.dist = pTri0->m_flDist; |
|
plane.signbits = pTri0->m_ucSignBits; |
|
plane.type = pTri0->m_ucPlaneType; |
|
|
|
if ( IsBoxIntersectingTriangle( center, extents, |
|
m_aVerts[pTri0->GetVert( 0 )], |
|
m_aVerts[pTri0->GetVert( 2 )], |
|
m_aVerts[pTri0->GetVert( 1 )], |
|
plane, 0.0f ) ) |
|
return true; |
|
VectorCopy( pTri1->m_vecNormal, plane.normal ); |
|
plane.dist = pTri1->m_flDist; |
|
plane.signbits = pTri1->m_ucSignBits; |
|
plane.type = pTri1->m_ucPlaneType; |
|
|
|
if ( IsBoxIntersectingTriangle( center, extents, |
|
m_aVerts[pTri1->GetVert( 0 )], |
|
m_aVerts[pTri1->GetVert( 2 )], |
|
m_aVerts[pTri1->GetVert( 1 )], |
|
plane, 0.0f ) ) |
|
return true; |
|
} |
|
break; |
|
} |
|
else |
|
{ |
|
const CDispCollNode &node = m_nodes[iNode]; |
|
int mask = IntersectFourBoxPairs( mins0, maxs0, node.m_mins, node.m_maxs ); |
|
if ( mask ) |
|
{ |
|
int child = Nodes_GetChild( iNode, 0 ); |
|
if ( mask & 1 ) |
|
{ |
|
++maxIndex; |
|
nodeList[maxIndex] = child; |
|
} |
|
if ( mask & 2 ) |
|
{ |
|
++maxIndex; |
|
nodeList[maxIndex] = child+1; |
|
} |
|
if ( mask & 4 ) |
|
{ |
|
++maxIndex; |
|
nodeList[maxIndex] = child+2; |
|
} |
|
if ( mask & 8 ) |
|
{ |
|
++maxIndex; |
|
nodeList[maxIndex] = child+3; |
|
} |
|
Assert(maxIndex < MAX_AABB_LIST); |
|
} |
|
} |
|
} |
|
|
|
// no collision |
|
return false; |
|
} |
|
|
|
static const Vector g_Vec3DispCollEpsilons(DISPCOLL_DIST_EPSILON,DISPCOLL_DIST_EPSILON,DISPCOLL_DIST_EPSILON); |
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CDispCollTree::AABBTree_SweepAABB( const Ray_t &ray, const Vector &vecInvDelta, CBaseTrace *pTrace ) |
|
{ |
|
VPROF( "DispHullTest" ); |
|
// VPROF_BUDGET( "DispHullTraces", VPROF_BUDGETGROUP_DISP_HULLTRACES ); |
|
// Check for hull test. |
|
if ( CheckFlags( CCoreDispInfo::SURF_NOHULL_COLL ) ) |
|
return false; |
|
|
|
// Test ray against the triangles in the list. |
|
Vector rayDir; |
|
VectorCopy( ray.m_Delta, rayDir ); |
|
VectorNormalize( rayDir ); |
|
// Save fraction. |
|
float flFrac = pTrace->fraction; |
|
|
|
rayleaflist_t list; |
|
// NOTE: This part is loop invariant - should be hoisted up as far as possible |
|
list.invDelta.DuplicateVector(vecInvDelta); |
|
list.rayStart.DuplicateVector(ray.m_Start); |
|
Vector ext = ray.m_Extents + g_Vec3DispCollEpsilons; |
|
list.rayExtents.DuplicateVector(ext); |
|
int listIndex = BuildRayLeafList( 0, list ); |
|
|
|
if ( listIndex <= list.maxIndex ) |
|
{ |
|
LockCache(); |
|
for ( ; listIndex <= list.maxIndex; listIndex++ ) |
|
{ |
|
int leafIndex = list.nodeList[listIndex] - m_nodes.Count(); |
|
int iTri0 = m_leaves[leafIndex].m_tris[0]; |
|
int iTri1 = m_leaves[leafIndex].m_tris[1]; |
|
CDispCollTri *pTri0 = &m_aTris[iTri0]; |
|
CDispCollTri *pTri1 = &m_aTris[iTri1]; |
|
|
|
SweepAABBTriIntersect( ray, rayDir, iTri0, pTri0, pTrace ); |
|
SweepAABBTriIntersect( ray, rayDir, iTri1, pTri1, pTrace ); |
|
} |
|
UnlockCache(); |
|
} |
|
|
|
// Collision. |
|
if ( pTrace->fraction < flFrac ) |
|
return true; |
|
|
|
// No collision. |
|
return false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
//----------------------------------------------------------------------------- |
|
bool CDispCollTree::ResolveRayPlaneIntersect( float flStart, float flEnd, const Vector &vecNormal, float flDist, CDispCollHelper *pHelper ) |
|
{ |
|
if( ( flStart > 0.0f ) && ( flEnd > 0.0f ) ) |
|
return false; |
|
|
|
if( ( flStart < 0.0f ) && ( flEnd < 0.0f ) ) |
|
return true; |
|
|
|
float flDenom = flStart - flEnd; |
|
bool bDenomIsZero = ( flDenom == 0.0f ); |
|
if( ( flStart >= 0.0f ) && ( flEnd <= 0.0f ) ) |
|
{ |
|
// Find t - the parametric distance along the trace line. |
|
float t = ( !bDenomIsZero ) ? ( flStart - DISPCOLL_DIST_EPSILON ) / flDenom : 0.0f; |
|
if( t > pHelper->m_flStartFrac ) |
|
{ |
|
pHelper->m_flStartFrac = t; |
|
VectorCopy( vecNormal, pHelper->m_vecImpactNormal ); |
|
pHelper->m_flImpactDist = flDist; |
|
} |
|
} |
|
else |
|
{ |
|
// Find t - the parametric distance along the trace line. |
|
float t = ( !bDenomIsZero ) ? ( flStart + DISPCOLL_DIST_EPSILON ) / flDenom : 0.0f; |
|
if( t < pHelper->m_flEndFrac ) |
|
{ |
|
pHelper->m_flEndFrac = t; |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
inline bool CDispCollTree::FacePlane( const Ray_t &ray, const Vector &rayDir, CDispCollTri *pTri, CDispCollHelper *pHelper ) |
|
{ |
|
// Calculate the closest point on box to plane (get extents in that direction). |
|
Vector vecExtent; |
|
CalcClosestExtents( pTri->m_vecNormal, ray.m_Extents, vecExtent ); |
|
|
|
float flExpandDist = pTri->m_flDist - pTri->m_vecNormal.Dot( vecExtent ); |
|
|
|
float flStart = pTri->m_vecNormal.Dot( ray.m_Start ) - flExpandDist; |
|
float flEnd = pTri->m_vecNormal.Dot( ( ray.m_Start + ray.m_Delta ) ) - flExpandDist; |
|
|
|
return ResolveRayPlaneIntersect( flStart, flEnd, pTri->m_vecNormal, pTri->m_flDist, pHelper ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool FORCEINLINE CDispCollTree::AxisPlanesXYZ( const Ray_t &ray, CDispCollTri *pTri, CDispCollHelper *pHelper ) |
|
{ |
|
static const TableVector g_ImpactNormalVecs[2][3] = |
|
{ |
|
{ |
|
{ -1, 0, 0 }, |
|
{ 0, -1, 0 }, |
|
{ 0, 0, -1 }, |
|
}, |
|
|
|
{ |
|
{ 1, 0, 0 }, |
|
{ 0, 1, 0 }, |
|
{ 0, 0, 1 }, |
|
} |
|
}; |
|
|
|
Vector vecImpactNormal; |
|
float flDist, flExpDist, flStart, flEnd; |
|
|
|
int iAxis; |
|
for ( iAxis = 2; iAxis >= 0; --iAxis ) |
|
{ |
|
const float rayStart = ray.m_Start[iAxis]; |
|
const float rayExtent = ray.m_Extents[iAxis]; |
|
const float rayDelta = ray.m_Delta[iAxis]; |
|
|
|
// Min |
|
flDist = m_aVerts[pTri->GetVert(pTri->GetMin(iAxis))][iAxis]; |
|
flExpDist = flDist - rayExtent; |
|
flStart = flExpDist - rayStart; |
|
flEnd = flStart - rayDelta; |
|
|
|
if ( !ResolveRayPlaneIntersect( flStart, flEnd, g_ImpactNormalVecs[0][iAxis], flDist, pHelper ) ) |
|
return false; |
|
|
|
// Max |
|
flDist = m_aVerts[pTri->GetVert(pTri->GetMax(iAxis))][iAxis]; |
|
flExpDist = flDist + rayExtent; |
|
flStart = rayStart - flExpDist; |
|
flEnd = flStart + rayDelta; |
|
|
|
if ( !ResolveRayPlaneIntersect( flStart, flEnd, g_ImpactNormalVecs[1][iAxis], flDist, pHelper ) ) |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Testing! |
|
//----------------------------------------------------------------------------- |
|
void CDispCollTree::Cache_Create( CDispCollTri *pTri, int iTri ) |
|
{ |
|
MEM_ALLOC_CREDIT(); |
|
Vector *pVerts[3]; |
|
pVerts[0] = &m_aVerts[pTri->GetVert( 0 )]; |
|
pVerts[1] = &m_aVerts[pTri->GetVert( 1 )]; |
|
pVerts[2] = &m_aVerts[pTri->GetVert( 2 )]; |
|
|
|
CDispCollTriCache *pCache = &m_aTrisCache[iTri]; |
|
|
|
Vector vecEdge; |
|
|
|
// Edge 1 |
|
VectorSubtract( *pVerts[1], *pVerts[0], vecEdge ); |
|
Cache_EdgeCrossAxisX( vecEdge, *pVerts[0], *pVerts[2], pTri, pCache->m_iCrossX[0] ); |
|
Cache_EdgeCrossAxisY( vecEdge, *pVerts[0], *pVerts[2], pTri, pCache->m_iCrossY[0] ); |
|
Cache_EdgeCrossAxisZ( vecEdge, *pVerts[0], *pVerts[2], pTri, pCache->m_iCrossZ[0] ); |
|
|
|
// Edge 2 |
|
VectorSubtract( *pVerts[2], *pVerts[1], vecEdge ); |
|
Cache_EdgeCrossAxisX( vecEdge, *pVerts[1], *pVerts[0], pTri, pCache->m_iCrossX[1] ); |
|
Cache_EdgeCrossAxisY( vecEdge, *pVerts[1], *pVerts[0], pTri, pCache->m_iCrossY[1] ); |
|
Cache_EdgeCrossAxisZ( vecEdge, *pVerts[1], *pVerts[0], pTri, pCache->m_iCrossZ[1] ); |
|
|
|
// Edge 3 |
|
VectorSubtract( *pVerts[0], *pVerts[2], vecEdge ); |
|
Cache_EdgeCrossAxisX( vecEdge, *pVerts[2], *pVerts[1], pTri, pCache->m_iCrossX[2] ); |
|
Cache_EdgeCrossAxisY( vecEdge, *pVerts[2], *pVerts[1], pTri, pCache->m_iCrossY[2] ); |
|
Cache_EdgeCrossAxisZ( vecEdge, *pVerts[2], *pVerts[1], pTri, pCache->m_iCrossZ[2] ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
//----------------------------------------------------------------------------- |
|
int CDispCollTree::AddPlane( const Vector &vecNormal ) |
|
{ |
|
UtlHashHandle_t handle; |
|
DispCollPlaneIndex_t planeIndex; |
|
bool bDidInsert; |
|
|
|
planeIndex.vecPlane = vecNormal; |
|
planeIndex.index = m_aEdgePlanes.Count(); |
|
|
|
handle = g_DispCollPlaneIndexHash.Insert( planeIndex, &bDidInsert ); |
|
|
|
if ( !bDidInsert ) |
|
{ |
|
DispCollPlaneIndex_t &existingEntry = g_DispCollPlaneIndexHash[handle]; |
|
if ( existingEntry.vecPlane == vecNormal ) |
|
{ |
|
return existingEntry.index; |
|
} |
|
else |
|
{ |
|
return ( existingEntry.index | 0x8000 ); |
|
} |
|
} |
|
|
|
return m_aEdgePlanes.AddToTail( vecNormal ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// NOTE: The plane distance get stored in the normal x position since it isn't |
|
// used. |
|
//----------------------------------------------------------------------------- |
|
bool CDispCollTree::Cache_EdgeCrossAxisX( const Vector &vecEdge, const Vector &vecOnEdge, |
|
const Vector &vecOffEdge, CDispCollTri *pTri, |
|
unsigned short &iPlane ) |
|
{ |
|
// Calculate the normal - edge x axisX = ( 0.0, edgeZ, -edgeY ) |
|
Vector vecNormal( 0.0f, vecEdge.z, -vecEdge.y ); |
|
VectorNormalize( vecNormal ); |
|
|
|
// Check for zero length normals. |
|
if( ( vecNormal.y == 0.0f ) || ( vecNormal.z == 0.0f ) ) |
|
{ |
|
iPlane = DISPCOLL_NORMAL_UNDEF; |
|
return false; |
|
} |
|
|
|
// if ( pTri->m_vecNormal.Dot( vecNormal ) ) |
|
// { |
|
// iPlane = DISPCOLL_NORMAL_UNDEF; |
|
// return false; |
|
// } |
|
|
|
// Finish the plane definition - get distance. |
|
float flDist = ( vecNormal.y * vecOnEdge.y ) + ( vecNormal.z * vecOnEdge.z ); |
|
|
|
// Special case the point off edge in plane |
|
float flOffDist = ( vecNormal.y * vecOffEdge.y ) + ( vecNormal.z * vecOffEdge.z ); |
|
if ( !( FloatMakePositive( flOffDist - flDist ) < DISPCOLL_DIST_EPSILON ) && ( flOffDist > flDist ) ) |
|
{ |
|
// Adjust plane facing - triangle should be behind the plane. |
|
vecNormal.x = -flDist; |
|
vecNormal.y = -vecNormal.y; |
|
vecNormal.z = -vecNormal.z; |
|
} |
|
else |
|
{ |
|
vecNormal.x = flDist; |
|
} |
|
|
|
// Add edge plane to edge plane list. |
|
iPlane = static_cast<unsigned short>( AddPlane( vecNormal ) ); |
|
|
|
// Created the cached edge. |
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// NOTE: The plane distance get stored in the normal y position since it isn't |
|
// used. |
|
//----------------------------------------------------------------------------- |
|
bool CDispCollTree::Cache_EdgeCrossAxisY( const Vector &vecEdge, const Vector &vecOnEdge, |
|
const Vector &vecOffEdge, CDispCollTri *pTri, |
|
unsigned short &iPlane ) |
|
{ |
|
// Calculate the normal - edge x axisY = ( -edgeZ, 0.0, edgeX ) |
|
Vector vecNormal( -vecEdge.z, 0.0f, vecEdge.x ); |
|
VectorNormalize( vecNormal ); |
|
|
|
// Check for zero length normals |
|
if( ( vecNormal.x == 0.0f ) || ( vecNormal.z == 0.0f ) ) |
|
{ |
|
iPlane = DISPCOLL_NORMAL_UNDEF; |
|
return false; |
|
} |
|
|
|
// if ( pTri->m_vecNormal.Dot( vecNormal ) ) |
|
// { |
|
// iPlane = DISPCOLL_NORMAL_UNDEF; |
|
// return false; |
|
// } |
|
|
|
// Finish the plane definition - get distance. |
|
float flDist = ( vecNormal.x * vecOnEdge.x ) + ( vecNormal.z * vecOnEdge.z ); |
|
|
|
// Special case the point off edge in plane |
|
float flOffDist = ( vecNormal.x * vecOffEdge.x ) + ( vecNormal.z * vecOffEdge.z ); |
|
if ( !( FloatMakePositive( flOffDist - flDist ) < DISPCOLL_DIST_EPSILON ) && ( flOffDist > flDist ) ) |
|
{ |
|
// Adjust plane facing if necessay - triangle should be behind the plane. |
|
vecNormal.x = -vecNormal.x; |
|
vecNormal.y = -flDist; |
|
vecNormal.z = -vecNormal.z; |
|
} |
|
else |
|
{ |
|
vecNormal.y = flDist; |
|
} |
|
|
|
// Add edge plane to edge plane list. |
|
iPlane = static_cast<unsigned short>( AddPlane( vecNormal ) ); |
|
|
|
// Created the cached edge. |
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CDispCollTree::Cache_EdgeCrossAxisZ( const Vector &vecEdge, const Vector &vecOnEdge, |
|
const Vector &vecOffEdge, CDispCollTri *pTri, |
|
unsigned short &iPlane ) |
|
{ |
|
// Calculate the normal - edge x axisY = ( edgeY, -edgeX, 0.0 ) |
|
Vector vecNormal( vecEdge.y, -vecEdge.x, 0.0f ); |
|
VectorNormalize( vecNormal ); |
|
|
|
// Check for zero length normals |
|
if( ( vecNormal.x == 0.0f ) || ( vecNormal.y == 0.0f ) ) |
|
{ |
|
iPlane = DISPCOLL_NORMAL_UNDEF; |
|
return false; |
|
} |
|
|
|
// if ( pTri->m_vecNormal.Dot( vecNormal ) ) |
|
// { |
|
// iPlane = DISPCOLL_NORMAL_UNDEF; |
|
// return false; |
|
// } |
|
|
|
// Finish the plane definition - get distance. |
|
float flDist = ( vecNormal.x * vecOnEdge.x ) + ( vecNormal.y * vecOnEdge.y ); |
|
|
|
// Special case the point off edge in plane |
|
float flOffDist = ( vecNormal.x * vecOffEdge.x ) + ( vecNormal.y * vecOffEdge.y ); |
|
if ( !( FloatMakePositive( flOffDist - flDist ) < DISPCOLL_DIST_EPSILON ) && ( flOffDist > flDist ) ) |
|
{ |
|
// Adjust plane facing if necessay - triangle should be behind the plane. |
|
vecNormal.x = -vecNormal.x; |
|
vecNormal.y = -vecNormal.y; |
|
vecNormal.z = -flDist; |
|
} |
|
else |
|
{ |
|
vecNormal.z = flDist; |
|
} |
|
|
|
// Add edge plane to edge plane list. |
|
iPlane = static_cast<unsigned short>( AddPlane( vecNormal ) ); |
|
|
|
// Created the cached edge. |
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
template <int AXIS> |
|
bool CDispCollTree::EdgeCrossAxis( const Ray_t &ray, unsigned short iPlane, CDispCollHelper *pHelper ) |
|
{ |
|
if ( iPlane == DISPCOLL_NORMAL_UNDEF ) |
|
return true; |
|
|
|
// Get the edge plane. |
|
Vector vecNormal; |
|
if ( ( iPlane & 0x8000 ) != 0 ) |
|
{ |
|
VectorCopy( m_aEdgePlanes[(iPlane&0x7fff)], vecNormal ); |
|
vecNormal.Negate(); |
|
} |
|
else |
|
{ |
|
VectorCopy( m_aEdgePlanes[iPlane], vecNormal ); |
|
} |
|
|
|
const int OTHER_AXIS1 = ( AXIS + 1 ) % 3; |
|
const int OTHER_AXIS2 = ( AXIS + 2 ) % 3; |
|
|
|
// Get the pland distance are "fix" the normal. |
|
float flDist = vecNormal[AXIS]; |
|
vecNormal[AXIS] = 0.0f; |
|
|
|
// Calculate the closest point on box to plane (get extents in that direction). |
|
Vector vecExtent; |
|
//vecExtent[AXIS] = 0.0f; |
|
vecExtent[OTHER_AXIS1] = ( vecNormal[OTHER_AXIS1] < 0.0f ) ? ray.m_Extents[OTHER_AXIS1] : -ray.m_Extents[OTHER_AXIS1]; |
|
vecExtent[OTHER_AXIS2] = ( vecNormal[OTHER_AXIS2] < 0.0f ) ? ray.m_Extents[OTHER_AXIS2] : -ray.m_Extents[OTHER_AXIS2]; |
|
|
|
// Expand the plane by the extents of the box to reduce the swept box/triangle |
|
// test to a ray/extruded triangle test (one of the triangles extruded planes |
|
// was just calculated above). |
|
Vector vecEnd; |
|
vecEnd[AXIS] = 0; |
|
vecEnd[OTHER_AXIS1] = ray.m_Start[OTHER_AXIS1] + ray.m_Delta[OTHER_AXIS1]; |
|
vecEnd[OTHER_AXIS2] = ray.m_Start[OTHER_AXIS2] + ray.m_Delta[OTHER_AXIS2]; |
|
|
|
float flExpandDist = flDist - ( ( vecNormal[OTHER_AXIS1] * vecExtent[OTHER_AXIS1] ) + ( vecNormal[OTHER_AXIS2] * vecExtent[OTHER_AXIS2] ) ); |
|
float flStart = ( vecNormal[OTHER_AXIS1] * ray.m_Start[OTHER_AXIS1] ) + ( vecNormal[OTHER_AXIS2] * ray.m_Start[OTHER_AXIS2] ) - flExpandDist; |
|
float flEnd = ( vecNormal[OTHER_AXIS1] * vecEnd[OTHER_AXIS1] ) + ( vecNormal[OTHER_AXIS2] * vecEnd[OTHER_AXIS2] ) - flExpandDist; |
|
|
|
return ResolveRayPlaneIntersect( flStart, flEnd, vecNormal, flDist, pHelper ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
inline bool CDispCollTree::EdgeCrossAxisX( const Ray_t &ray, unsigned short iPlane, CDispCollHelper *pHelper ) |
|
{ |
|
return EdgeCrossAxis<0>( ray, iPlane, pHelper ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
inline bool CDispCollTree::EdgeCrossAxisY( const Ray_t &ray, unsigned short iPlane, CDispCollHelper *pHelper ) |
|
{ |
|
return EdgeCrossAxis<1>( ray, iPlane, pHelper ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
inline bool CDispCollTree::EdgeCrossAxisZ( const Ray_t &ray, unsigned short iPlane, CDispCollHelper *pHelper ) |
|
{ |
|
return EdgeCrossAxis<2>( ray, iPlane, pHelper ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CDispCollTree::SweepAABBTriIntersect( const Ray_t &ray, const Vector &rayDir, int iTri, CDispCollTri *pTri, CBaseTrace *pTrace ) |
|
{ |
|
// Init test data. |
|
CDispCollHelper helper; |
|
helper.m_flEndFrac = 1.0f; |
|
helper.m_flStartFrac = DISPCOLL_INVALID_FRAC; |
|
|
|
// Make sure objects are traveling toward one another. |
|
float flDistAlongNormal = pTri->m_vecNormal.Dot( ray.m_Delta ); |
|
if( flDistAlongNormal > DISPCOLL_DIST_EPSILON ) |
|
return; |
|
|
|
// Test against the axis planes. |
|
if ( !AxisPlanesXYZ( ray, pTri, &helper ) ) |
|
{ |
|
return; |
|
} |
|
|
|
// |
|
// There are 9 edge tests - edges 1, 2, 3 cross with the box edges (symmetry) 1, 2, 3. However, the box |
|
// is axis-aligned resulting in axially directional edges -- thus each test is edges 1, 2, and 3 vs. |
|
// axial planes x, y, and z |
|
// |
|
// There are potentially 9 more tests with edges, the edge's edges and the direction of motion! |
|
// NOTE: I don't think these tests are necessary for a manifold surface. |
|
// |
|
|
|
CDispCollTriCache *pCache = &m_aTrisCache[iTri]; |
|
|
|
// Edges 1-3, interleaved - axis tests are 2d tests |
|
if ( !EdgeCrossAxisX( ray, pCache->m_iCrossX[0], &helper ) ) { return; } |
|
if ( !EdgeCrossAxisX( ray, pCache->m_iCrossX[1], &helper ) ) { return; } |
|
if ( !EdgeCrossAxisX( ray, pCache->m_iCrossX[2], &helper ) ) { return; } |
|
|
|
if ( !EdgeCrossAxisY( ray, pCache->m_iCrossY[0], &helper ) ) { return; } |
|
if ( !EdgeCrossAxisY( ray, pCache->m_iCrossY[1], &helper ) ) { return; } |
|
if ( !EdgeCrossAxisY( ray, pCache->m_iCrossY[2], &helper ) ) { return; } |
|
|
|
if ( !EdgeCrossAxisZ( ray, pCache->m_iCrossZ[0], &helper ) ) { return; } |
|
if ( !EdgeCrossAxisZ( ray, pCache->m_iCrossZ[1], &helper ) ) { return; } |
|
if ( !EdgeCrossAxisZ( ray, pCache->m_iCrossZ[2], &helper ) ) { return; } |
|
|
|
// Test against the triangle face plane. |
|
if ( !FacePlane( ray, rayDir, pTri, &helper ) ) |
|
return; |
|
|
|
if ( ( helper.m_flStartFrac < helper.m_flEndFrac ) || ( FloatMakePositive( helper.m_flStartFrac - helper.m_flEndFrac ) < 0.001f ) ) |
|
{ |
|
if ( ( helper.m_flStartFrac != DISPCOLL_INVALID_FRAC ) && ( helper.m_flStartFrac < pTrace->fraction ) ) |
|
{ |
|
// Clamp -- shouldn't really ever be here!??? |
|
if ( helper.m_flStartFrac < 0.0f ) |
|
{ |
|
helper.m_flStartFrac = 0.0f; |
|
} |
|
|
|
pTrace->fraction = helper.m_flStartFrac; |
|
VectorCopy( helper.m_vecImpactNormal, pTrace->plane.normal ); |
|
pTrace->plane.dist = helper.m_flImpactDist; |
|
pTrace->dispFlags = pTri->m_uiFlags; |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: constructor |
|
//----------------------------------------------------------------------------- |
|
CDispCollTree::CDispCollTree() |
|
{ |
|
m_nPower = 0; |
|
m_nFlags = 0; |
|
|
|
for ( int iPoint = 0; iPoint < 4; ++iPoint ) |
|
{ |
|
m_vecSurfPoints[iPoint].Init(); |
|
} |
|
m_nContents = -1; |
|
m_nSurfaceProps[0] = 0; |
|
m_nSurfaceProps[1] = 0; |
|
|
|
m_vecStabDir.Init(); |
|
m_mins.Init( FLT_MAX, FLT_MAX, FLT_MAX ); |
|
m_maxs.Init( -FLT_MAX, -FLT_MAX, -FLT_MAX ); |
|
|
|
m_iCounter = 0; |
|
|
|
m_aVerts.Purge(); |
|
m_aTris.Purge(); |
|
m_aEdgePlanes.Purge(); |
|
#ifdef ENGINE_DLL |
|
m_hCache = INVALID_MEMHANDLE; |
|
#endif |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: deconstructor |
|
//----------------------------------------------------------------------------- |
|
CDispCollTree::~CDispCollTree() |
|
{ |
|
#ifdef ENGINE_DLL |
|
if ( m_hCache != INVALID_MEMHANDLE ) |
|
g_DispCollTriCache.DestroyResource( m_hCache ); |
|
#endif |
|
m_aVerts.Purge(); |
|
m_aTris.Purge(); |
|
m_aEdgePlanes.Purge(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CDispCollTree::Create( CCoreDispInfo *pDisp ) |
|
{ |
|
// Create the AABB Tree. |
|
return AABBTree_Create( pDisp ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CDispCollTree::PointInBounds( const Vector &vecBoxCenter, const Vector &vecBoxMin, |
|
const Vector &vecBoxMax, bool bPoint ) |
|
{ |
|
// Point test inside bounds. |
|
if( bPoint ) |
|
{ |
|
return IsPointInBox( vecBoxCenter, m_mins, m_maxs ); |
|
} |
|
|
|
// Box test inside bounds |
|
Vector vecExtents; |
|
VectorSubtract( vecBoxMax, vecBoxMin, vecExtents ); |
|
vecExtents *= 0.5f; |
|
|
|
Vector vecExpandBounds[2]; |
|
vecExpandBounds[0] = m_mins - vecExtents; |
|
vecExpandBounds[1] = m_maxs + vecExtents; |
|
|
|
return IsPointInBox( vecBoxCenter, vecExpandBounds[0], vecExpandBounds[1] ); |
|
} |
|
|
|
void CDispCollTree::GetVirtualMeshList( virtualmeshlist_t *pList ) |
|
{ |
|
int i; |
|
int triangleCount = GetTriSize(); |
|
pList->indexCount = triangleCount * 3; |
|
pList->triangleCount = triangleCount; |
|
pList->vertexCount = m_aVerts.Count(); |
|
pList->pVerts = m_aVerts.Base(); |
|
pList->pHull = NULL; |
|
pList->surfacePropsIndex = GetSurfaceProps(0); |
|
int index = 0; |
|
for ( i = 0 ; i < triangleCount; i++ ) |
|
{ |
|
pList->indices[index+0] = m_aTris[i].GetVert(0); |
|
pList->indices[index+1] = m_aTris[i].GetVert(1); |
|
pList->indices[index+2] = m_aTris[i].GetVert(2); |
|
index += 3; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
//----------------------------------------------------------------------------- |
|
#ifdef ENGINE_DLL |
|
static int g_nTrees; |
|
#endif |
|
CDispCollTree *DispCollTrees_Alloc( int count ) |
|
{ |
|
CDispCollTree *pTrees = NULL; |
|
#ifdef ENGINE_DLL |
|
pTrees = (CDispCollTree *)Hunk_Alloc( count * sizeof(CDispCollTree), false ); |
|
g_nTrees = count; |
|
for ( int i = 0; i < g_nTrees; i++ ) |
|
{ |
|
Construct( pTrees + i ); |
|
} |
|
#else |
|
pTrees = new CDispCollTree[count]; |
|
#endif |
|
if( !pTrees ) |
|
return NULL; |
|
|
|
for ( int i = 0; i < count; i++ ) |
|
{ |
|
pTrees[i].m_iCounter = i; |
|
} |
|
return pTrees; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
//----------------------------------------------------------------------------- |
|
void DispCollTrees_Free( CDispCollTree *pTrees ) |
|
{ |
|
#ifdef ENGINE_DLL |
|
for ( int i = 0; i < g_nTrees; i++ ) |
|
{ |
|
Destruct( pTrees + i ); |
|
} |
|
g_nTrees = 0; |
|
#else |
|
if( pTrees ) |
|
{ |
|
delete [] pTrees; |
|
pTrees = NULL; |
|
} |
|
#endif |
|
}
|
|
|