etqw-sdk/source/idlib/containers/QuadTree.h

723 lines
19 KiB
C++

// Copyright (C) 2007 Id Software, Inc.
//
#ifndef __QUADTREE_H__
#define __QUADTREE_H__
/*
==============================================================================
sdQuadTree
Direct Access QuadTree Lookup template
Children are orientated in the following order:
+---+---+
| 2 | 3 |
+---+---+
| 0 | 1 |
+---+---+
(0,0)
Neighbors are orientated in the following order:
3
1+2
0
==============================================================================
*/
template< class type >
class sdQuadTree {
public:
typedef struct nodePosition_s {
int level;
int x;
int y;
} nodePosition_t;
typedef type dataType;
class sdQuadNode {
public:
sdQuadNode( void ) {
parent = NULL;
memset( children, 0, sizeof( children ) );
memset( neighbors, 0, sizeof( neighbors ) );
data = NULL;
bounds.Clear();
}
explicit sdQuadNode( const idBounds &bounds ) {
parent = NULL;
memset( children, 0, sizeof( children ) );
memset( neighbors, 0, sizeof( neighbors ) );
data = NULL;
this->bounds = bounds;
}
explicit sdQuadNode( type *data, const idBounds &bounds ) {
assert( data != NULL );
parent = NULL;
memset( children, 0, sizeof( children ) );
memset( neighbors, 0, sizeof( neighbors ) );
this->data = data;
this->bounds = bounds;
}
virtual ~sdQuadNode( void ) {
assert( data == NULL );
}
void SetData( type *data ) {
this->data = data;
}
type * GetData( void ) const {
return data;
}
void SetParent( sdQuadNode &parent ) {
this->parent = &parent;
}
void SetChild( sdQuadNode &child, const int index ) {
assert( index >= 0 && index < 4 );
children[ index ] = &child;
}
void SetNeighbor( sdQuadNode &neighbor, const int index ) {
assert( index >= 0 && index < 4 );
neighbors[ index ] = &neighbor;
}
void SetBounds( const idBounds &bounds ) {
this->bounds = bounds;
}
sdQuadNode * GetParent( void ) const {
return parent;
}
sdQuadNode * GetChild( const int index ) const {
assert( index >= 0 && index < 4 );
return children[ index ];
}
sdQuadNode * GetNeighbor( const int index ) const {
assert( index >= 0 && index < 4 );
return neighbors[ index ];
}
idBounds & GetBounds( void ) {
return bounds;
}
void SetNodePosition( const int nodeLevel, const int nodeX, const int nodeY ) {
nodePosition.level = nodeLevel;
nodePosition.x = nodeX;
nodePosition.y = nodeY;
}
const nodePosition_t & GetNodePosition( void ) const {
return nodePosition;
}
bool HasChildren( void ) {
return( children[0] || children[1] || children[2] || children[3] );
}
void ClearChildren( void ) {
memset( children, 0, sizeof( children ) );
}
private:
sdQuadNode * parent;
sdQuadNode * children[4];
sdQuadNode * neighbors[4];
type * data;
idBounds bounds;
// keep track of the position in the tree
nodePosition_t nodePosition;
};
explicit sdQuadTree( const idBounds &bounds, const int depth = 6 );
virtual ~sdQuadTree( void );
void BuildQuadTree( void );
void BuildQuadTree( sdQuadNode &node );
const int GetDepth( void ) const { return depth; }
const int GetUsedDepth( void ) const;
const sdQuadNode * GetHeadNode( void ) const { return headNode; }
sdQuadNode * GetHeadNode( void ) { return headNode; }
sdQuadNode * FindNode( const idVec3 &point );
sdQuadNode * GetNode( const idBounds &bounds );
sdQuadNode * GetNode( const nodePosition_t &nodePosition );
sdQuadNode ** GetNodes( const int nodeLevel, int &numNodes ) {
assert( nodeLevel >=0 && nodeLevel < depth );
numNodes = idMath::Pow( 2, nodeLevel * 2 );
return nodes[ nodeLevel ];
}
const int GetNumLeafNodes( void ) const;
void CreateChildren( sdQuadNode &parent );
void FreeChildren( sdQuadNode &parent ) {
int i;
for ( i = 0; i < 4; i++ ) {
if ( parent.GetChild(i) ) {
FreeNode( *parent.GetChild(i) );
}
}
parent.ClearChildren();
}
private:
void GetUsedDepth_r( sdQuadNode &node, const int currentDepth, int *maxReachedDepth ) const;
void GetNumLeafNodes_r( sdQuadNode &node, int *numLeafNodes ) const;
sdQuadNode * AllocNode( sdQuadNode **node, int nodeLevel, int x, int y );
void FindChildren_r( sdQuadNode &parent, const int nodeLevel );
void FindChildren_r( sdQuadNode &parent, const int nodeLevel, const int parentX, const int parentY );
void FindNeighbors_r( const int nodeLevel );
void FreeNode( sdQuadNode &node ) {
nodes[ node.GetNodePosition().level ][ ( node.GetNodePosition().y << ( node.GetNodePosition().level ) ) + node.GetNodePosition().x ] = NULL;
delete &node;
}
private:
sdQuadNode * headNode;
sdQuadNode *** nodes;
int depth;
idBounds bounds;
idVec2 nodeScale;
};
/*
================
sdQuadTree<type>::sdQuadTree( const int depth )
================
*/
template< class type >
ID_INLINE sdQuadTree<type>::sdQuadTree( const idBounds &bounds, const int depth ) {
assert( depth > 0 );
this->depth = depth;
this->bounds = bounds;
// expand by 1 unit so everything fits completely in it
this->bounds.ExpandSelf( 1.f );
nodeScale.x = idMath::Pow( 2, depth - 1 ) / ( this->bounds[ 1 ].x - this->bounds[ 0 ].x );
nodeScale.y = idMath::Pow( 2, depth - 1 ) / ( this->bounds[ 1 ].y - this->bounds[ 0 ].y );
nodes = new sdQuadNode** [ depth ];
for ( int i = 0; i < depth; i++ ) {
int nCells = idMath::Pow( 2, i * 2 );
nodes[ i ] = new sdQuadNode* [ nCells ];
memset( nodes[ i ], 0, nCells * sizeof( sdQuadNode* ) );
}
// create head node
headNode = new sdQuadNode;
headNode->SetBounds( bounds );
// put in node array
nodes[ 0 ][ 0 ] = headNode;
}
/*
================
sdQuadTree<type>::~sdQuadTree
================
*/
template< class type >
ID_INLINE sdQuadTree<type>::~sdQuadTree( void ) {
if ( nodes ) {
for ( int i = 0; i < depth; i++ ) {
int nCells = static_cast< int >( idMath::Pow( 2.f, i * 2.f ) );
for ( int j = 0; j < nCells; j++ ) {
if ( nodes[ i ][ j ] ) {
delete nodes[ i ][ j ];
}
}
delete [] nodes[ i ];
}
delete [] nodes;
}
}
/*
================
sdQuadTree<type>::GetUsedDepth_r
================
*/
template< class type >
void sdQuadTree<type>::GetUsedDepth_r( sdQuadNode &node, const int currentDepth, int *maxReachedDepth ) const {
int i;
sdQuadNode* child;
if ( currentDepth > *maxReachedDepth ) {
*maxReachedDepth = currentDepth;
}
for ( i = 0; i < 4; i++ ) {
child = node.GetChild( i );
if ( child ) {
if ( currentDepth + 1 < depth ) {
GetUsedDepth_r( *child, currentDepth + 1, maxReachedDepth );
}
}
}
}
/*
================
sdQuadTree<type>::GetUsedDepth
================
*/
template< class type >
ID_INLINE const int sdQuadTree<type>::GetUsedDepth( void ) const {
int maxReachedDepth = 0;
GetUsedDepth_r( *headNode, 1, &maxReachedDepth );
return maxReachedDepth + 1;
}
/*
================
sdQuadTree<type>::BuildQuadTree
================
*/
template< class type >
ID_INLINE void sdQuadTree<type>::BuildQuadTree( void ) {
#if 1
FindChildren_r( *headNode, 1, 0, 0 );
#else
FindChildren_r( *headNode, 1 );
#endif
FindNeighbors_r( 1 );
}
/*
================
sdQuadTree<type>::BuildQuadTree
================
*/
template< class type >
ID_INLINE void sdQuadTree<type>::BuildQuadTree( typename sdQuadTree<type>::sdQuadNode &node ) {
// TODO
}
/*
================
sdQuadTree<type>::FindNode
================
*/
template< class type >
ID_INLINE typename sdQuadTree<type>::sdQuadNode * sdQuadTree<type>::FindNode( const idVec3 &point ) {
int nodeDepth, x, y;
sdQuadNode *node;
if ( !bounds.ContainsPoint( point ) ) {
return NULL;
}
x = (int)( ( point.x - bounds[ 0 ].x ) * nodeScale.x );
y = (int)( ( point.y - bounds[ 0 ].y ) * nodeScale.y );
for ( nodeDepth = depth - 1; nodeDepth >= 0; nodeDepth--, x >>= 1, y >>= 1 ) {
node = nodes[ nodeDepth ][ ( y << nodeDepth ) + x ];
if ( node ) {
return node;
}
}
// should never happen
return NULL;
}
/*
================
sdQuadTree<type>::GetNode( const idBounds & )
================
*/
template< class type >
ID_INLINE typename sdQuadTree<type>::sdQuadNode * sdQuadTree<type>::GetNode( const idBounds &bounds ) {
int x = (int)( ( bounds[ 0 ].x - this->bounds[ 0 ].x ) * nodeScale.x );
int y = (int)( ( bounds[ 0 ].y - this->bounds[ 0 ].y ) * nodeScale.y );
int xR = x ^ (int)( ( bounds[ 1 ].x - this->bounds[ 0 ].x ) * nodeScale.x );
int yR = y ^ (int)( ( bounds[ 1 ].y - this->bounds[ 0 ].y ) * nodeScale.y );
int nodeDepth = depth;
// OPTIMIZE: for x86, optimise using BSR ?
int shifted = 0;
while ( xR + yR != 0 ) {
xR >>= 1;
yR >>= 1;
nodeDepth--;
shifted++;
}
x >>= shifted;
y >>= shifted;
sdQuadNode** node = &nodes[ nodeDepth - 1 ][ ( y << ( nodeDepth - 1 ) ) + x ];
if ( *node ) {
return *node;
} else {
return AllocNode( node, nodeDepth - 1, x, y );
}
}
/*
================
sdQuadTree<type>::GetNode( const nodePosition_t &nodePosition )
================
*/
template< class type >
ID_INLINE typename sdQuadTree<type>::sdQuadNode * sdQuadTree<type>::GetNode( const nodePosition_t &nodePosition ) {
sdQuadNode** node = &nodes[ nodePosition.level ][ ( nodePosition.y << ( nodePosition.level ) ) + nodePosition.x ];
if ( *node ) {
return *node;
} else {
return AllocNode( node, nodePosition.level, nodePosition.x, nodePosition.y );
}
}
/*
================
sdQuadTree<type>::GetNumLeafNodes_r
================
*/
template< class type >
void sdQuadTree<type>::GetNumLeafNodes_r( sdQuadNode &node, int *numLeafNodes ) const {
int i;
sdQuadNode *child;
if ( !node.HasChildren() ) {
(*numLeafNodes)++;
return;
}
for ( i = 0; i < 4; i++ ) {
child = node.GetChild(i);
if ( child ) {
GetNumLeafNodes_r( *child, numLeafNodes );
}
}
}
/*
================
sdQuadTree<type>::GetNumLeafNodes
================
*/
template< class type >
const int sdQuadTree<type>::GetNumLeafNodes( void ) const {
int numLeafNodes = 0;
GetNumLeafNodes_r( *headNode, &numLeafNodes );
return numLeafNodes;
}
/*
================
sdQuadTree<type>::AllocNode
================
*/
template< class type >
ID_INLINE typename sdQuadTree<type>::sdQuadNode * sdQuadTree<type>::AllocNode( sdQuadNode **node, int nodeLevel, int x, int y ) {
int levelDimensions = idMath::Pow( 2, nodeLevel );
idVec2 cellSize( ( headNode->GetBounds()[ 1 ].x - headNode->GetBounds()[ 0 ].x ) / levelDimensions,
( headNode->GetBounds()[ 1 ].y - headNode->GetBounds()[ 0 ].y ) / levelDimensions );
idBounds nodeBounds;
idVec2 nodeMins, pCellsize;
int pX, pY, pNodeLevel;
// create the new node
nodeBounds.Clear();
nodeMins.Set( headNode->GetBounds()[ 0 ].x + x * cellSize.x, headNode->GetBounds()[ 0 ].y + y * cellSize.y );
nodeBounds.AddPoint( idVec3( nodeMins.x, nodeMins.y, 0.f ) );
nodeBounds.AddPoint( idVec3( nodeMins.x + cellSize.x, nodeMins.y + cellSize.y, 0.f ) );
*node = new sdQuadNode( nodeBounds );
(*node)->SetNodePosition( nodeLevel, x, y );
// find (and create) all its parents
sdQuadNode** parent;
sdQuadNode** child = node;
pX = x;
pY = y;
pNodeLevel = nodeLevel;
do {
pX >>= 1;
pY >>= 1;
pNodeLevel--;
parent = &nodes[ pNodeLevel ][ ( pY << ( pNodeLevel ) ) + pX ];
if ( !(*parent) ) {
levelDimensions = idMath::Pow( 2, pNodeLevel );
pCellsize.Set( ( headNode->GetBounds()[ 1 ].x - headNode->GetBounds()[ 0 ].x ) / levelDimensions,
( headNode->GetBounds()[ 1 ].y - headNode->GetBounds()[ 0 ].y ) / levelDimensions );
// create the new node
nodeBounds.Clear();
nodeMins.Set( headNode->GetBounds()[ 0 ].x + pX * pCellsize.x, headNode->GetBounds()[ 0 ].y + pY * pCellsize.y );
nodeBounds.AddPoint( idVec3( nodeMins.x, nodeMins.y, 0.f ) );
nodeBounds.AddPoint( idVec3( nodeMins.x + pCellsize.x, nodeMins.y + pCellsize.y, 0.f ) );
*parent = new sdQuadNode( nodeBounds );
(*parent)->SetNodePosition( pNodeLevel, pX, pY );
}
(*child)->SetParent( *(*parent) );
child = parent;
} while( *parent != headNode && !(*child)->GetParent() );
// create its siblings
pX = x & ~1;
pY = y & ~1;
for ( x = pX; x < pX + 2; x++ ) {
for ( y = pY; y < pY + 2; y++ ) {
sdQuadNode** sibling = &nodes[ nodeLevel ][ ( y << nodeLevel ) + x ];
if ( sibling == node ) {
continue;
}
// create the new node
nodeBounds.Clear();
nodeMins.Set( headNode->GetBounds()[ 0 ].x + x * cellSize.x, headNode->GetBounds()[ 0 ].y + y * cellSize.y );
nodeBounds.AddPoint( idVec3( nodeMins.x, nodeMins.y, 0.f ) );
nodeBounds.AddPoint( idVec3( nodeMins.x + cellSize.x, nodeMins.y + cellSize.y, 0.f ) );
*sibling = new sdQuadNode( nodeBounds );
(*sibling)->SetParent( *((*node)->GetParent()) );
(*sibling)->SetNodePosition( nodeLevel, x, y );
}
}
return *node;
}
/*
================
sdQuadTree<type>::FindChildren_r
================
*/
template< class type >
void sdQuadTree<type>::FindChildren_r( sdQuadNode &parent, const int nodeLevel ) {
int x, y;
int levelDimensions = idMath::Pow( 2, nodeLevel );
sdQuadNode* child;
// find all nodes with this node as a parent
for ( x = 0; x < levelDimensions; x++ ) {
for ( y = 0; y < levelDimensions; y++ ) {
child = nodes[ nodeLevel ][ ( y << nodeLevel ) + x ];
if ( child && child->GetParent() == &parent ) {
parent.SetChild( *child, (y % 2) * 2 + (x % 2) );
if ( nodeLevel + 1 < depth ) {
FindChildren_r( *child, nodeLevel + 1 );
}
}
}
}
}
/*
================
sdQuadTree<type>::FindChildren_r
================
*/
template< class type >
void sdQuadTree<type>::FindChildren_r( sdQuadNode &parent, const int nodeLevel, const int parentX, const int parentY ) {
int x, y;
sdQuadNode* child;
// find all nodes with this node as a parent
for ( x = parentX; x < parentX + 2; x++ ) {
for ( y = parentY; y < parentY + 2; y++ ) {
child = nodes[ nodeLevel ][ ( y << nodeLevel ) + x ];
if ( child ) {
parent.SetChild( *child, ( ( y - parentY ) << 1 ) + ( x - parentX ) );
if ( nodeLevel + 1 < depth ) {
// transform parent coordinates to lower level coordinates
FindChildren_r( *child, nodeLevel + 1, x << 1, y << 1 );
}
}
}
}
}
/*
================
sdQuadTree<type>::FindNeighbors_r
================
*/
template< class type >
void sdQuadTree<type>::FindNeighbors_r( const int nodeLevel ) {
int x, y;
int nbX, nbY;
int pX, pY, pNodeLevel;
int levelDimensions = idMath::Pow( 2, nodeLevel );
sdQuadNode *node, *neighbor;
for ( x = 0; x < levelDimensions; x++ ) {
for ( y = 0; y < levelDimensions; y++ ) {
node = nodes[ nodeLevel ][ ( y << nodeLevel ) + x ];
if ( !node ) {
continue;
}
// bottom neighbor (0)
if ( y > 0 ) {
nbX = x;
nbY = y - 1;
neighbor = nodes[ nodeLevel ][ ( nbY << nodeLevel ) + nbX ];
// first see if we have a neighbor on this level
if ( neighbor ) {
node->SetNeighbor( *neighbor, 0 );
} else if ( !(y & 1) ) {
// try higher levels ( till > 0 as the headnode doesn't have any neighbors )
for ( pNodeLevel = nodeLevel - 1, pX = nbX >> 1, pY = nbY >> 1; pNodeLevel > 0; pNodeLevel--, pX >>= 1, pY >>= 1 ) {
neighbor = nodes[ pNodeLevel ][ ( pY << ( pNodeLevel ) ) + pX ];
if ( neighbor ) {
node->SetNeighbor( *neighbor, 0 );
break;
}
}
}
}
// left neighbor (1)
if ( x > 0 ) {
nbX = x - 1;
nbY = y;
neighbor = nodes[ nodeLevel ][ ( nbY << nodeLevel ) + nbX ];
// first see if we have a neighbor on this level
if ( neighbor ) {
node->SetNeighbor( *neighbor, 1 );
} else if ( !(x & 1) ) {
// try higher levels ( till > 0 as the headnode doesn't have any neighbors )
for ( pNodeLevel = nodeLevel - 1, pX = nbX >> 1, pY = nbY >> 1; pNodeLevel > 0; pNodeLevel--, pX >>= 1, pY >>= 1 ) {
neighbor = nodes[ pNodeLevel ][ ( pY << ( pNodeLevel ) ) + pX ];
if ( neighbor ) {
node->SetNeighbor( *neighbor, 1 );
break;
}
}
}
}
// right neighbor (2)
if ( x < levelDimensions - 1 ) {
nbX = x + 1;
nbY = y;
neighbor = nodes[ nodeLevel ][ ( nbY << nodeLevel ) + nbX ];
// first see if we have a neighbor on this level
if ( neighbor ) {
node->SetNeighbor( *neighbor, 2 );
} else if ( x & 1 ) {
// try higher levels ( till > 0 as the headnode doesn't have any neighbors )
for ( pNodeLevel = nodeLevel - 1, pX = nbX >> 1, pY = nbY >> 1; pNodeLevel > 0; pNodeLevel--, pX >>= 1, pY >>= 1 ) {
neighbor = nodes[ pNodeLevel ][ ( pY << ( pNodeLevel ) ) + pX ];
if ( neighbor ) {
node->SetNeighbor( *neighbor, 2 );
break;
}
}
}
}
// top neighbor (3)
if ( y < levelDimensions - 1 ) {
nbX = x;
nbY = y + 1;
neighbor = nodes[ nodeLevel ][ ( nbY << nodeLevel ) + nbX ];
// first see if we have a neighbor on this level
if ( neighbor ) {
node->SetNeighbor( *neighbor, 3 );
} else if ( y & 1 ) {
// try higher levels ( till > 0 as the headnode doesn't have any neighbors )
for ( pNodeLevel = nodeLevel - 1, pX = nbX >> 1, pY = nbY >> 1; pNodeLevel > 0; pNodeLevel--, pX >>= 1, pY >>= 1 ) {
neighbor = nodes[ pNodeLevel ][ ( pY << ( pNodeLevel ) ) + pX ];
if ( neighbor ) {
node->SetNeighbor( *neighbor, 3 );
break;
}
}
}
}
}
}
if ( nodeLevel + 1 < depth ) {
FindNeighbors_r( nodeLevel + 1 );
}
}
/*
================
sdQuadTree<type>::FindNeighbors_r
================
*/
template< class type >
void sdQuadTree<type>::CreateChildren( sdQuadNode &parent ) {
int x, y, parentX, parentY;
sdQuadNode** child;
nodePosition_t parentNodePosition = parent.GetNodePosition();
if ( parentNodePosition.level + 1 >= depth ) {
return;
}
parentX = parentNodePosition.x << 1;
parentY = parentNodePosition.y << 1;
// create all the nodes children
for ( x = parentX; x < parentX + 2; x++ ) {
for ( y = parentY; y < parentY + 2; y++ ) {
child = &nodes[ parentNodePosition.level + 1 ][ ( y << (parentNodePosition.level + 1) ) + x ];
if ( !(*child) ) {
AllocNode( child, parentNodePosition.level + 1 , x, y );
}
parent.SetChild( **child, ( ( y - parentY ) << 1 ) + ( x - parentX ) );
}
}
}
#endif /* !__QUADTREE_H__ */