📄 opc_aabbtree.cpp
字号:
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*
* OPCODE - Optimized Collision Detection
* Copyright (C) 2001 Pierre Terdiman
* Homepage: http://www.codercorner.com/Opcode.htm
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains code for a versatile AABB tree.
* \file OPC_AABBTree.cpp
* \author Pierre Terdiman
* \date March, 20, 2001
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains a generic AABB tree node.
*
* \class AABBTreeNode
* \author Pierre Terdiman
* \version 1.3
* \date March, 20, 2001
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains a generic AABB tree.
* This is a vanilla AABB tree, without any particular optimization. It contains anonymous references to
* user-provided primitives, which can theoretically be anything - triangles, boxes, etc. Each primitive
* is surrounded by an AABB, regardless of the primitive's nature. When the primitive is a triangle, the
* resulting tree can be converted into an optimized tree. If the primitive is a box, the resulting tree
* can be used for culling - VFC or occlusion -, assuming you cull on a mesh-by-mesh basis (modern way).
*
* \class AABBTree
* \author Pierre Terdiman
* \version 1.3
* \date March, 20, 2001
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Precompiled Header
#include "Opcode/Stdafx.h"
using namespace Opcode;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Constructor.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
AABBTreeNode::AABBTreeNode() :
mPos (null),
#ifndef OPC_NO_NEG_VANILLA_TREE
mNeg (null),
#endif
mNbPrimitives (0),
mNodePrimitives (null)
{
#ifdef OPC_USE_TREE_COHERENCE
mBitmask = 0;
#endif
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Destructor.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
AABBTreeNode::~AABBTreeNode()
{
// Opcode 1.3:
const AABBTreeNode* Pos = GetPos();
const AABBTreeNode* Neg = GetNeg();
#ifndef OPC_NO_NEG_VANILLA_TREE
if(!(mPos&1)) DELETESINGLE(Pos);
if(!(mNeg&1)) DELETESINGLE(Neg);
#else
if(!(mPos&1)) DELETEARRAY(Pos);
#endif
mNodePrimitives = null; // This was just a shortcut to the global list => no release
mNbPrimitives = 0;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Splits the node along a given axis.
* The list of indices is reorganized according to the split values.
* \param axis [in] splitting axis index
* \param builder [in] the tree builder
* \return the number of primitives assigned to the first child
* \warning this method reorganizes the internal list of primitives
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
udword AABBTreeNode::Split(udword axis, AABBTreeBuilder* builder)
{
// Get node split value
float SplitValue = builder->GetSplittingValue(mNodePrimitives, mNbPrimitives, mBV, axis);
udword NbPos = 0;
// Loop through all node-related primitives. Their indices range from mNodePrimitives[0] to mNodePrimitives[mNbPrimitives-1].
// Those indices map the global list in the tree builder.
for(udword i=0;i<mNbPrimitives;i++)
{
// Get index in global list
udword Index = mNodePrimitives[i];
// Test against the splitting value. The primitive value is tested against the enclosing-box center.
// [We only need an approximate partition of the enclosing box here.]
float PrimitiveValue = builder->GetSplittingValue(Index, axis);
// Reorganize the list of indices in this order: positive - negative.
if(PrimitiveValue > SplitValue)
{
// Swap entries
udword Tmp = mNodePrimitives[i];
mNodePrimitives[i] = mNodePrimitives[NbPos];
mNodePrimitives[NbPos] = Tmp;
// Count primitives assigned to positive space
NbPos++;
}
}
return NbPos;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Subdivides the node.
*
* N
* / \
* / \
* N/2 N/2
* / \ / \
* N/4 N/4 N/4 N/4
* (etc)
*
* A well-balanced tree should have a O(log n) depth.
* A degenerate tree would have a O(n) depth.
* Note a perfectly-balanced tree is not well-suited to collision detection anyway.
*
* \param builder [in] the tree builder
* \return true if success
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool AABBTreeNode::Subdivide(AABBTreeBuilder* builder)
{
// Checkings
if(!builder) return false;
// Stop subdividing if we reach a leaf node. This is always performed here,
// else we could end in trouble if user overrides this.
if(mNbPrimitives==1) return true;
// Let the user validate the subdivision
if(!builder->ValidateSubdivision(mNodePrimitives, mNbPrimitives, mBV)) return true;
bool ValidSplit = true; // Optimism...
udword NbPos;
if(builder->mSettings.mRules & SPLIT_LARGEST_AXIS)
{
// Find the largest axis to split along
IceMaths::Point Extents; mBV.GetExtents(Extents); // Box extents
udword Axis = Extents.LargestAxis(); // Index of largest axis
// Split along the axis
NbPos = Split(Axis, builder);
// Check split validity
if(!NbPos || NbPos==mNbPrimitives) ValidSplit = false;
}
else if(builder->mSettings.mRules & SPLIT_SPLATTER_POINTS)
{
// Compute the means
IceMaths::Point Means(0.0f, 0.0f, 0.0f);
for(udword i=0;i<mNbPrimitives;i++)
{
udword Index = mNodePrimitives[i];
Means.x+=builder->GetSplittingValue(Index, 0);
Means.y+=builder->GetSplittingValue(Index, 1);
Means.z+=builder->GetSplittingValue(Index, 2);
}
Means/=float(mNbPrimitives);
// Compute variances
IceMaths::Point Vars(0.0f, 0.0f, 0.0f);
for(udword i=0;i<mNbPrimitives;i++)
{
udword Index = mNodePrimitives[i];
float Cx = builder->GetSplittingValue(Index, 0);
float Cy = builder->GetSplittingValue(Index, 1);
float Cz = builder->GetSplittingValue(Index, 2);
Vars.x += (Cx - Means.x)*(Cx - Means.x);
Vars.y += (Cy - Means.y)*(Cy - Means.y);
Vars.z += (Cz - Means.z)*(Cz - Means.z);
}
Vars/=float(mNbPrimitives-1);
// Choose axis with greatest variance
udword Axis = Vars.LargestAxis();
// Split along the axis
NbPos = Split(Axis, builder);
// Check split validity
if(!NbPos || NbPos==mNbPrimitives) ValidSplit = false;
}
else if(builder->mSettings.mRules & SPLIT_BALANCED)
{
// Test 3 axis, take the best
float Results[3];
NbPos = Split(0, builder); Results[0] = float(NbPos)/float(mNbPrimitives);
NbPos = Split(1, builder); Results[1] = float(NbPos)/float(mNbPrimitives);
NbPos = Split(2, builder); Results[2] = float(NbPos)/float(mNbPrimitives);
Results[0]-=0.5f; Results[0]*=Results[0];
Results[1]-=0.5f; Results[1]*=Results[1];
Results[2]-=0.5f; Results[2]*=Results[2];
udword Min=0;
if(Results[1]<Results[Min]) Min = 1;
if(Results[2]<Results[Min]) Min = 2;
// Split along the axis
NbPos = Split(Min, builder);
// Check split validity
if(!NbPos || NbPos==mNbPrimitives) ValidSplit = false;
}
else if(builder->mSettings.mRules & SPLIT_BEST_AXIS)
{
// Test largest, then middle, then smallest axis...
// Sort axis
IceMaths::Point Extents; mBV.GetExtents(Extents); // Box extents
udword SortedAxis[] = { 0, 1, 2 };
float* Keys = (float*)&Extents.x;
for(udword j=0;j<3;j++)
{
for(udword i=0;i<2;i++)
{
if(Keys[SortedAxis[i]]<Keys[SortedAxis[i+1]])
{
udword Tmp = SortedAxis[i];
SortedAxis[i] = SortedAxis[i+1];
SortedAxis[i+1] = Tmp;
}
}
}
// Find the largest axis to split along
udword CurAxis = 0;
ValidSplit = false;
while(!ValidSplit && CurAxis!=3)
{
NbPos = Split(SortedAxis[CurAxis], builder);
// Check the subdivision has been successful
if(!NbPos || NbPos==mNbPrimitives) CurAxis++;
else ValidSplit = true;
}
}
else if(builder->mSettings.mRules & SPLIT_FIFTY)
{
// Don't even bother splitting (mainly a performance test)
NbPos = mNbPrimitives>>1;
}
else return false; // Unknown splitting rules
// Check the subdivision has been successful
if(!ValidSplit)
{
// Here, all boxes lie in the same sub-space. Two strategies:
// - if the tree *must* be complete, make an arbitrary 50-50 split
// - else stop subdividing
// if(builder->mSettings.mRules&SPLIT_COMPLETE)
if(builder->mSettings.mLimit==1)
{
builder->IncreaseNbInvalidSplits();
NbPos = mNbPrimitives>>1;
}
else return true;
}
// Now create children and assign their pointers.
if(builder->mNodeBase)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -