DragonNest/Common/EngineUtil/EtOctree.h
2024-12-19 09:48:26 +08:00

603 lines
13 KiB
C++

#pragma once
#include "EtPrimitive.h"
#include "EtCollisionFunc.h"
#include "CriticalSection.h"
#include "PerfCheck.h"
template < class T >
class CEtOctreeNode: public TBoostMemoryPool< CEtOctreeNode<T> >
{
public:
CEtOctreeNode()
{
Init();
}
CEtOctreeNode( SSphere &Sphere )
{
Init();
m_NodeSphere = Sphere;
}
~CEtOctreeNode()
{
int i;
for( i = 0; i < 8; i++ )
{
SAFE_DELETE( m_pChildNode[ i ] );
}
}
protected:
SSphere m_NodeSphere;
CEtOctreeNode *m_pParent;
CEtOctreeNode *m_pChildNode[ 8 ];
UINT m_uiChildObjectCount;
DNVector(T) m_vecObjectList;
static float s_fMinRadius;
public:
void Init()
{
m_pParent = NULL;
m_uiChildObjectCount = 0;
memset( m_pChildNode, 0, sizeof( CEtOctreeNode * ) * 8 );
}
CEtOctreeNode *GetParent()
{
return m_pParent;
}
void SetParent( CEtOctreeNode* pParent )
{
m_pParent = pParent;
}
UINT GetChildObjectCount(){ return m_uiChildObjectCount; }
SSphere& GetNodeSphere(){ return m_NodeSphere; }
int GetObjectCount()
{
return ( int )m_vecObjectList.size();
}
T GetObject( int nIndex )
{
return m_vecObjectList[ nIndex ];
}
void SetRange( EtVector3 &CenterPos, float fRadius )
{
m_NodeSphere.Center = CenterPos;
m_NodeSphere.fRadius = fRadius;
}
void GetAllObject( DNVector(T) &vecPickObject )
{
if( !m_vecObjectList.empty() )
{
vecPickObject.insert( vecPickObject.end(), m_vecObjectList.begin(), m_vecObjectList.end() );
}
for( volatile int i = 0; i < 8; i++ )
{
if( m_pChildNode[ i ] && m_pChildNode[i]->GetChildObjectCount() )
{
m_pChildNode[ i ]->GetAllObject( vecPickObject );
}
}
}
CEtOctreeNode *Insert( T Object, SSphere &Sphere )
{
if( m_NodeSphere.fRadius <= s_fMinRadius )
{
++m_uiChildObjectCount;
CEtOctreeNode* pParent = GetParent();
while( pParent )
{
++pParent->m_uiChildObjectCount;
pParent = pParent->GetParent();
}
m_vecObjectList.push_back( Object );
return this;
}
if( Sphere.fRadius > m_NodeSphere.fRadius )
{
if( m_pParent )
{
return m_pParent->Insert( Object, Sphere );
}
else
{
ASSERT( 0 && "Object Is Too Big" );
return NULL;
}
return this;
}
volatile bool bInside[ 8 ];
volatile int i, nInsideCount, nChildIndex;
volatile float fChildPivot;
SSphere ChildSphere[ 8 ];
static EtVector3 CenterTable[ 8 ] =
{
EtVector3( 1.0f, 1.0f, 1.0f ),
EtVector3( 1.0f, 1.0f, -1.0f ),
EtVector3( 1.0f, -1.0f, 1.0f ),
EtVector3( 1.0f, -1.0f, -1.0f ),
EtVector3( -1.0f, 1.0f, 1.0f ),
EtVector3( -1.0f, 1.0f, -1.0f ),
EtVector3( -1.0f, -1.0f, 1.0f ),
EtVector3( -1.0f, -1.0f, -1.0f ),
};
fChildPivot = ( m_NodeSphere.fRadius / 1.732050f ) * 0.5f;
nInsideCount = 0;
for( i = 0; i < 8; i++ )
{
ChildSphere[ i ].Center = m_NodeSphere.Center + CenterTable[ i ] * fChildPivot;
ChildSphere[ i ].fRadius = m_NodeSphere.fRadius * 0.5f;
bInside[ i ] = false;
if( ChildSphere[ i ].IsInside( Sphere ) )
{
nChildIndex = i;
nInsideCount++;
bInside[ i ] = true;
}
}
if( nInsideCount == 0 )
{
++m_uiChildObjectCount;
CEtOctreeNode* pParent = GetParent();
while( pParent )
{
++pParent->m_uiChildObjectCount;
pParent = pParent->GetParent();
}
m_vecObjectList.push_back( Object );
return this;
}
else if( nInsideCount >= 2 )
{
float fMinDist, fDist;
fMinDist = FLT_MAX;
for( i = 0; i < 8; i++ )
{
if( bInside[ i ] )
{
fDist = EtVec3LengthSq( &( Sphere.Center - ChildSphere[ i ].Center ) );
if( fDist < fMinDist )
{
fMinDist = fDist;
nChildIndex = i;
}
}
}
}
if( m_pChildNode[ nChildIndex ] == NULL )
{
m_pChildNode[ nChildIndex ] = new CEtOctreeNode( ChildSphere[ nChildIndex ] );
m_pChildNode[ nChildIndex ]->SetParent( this );
}
return m_pChildNode[ nChildIndex ]->Insert( Object, Sphere );
}
struct ValidCheckFind {
T _val;
ValidCheckFind( T &Object ) { _val = Object; }
bool operator() ( T &Object ) { return ( _val && Object ) ? ( Object == _val ) : false; }
};
bool Remove( T Object )
{
DNVector(T)::iterator Iter;
Iter = std::find_if( m_vecObjectList.begin(), m_vecObjectList.end(), ValidCheckFind(Object) );
if( Iter != m_vecObjectList.end() )
{
--m_uiChildObjectCount;
CEtOctreeNode* pParent = GetParent();
while( pParent )
{
--pParent->m_uiChildObjectCount;
pParent = pParent->GetParent();
}
m_vecObjectList.erase( Iter );
return true;
}
volatile int i;
for( i = 0; i < 8; i++ )
{
if( m_pChildNode[ i ] == NULL )
{
continue;
}
if( m_pChildNode[ i ]->Remove( Object ) )
{
return true;
}
}
return false;
}
CEtOctreeNode *Update( T Object, SSphere &Sphere )
{
if( m_NodeSphere.IsInside( Sphere ) )
{
return this;
}
else
{
return NULL;
}
}
void Pick( EtVector3 &Position, EtVector3 &Direction, T &PickObject, float &fMinDist )
{
int i;
for( i = 0; i < ( int )m_vecObjectList.size(); i++ )
{
if( !m_vecObjectList[i] ) continue;
SAABox ObjectBox;
float fDist;
m_vecObjectList[ i ]->GetBoundingBox( ObjectBox );
if( TestLineToBox( Position, Direction, ObjectBox, fDist ) )
{
if( fDist < fMinDist )
{
PickObject = m_vecObjectList[ i ];
fMinDist = fDist;
}
}
}
for( i = 0; i < 8; i++ )
{
if( m_pChildNode[ i ] )
{
m_pChildNode[ i ]->Pick( Position, Direction, PickObject, fMinDist );
}
}
}
void PickBySize( EtVector3 &Position, EtVector3 &Direction, T &PickObject, float &fMinSize, float &fMinDist, SOBB &Obb )
{
int i;
EtVector3 vTemp;
for( i = 0; i < ( int )m_vecObjectList.size(); i++ )
{
if( !m_vecObjectList[i] ) continue;
SAABox AABox;
SOBB ObjectBox;
float fDist, fSize;
SSegment Segment;
Segment.vDirection = Direction * fMinDist;
Segment.vOrigin = Position;
m_vecObjectList[ i ]->GetBoundingBox( ObjectBox );
m_vecObjectList[ i ]->GetBoundingBox( AABox );
if( TestSegmentToOBB( Segment, ObjectBox ) )
{
// if( TestLineToBox( Position, Direction, AABox, fDist ) == false ) {
fDist = EtVec3Length( &( Position - ObjectBox.Center ) ) + max( max( ObjectBox.Extent[0], ObjectBox.Extent[1] ), ObjectBox.Extent[2] );
// }
fSize = ( ObjectBox.Extent[0] / 100.f ) * ( ObjectBox.Extent[1] / 100.f ) * ( ObjectBox.Extent[2] / 100.f );
if( fDist < fMinDist ) {
PickObject = m_vecObjectList[ i ];
fMinSize = fSize;
fMinDist = fDist;
}
/*
if( fSize < fMinSize && fDist < fMinDist )
{
if( fMinSize == FLT_MAX ) {
Obb = ObjectBox;
// TestOBBToOBB( Obb, ObjectBox )
}
PickObject = m_vecObjectList[ i ];
fMinSize = fSize;
fMinDist = fDist;
}
else if( fSize < fMinSize && fMinSize != FLT_MAX ) {
if( TestOBBToOBB( Obb, ObjectBox ) == true ) {
PickObject = m_vecObjectList[ i ];
fMinSize = fSize;
fMinDist = fDist;
}
}
*/
}
}
for( i = 0; i < 8; i++ )
{
if( m_pChildNode[ i ] )
{
m_pChildNode[ i ]->PickBySize( Position, Direction, PickObject, fMinSize, fMinDist, Obb );
}
}
}
void Pick( SAABox &Box, DNVector(T) &vecPickObject, bool bInside )
{
if( !TestBoxToSphere( Box, m_NodeSphere ) )
{
return;
}
if( Box.IsInside( m_NodeSphere ) )
{
GetAllObject( vecPickObject );
}
else
{
int i;
for( i = 0; i < ( int )m_vecObjectList.size(); i++ )
{
if( !m_vecObjectList[i] ) continue;
SAABox ObjectBox;
m_vecObjectList[ i ]->GetBoundingBox( ObjectBox );
if( bInside )
{
if( Box.IsInside( ObjectBox ) )
{
vecPickObject.push_back( m_vecObjectList[ i ] );
}
}
else
{
if( TestBoxToBox( ObjectBox, Box ) )
{
vecPickObject.push_back( m_vecObjectList[ i ] );
}
}
}
for( i = 0; i < 8; i++ )
{
if( m_pChildNode[ i ] )
{
m_pChildNode[ i ]->Pick( Box, vecPickObject, bInside );
}
}
}
}
void Pick( SCircle &Circle, DNVector(T) &vecPickObject, bool bInside)
{
SCircle NodeCircle;
NodeCircle.Center.x = m_NodeSphere.Center.x;
NodeCircle.Center.y = m_NodeSphere.Center.z;
NodeCircle.fRadius = m_NodeSphere.fRadius;
if( !TestCircleToCircle( Circle, NodeCircle ) )
{
return;
}
if( Circle.IsInside( NodeCircle ) )
{
GetAllObject( vecPickObject );
}
else
{
int i;
for( i = 0; i < ( int )m_vecObjectList.size(); i++ )
{
if( !m_vecObjectList[i] ) continue;
SAABox ObjectBox;
SAABox2D ObjectBox2D;
m_vecObjectList[ i ]->GetBoundingBox( ObjectBox );
ObjectBox2D.Reset();
ObjectBox2D.AddPoint( EtVector2( ObjectBox.Max.x, ObjectBox.Max.z ) );
ObjectBox2D.AddPoint( EtVector2( ObjectBox.Min.x, ObjectBox.Min.z ) );
if( bInside )
{
if( Circle.IsInside( ObjectBox2D ) )
{
vecPickObject.push_back( m_vecObjectList[ i ] );
}
}
else
{
if( TestCircleToBox2D( Circle, ObjectBox2D ) )
{
vecPickObject.push_back( m_vecObjectList[ i ] );
}
}
}
for( i = 0; i < 8; i++ )
{
if( m_pChildNode[ i ] )
{
m_pChildNode[ i ]->Pick( Circle, vecPickObject, bInside );
}
}
}
}
void Pick( SSphere &Sphere, DNVector(T) &vecPickObject, bool bInside, bool bActorSize )
{
if( !TestSphereToSphere( Sphere, m_NodeSphere ) )
{
return;
}
if( Sphere.IsInside( m_NodeSphere ) )
{
GetAllObject( vecPickObject );
}
else
{
volatile int i;
for( i = 0; i < ( int )m_vecObjectList.size(); i++ )
{
if( !m_vecObjectList[i] ) continue;
SSphere ObjectSphere;
m_vecObjectList[ i ]->GetBoundingSphere( ObjectSphere, bActorSize );
if( bInside )
{
if( Sphere.IsInside( ObjectSphere ) )
{
vecPickObject.push_back( m_vecObjectList[ i ] );
}
}
else
{
if( TestSphereToSphere( ObjectSphere, Sphere ) )
{
vecPickObject.push_back( m_vecObjectList[ i ] );
}
}
}
for( i = 0; i < 8; i++ )
{
if( m_pChildNode[ i ] && m_pChildNode[i]->GetChildObjectCount() > 0 )
{
m_pChildNode[ i ]->Pick( Sphere, vecPickObject, bInside, bActorSize );
}
}
}
}
};
template < class T >
class CEtOctree:public TBoostMemoryPool<CEtOctree<T>>
{
public:
CEtOctree(bool bLock = true)
{
m_pRootNode = NULL;
m_bInit = false;
m_bUseLock = bLock;
}
virtual ~CEtOctree(void)
{
SAFE_DELETE( m_pRootNode );
}
protected:
bool m_bInit;
CEtOctreeNode< T > *m_pRootNode;
CSyncLock m_Lock;
bool m_bUseLock;
public:
void UseLock( bool bUse ) { m_bUseLock = bUse; }
void Initialize( EtVector3 &CenterPos, float fWorldSize )
{
m_bInit = true;
float fRootRadius = fWorldSize * 1.414214f * 0.5f;
SAFE_DELETE( m_pRootNode );
m_pRootNode = new CEtOctreeNode< T >();
m_pRootNode->SetRange( CenterPos, fRootRadius );
}
CEtOctreeNode< T > *Insert( T Object, SSphere &Sphere )
{
ScopeLock<CSyncLock> Lock(m_Lock, m_bUseLock);
if( !m_bInit )
{
return NULL;
}
return m_pRootNode->Insert( Object, Sphere );
}
bool Remove( T Object, CEtOctreeNode< T > *pCurNode )
{
ScopeLock<CSyncLock> Lock(m_Lock, m_bUseLock);
if( !m_bInit )
{
return false;
}
if( pCurNode )
{
return pCurNode->Remove( Object );
}
else
{
return m_pRootNode->Remove( Object );
}
}
CEtOctreeNode< T > *Update( T Object, SSphere &Sphere, CEtOctreeNode< T > *pCurNode )
{
ScopeLock<CSyncLock> Lock(m_Lock, m_bUseLock);
if( !m_bInit )
{
return NULL;
}
if( !pCurNode )
{
return NULL;
}
CEtOctreeNode< T > *pUpdateNode;
pUpdateNode = pCurNode->Update( Object, Sphere );
if( pUpdateNode == NULL )
{
CEtOctreeNode< T > *pParentNode;
bool bRet = Remove( Object, pCurNode );
pParentNode = pCurNode->GetParent();
if( pParentNode )
{
pUpdateNode = pParentNode->Insert( Object, Sphere );
}
if( pUpdateNode == NULL )
{
pUpdateNode = m_pRootNode->Insert( Object, Sphere );
}
ASSERT( pUpdateNode );
}
return pUpdateNode;
}
void Pick( EtVector3 &Position, EtVector3 &Direction, T &PickObject )
{
ScopeLock<CSyncLock> Lock(m_Lock, m_bUseLock);
if( !m_bInit )
{
return;
}
float fMinDist;
fMinDist = FLT_MAX;
m_pRootNode->Pick( Position, Direction, PickObject, fMinDist );
}
void PickBySize( EtVector3 &Position, EtVector3 &Direction, T &PickObject )
{
ScopeLock<CSyncLock> Lock(m_Lock, m_bUseLock);
if( !m_bInit )
{
return;
}
float fMinSize, fMinDist;
SOBB ObjectBox;
fMinSize = FLT_MAX;
fMinDist = 1000000.f;
m_pRootNode->PickBySize( Position, Direction, PickObject, fMinSize, fMinDist, ObjectBox );
}
void Pick( SAABox &Box, DNVector(T) &vecPickObject, bool bInside = false )
{
ScopeLock<CSyncLock> Lock(m_Lock, m_bUseLock);
if( m_bInit )
m_pRootNode->Pick( Box, vecPickObject, bInside );
}
void Pick( SCircle &Circle, DNVector(T) &vecPickObject, bool bInside = false )
{
ScopeLock<CSyncLock> Lock(m_Lock, m_bUseLock);
if( m_bInit )
m_pRootNode->Pick( Circle, vecPickObject, bInside );
}
void Pick( SSphere &Sphere, DNVector(T) &vecPickObject, bool bInside = false, bool bActorSize = false )
{
ScopeLock<CSyncLock> Lock(m_Lock, m_bUseLock);
if( m_bInit )
m_pRootNode->Pick( Sphere, vecPickObject, bInside, bActorSize );
}
};