783 lines
32 KiB
C++
783 lines
32 KiB
C++
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
/*
|
|
* OPCODE - Optimized Collision Detection
|
|
* Copyright (C) 2001 Pierre Terdiman
|
|
* Homepage: http://www.codercorner.com/Opcode.htm
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
/**
|
|
* Contains code for optimized trees. Implements 4 trees:
|
|
* - normal
|
|
* - no leaf
|
|
* - quantized
|
|
* - no leaf / quantized
|
|
*
|
|
* \file OPC_OptimizedTree.cpp
|
|
* \author Pierre Terdiman
|
|
* \date March, 20, 2001
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
/**
|
|
* A standard AABB tree.
|
|
*
|
|
* \class AABBCollisionTree
|
|
* \author Pierre Terdiman
|
|
* \version 1.3
|
|
* \date March, 20, 2001
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
/**
|
|
* A no-leaf AABB tree.
|
|
*
|
|
* \class AABBNoLeafTree
|
|
* \author Pierre Terdiman
|
|
* \version 1.3
|
|
* \date March, 20, 2001
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
/**
|
|
* A quantized AABB tree.
|
|
*
|
|
* \class AABBQuantizedTree
|
|
* \author Pierre Terdiman
|
|
* \version 1.3
|
|
* \date March, 20, 2001
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
/**
|
|
* A quantized no-leaf AABB tree.
|
|
*
|
|
* \class AABBQuantizedNoLeafTree
|
|
* \author Pierre Terdiman
|
|
* \version 1.3
|
|
* \date March, 20, 2001
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Precompiled Header
|
|
#include "Stdafx.h"
|
|
|
|
using namespace Opcode;
|
|
|
|
//! Compilation flag:
|
|
//! - true to fix quantized boxes (i.e. make sure they enclose the original ones)
|
|
//! - false to see the effects of quantization errors (faster, but wrong results in some cases)
|
|
static bool gFixQuantized = true;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
/**
|
|
* Builds an implicit tree from a standard one. An implicit tree is a complete tree (2*N-1 nodes) whose negative
|
|
* box pointers and primitive pointers have been made implicit, hence packing 3 pointers in one.
|
|
*
|
|
* Layout for implicit trees:
|
|
* Node:
|
|
* - box
|
|
* - data (32-bits value)
|
|
*
|
|
* if data's LSB = 1 => remaining bits are a primitive pointer
|
|
* else remaining bits are a P-node pointer, and N = P + 1
|
|
*
|
|
* \relates AABBCollisionNode
|
|
* \fn _BuildCollisionTree(AABBCollisionNode* linear, const udword box_id, udword& current_id, const AABBTreeNode* current_node)
|
|
* \param linear [in] base address of destination nodes
|
|
* \param box_id [in] index of destination node
|
|
* \param current_id [in] current running index
|
|
* \param current_node [in] current node from input tree
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
static void _BuildCollisionTree(AABBCollisionNode* linear, const udword box_id, udword& current_id, const AABBTreeNode* current_node)
|
|
{
|
|
// Current node from input tree is "current_node". Must be flattened into "linear[boxid]".
|
|
|
|
// Store the AABB
|
|
current_node->GetAABB()->GetCenter(linear[box_id].mAABB.mCenter);
|
|
current_node->GetAABB()->GetExtents(linear[box_id].mAABB.mExtents);
|
|
// Store remaining info
|
|
if(current_node->IsLeaf())
|
|
{
|
|
// The input tree must be complete => i.e. one primitive/leaf
|
|
ASSERT(current_node->GetNbPrimitives()==1);
|
|
// Get the primitive index from the input tree
|
|
udword PrimitiveIndex = current_node->GetPrimitives()[0];
|
|
// Setup box data as the primitive index, marked as leaf
|
|
linear[box_id].mData = (PrimitiveIndex<<1)|1;
|
|
}
|
|
else
|
|
{
|
|
// To make the negative one implicit, we must store P and N in successive order
|
|
udword PosID = current_id++; // Get a new id for positive child
|
|
udword NegID = current_id++; // Get a new id for negative child
|
|
// Setup box data as the forthcoming new P pointer
|
|
linear[box_id].mData = (size_t)&linear[PosID];
|
|
// Make sure it's not marked as leaf
|
|
ASSERT(!(linear[box_id].mData&1));
|
|
// Recurse with new IDs
|
|
_BuildCollisionTree(linear, PosID, current_id, current_node->GetPos());
|
|
_BuildCollisionTree(linear, NegID, current_id, current_node->GetNeg());
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
/**
|
|
* Builds a "no-leaf" tree from a standard one. This is a tree whose leaf nodes have been removed.
|
|
*
|
|
* Layout for no-leaf trees:
|
|
*
|
|
* Node:
|
|
* - box
|
|
* - P pointer => a node (LSB=0) or a primitive (LSB=1)
|
|
* - N pointer => a node (LSB=0) or a primitive (LSB=1)
|
|
*
|
|
* \relates AABBNoLeafNode
|
|
* \fn _BuildNoLeafTree(AABBNoLeafNode* linear, const udword box_id, udword& current_id, const AABBTreeNode* current_node)
|
|
* \param linear [in] base address of destination nodes
|
|
* \param box_id [in] index of destination node
|
|
* \param current_id [in] current running index
|
|
* \param current_node [in] current node from input tree
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
static void _BuildNoLeafTree(AABBNoLeafNode* linear, const udword box_id, udword& current_id, const AABBTreeNode* current_node)
|
|
{
|
|
const AABBTreeNode* P = current_node->GetPos();
|
|
const AABBTreeNode* N = current_node->GetNeg();
|
|
// Leaf nodes here?!
|
|
ASSERT(P);
|
|
ASSERT(N);
|
|
// Internal node => keep the box
|
|
current_node->GetAABB()->GetCenter(linear[box_id].mAABB.mCenter);
|
|
current_node->GetAABB()->GetExtents(linear[box_id].mAABB.mExtents);
|
|
|
|
if(P->IsLeaf())
|
|
{
|
|
// The input tree must be complete => i.e. one primitive/leaf
|
|
ASSERT(P->GetNbPrimitives()==1);
|
|
// Get the primitive index from the input tree
|
|
udword PrimitiveIndex = P->GetPrimitives()[0];
|
|
// Setup prev box data as the primitive index, marked as leaf
|
|
linear[box_id].mPosData = (PrimitiveIndex<<1)|1;
|
|
}
|
|
else
|
|
{
|
|
// Get a new id for positive child
|
|
udword PosID = current_id++;
|
|
// Setup box data
|
|
linear[box_id].mPosData = (size_t)&linear[PosID];
|
|
// Make sure it's not marked as leaf
|
|
ASSERT(!(linear[box_id].mPosData&1));
|
|
// Recurse
|
|
_BuildNoLeafTree(linear, PosID, current_id, P);
|
|
}
|
|
|
|
if(N->IsLeaf())
|
|
{
|
|
// The input tree must be complete => i.e. one primitive/leaf
|
|
ASSERT(N->GetNbPrimitives()==1);
|
|
// Get the primitive index from the input tree
|
|
udword PrimitiveIndex = N->GetPrimitives()[0];
|
|
// Setup prev box data as the primitive index, marked as leaf
|
|
linear[box_id].mNegData = (PrimitiveIndex<<1)|1;
|
|
}
|
|
else
|
|
{
|
|
// Get a new id for negative child
|
|
udword NegID = current_id++;
|
|
// Setup box data
|
|
linear[box_id].mNegData = (size_t)&linear[NegID];
|
|
// Make sure it's not marked as leaf
|
|
ASSERT(!(linear[box_id].mNegData&1));
|
|
// Recurse
|
|
_BuildNoLeafTree(linear, NegID, current_id, N);
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
/**
|
|
* Constructor.
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
AABBCollisionTree::AABBCollisionTree() : mNodes(null)
|
|
{
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
/**
|
|
* Destructor.
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
AABBCollisionTree::~AABBCollisionTree()
|
|
{
|
|
DELETEARRAY(mNodes);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
/**
|
|
* Builds the collision tree from a generic AABB tree.
|
|
* \param tree [in] generic AABB tree
|
|
* \return true if success
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
bool AABBCollisionTree::Build(AABBTree* tree)
|
|
{
|
|
// Checkings
|
|
if(!tree) return false;
|
|
// Check the input tree is complete
|
|
udword NbTriangles = tree->GetNbPrimitives();
|
|
udword NbNodes = tree->GetNbNodes();
|
|
if(NbNodes!=NbTriangles*2-1) return false;
|
|
|
|
// Get nodes
|
|
if(mNbNodes!=NbNodes) // Same number of nodes => keep moving
|
|
{
|
|
mNbNodes = NbNodes;
|
|
DELETEARRAY(mNodes);
|
|
mNodes = new AABBCollisionNode[mNbNodes];
|
|
CHECKALLOC(mNodes);
|
|
}
|
|
|
|
// Build the tree
|
|
udword CurID = 1;
|
|
_BuildCollisionTree(mNodes, 0, CurID, tree);
|
|
ASSERT(CurID==mNbNodes);
|
|
|
|
return true;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
/**
|
|
* Refits the collision tree after vertices have been modified.
|
|
* \param mesh_interface [in] mesh interface for current model
|
|
* \return true if success
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
bool AABBCollisionTree::Refit(const MeshInterface* mesh_interface)
|
|
{
|
|
ASSERT(!"Not implemented since AABBCollisionTrees have twice as more nodes to refit as AABBNoLeafTrees!");
|
|
return false;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
/**
|
|
* Walks the tree and call the user back for each node.
|
|
* \param callback [in] walking callback
|
|
* \param user_data [in] callback's user data
|
|
* \return true if success
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
bool AABBCollisionTree::Walk(GenericWalkingCallback callback, void* user_data) const
|
|
{
|
|
if(!callback) return false;
|
|
|
|
struct Local
|
|
{
|
|
static void _Walk(const AABBCollisionNode* current_node, GenericWalkingCallback callback, void* user_data)
|
|
{
|
|
if(!current_node || !(callback)(current_node, user_data)) return;
|
|
|
|
if(!current_node->IsLeaf())
|
|
{
|
|
_Walk(current_node->GetPos(), callback, user_data);
|
|
_Walk(current_node->GetNeg(), callback, user_data);
|
|
}
|
|
}
|
|
};
|
|
Local::_Walk(mNodes, callback, user_data);
|
|
return true;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
/**
|
|
* Constructor.
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
AABBNoLeafTree::AABBNoLeafTree() : mNodes(null)
|
|
{
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
/**
|
|
* Destructor.
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
AABBNoLeafTree::~AABBNoLeafTree()
|
|
{
|
|
DELETEARRAY(mNodes);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
/**
|
|
* Builds the collision tree from a generic AABB tree.
|
|
* \param tree [in] generic AABB tree
|
|
* \return true if success
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
bool AABBNoLeafTree::Build(AABBTree* tree)
|
|
{
|
|
// Checkings
|
|
if(!tree) return false;
|
|
// Check the input tree is complete
|
|
udword NbTriangles = tree->GetNbPrimitives();
|
|
udword NbNodes = tree->GetNbNodes();
|
|
if(NbNodes!=NbTriangles*2-1) return false;
|
|
|
|
// Get nodes
|
|
if(mNbNodes!=NbTriangles-1) // Same number of nodes => keep moving
|
|
{
|
|
mNbNodes = NbTriangles-1;
|
|
DELETEARRAY(mNodes);
|
|
mNodes = new AABBNoLeafNode[mNbNodes];
|
|
CHECKALLOC(mNodes);
|
|
}
|
|
|
|
// Build the tree
|
|
udword CurID = 1;
|
|
_BuildNoLeafTree(mNodes, 0, CurID, tree);
|
|
ASSERT(CurID==mNbNodes);
|
|
|
|
return true;
|
|
}
|
|
|
|
inline_ void ComputeMinMax(Point& min, Point& max, const VertexPointers& vp)
|
|
{
|
|
// Compute triangle's AABB = a leaf box
|
|
#ifdef OPC_USE_FCOMI // a 15% speedup on my machine, not much
|
|
min.x = FCMin3(vp.Vertex[0]->x, vp.Vertex[1]->x, vp.Vertex[2]->x);
|
|
max.x = FCMax3(vp.Vertex[0]->x, vp.Vertex[1]->x, vp.Vertex[2]->x);
|
|
|
|
min.y = FCMin3(vp.Vertex[0]->y, vp.Vertex[1]->y, vp.Vertex[2]->y);
|
|
max.y = FCMax3(vp.Vertex[0]->y, vp.Vertex[1]->y, vp.Vertex[2]->y);
|
|
|
|
min.z = FCMin3(vp.Vertex[0]->z, vp.Vertex[1]->z, vp.Vertex[2]->z);
|
|
max.z = FCMax3(vp.Vertex[0]->z, vp.Vertex[1]->z, vp.Vertex[2]->z);
|
|
#else
|
|
min = *vp.Vertex[0];
|
|
max = *vp.Vertex[0];
|
|
min.Min(*vp.Vertex[1]);
|
|
max.Max(*vp.Vertex[1]);
|
|
min.Min(*vp.Vertex[2]);
|
|
max.Max(*vp.Vertex[2]);
|
|
#endif
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
/**
|
|
* Refits the collision tree after vertices have been modified.
|
|
* \param mesh_interface [in] mesh interface for current model
|
|
* \return true if success
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
bool AABBNoLeafTree::Refit(const MeshInterface* mesh_interface)
|
|
{
|
|
// Checkings
|
|
if(!mesh_interface) return false;
|
|
|
|
// Bottom-up update
|
|
VertexPointers VP;
|
|
Point Min,Max;
|
|
Point Min_,Max_;
|
|
udword Index = mNbNodes;
|
|
while(Index--)
|
|
{
|
|
AABBNoLeafNode& Current = mNodes[Index];
|
|
|
|
if(Current.HasPosLeaf())
|
|
{
|
|
mesh_interface->GetTriangle(VP, Current.GetPosPrimitive());
|
|
ComputeMinMax(Min, Max, VP);
|
|
}
|
|
else
|
|
{
|
|
const CollisionAABB& CurrentBox = Current.GetPos()->mAABB;
|
|
CurrentBox.GetMin(Min);
|
|
CurrentBox.GetMax(Max);
|
|
}
|
|
|
|
if(Current.HasNegLeaf())
|
|
{
|
|
mesh_interface->GetTriangle(VP, Current.GetNegPrimitive());
|
|
ComputeMinMax(Min_, Max_, VP);
|
|
}
|
|
else
|
|
{
|
|
const CollisionAABB& CurrentBox = Current.GetNeg()->mAABB;
|
|
CurrentBox.GetMin(Min_);
|
|
CurrentBox.GetMax(Max_);
|
|
}
|
|
#ifdef OPC_USE_FCOMI
|
|
Min.x = FCMin2(Min.x, Min_.x);
|
|
Max.x = FCMax2(Max.x, Max_.x);
|
|
Min.y = FCMin2(Min.y, Min_.y);
|
|
Max.y = FCMax2(Max.y, Max_.y);
|
|
Min.z = FCMin2(Min.z, Min_.z);
|
|
Max.z = FCMax2(Max.z, Max_.z);
|
|
#else
|
|
Min.Min(Min_);
|
|
Max.Max(Max_);
|
|
#endif
|
|
Current.mAABB.SetMinMax(Min, Max);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
/**
|
|
* Walks the tree and call the user back for each node.
|
|
* \param callback [in] walking callback
|
|
* \param user_data [in] callback's user data
|
|
* \return true if success
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
bool AABBNoLeafTree::Walk(GenericWalkingCallback callback, void* user_data) const
|
|
{
|
|
if(!callback) return false;
|
|
|
|
struct Local
|
|
{
|
|
static void _Walk(const AABBNoLeafNode* current_node, GenericWalkingCallback callback, void* user_data)
|
|
{
|
|
if(!current_node || !(callback)(current_node, user_data)) return;
|
|
|
|
if(!current_node->HasPosLeaf()) _Walk(current_node->GetPos(), callback, user_data);
|
|
if(!current_node->HasNegLeaf()) _Walk(current_node->GetNeg(), callback, user_data);
|
|
}
|
|
};
|
|
Local::_Walk(mNodes, callback, user_data);
|
|
return true;
|
|
}
|
|
|
|
// Quantization notes:
|
|
// - We could use the highest bits of mData to store some more quantized bits. Dequantization code
|
|
// would be slightly more complex, but number of overlap tests would be reduced (and anyhow those
|
|
// bits are currently wasted). Of course it's not possible if we move to 16 bits mData.
|
|
// - Something like "16 bits floats" could be tested, to bypass the int-to-float conversion.
|
|
// - A dedicated BV-BV test could be used, dequantizing while testing for overlap. (i.e. it's some
|
|
// lazy-dequantization which may save some work in case of early exits). At the very least some
|
|
// muls could be saved by precomputing several more matrices. But maybe not worth the pain.
|
|
// - Do we need to dequantize anyway? Not doing the extents-related muls only implies the box has
|
|
// been scaled, for example.
|
|
// - The deeper we move into the hierarchy, the smaller the extents should be. May not need a fixed
|
|
// number of quantization bits. Even better, could probably be best delta-encoded.
|
|
|
|
|
|
// Find max values. Some people asked why I wasn't simply using the first node. Well, I can't.
|
|
// I'm not looking for (min, max) values like in a standard AABB, I'm looking for the extremal
|
|
// centers/extents in order to quantize them. The first node would only give a single center and
|
|
// a single extents. While extents would be the biggest, the center wouldn't.
|
|
#define FIND_MAX_VALUES \
|
|
/* Get max values */ \
|
|
Point CMax(MIN_FLOAT, MIN_FLOAT, MIN_FLOAT); \
|
|
Point EMax(MIN_FLOAT, MIN_FLOAT, MIN_FLOAT); \
|
|
for(udword i=0;i<mNbNodes;i++) \
|
|
{ \
|
|
if(fabsf(Nodes[i].mAABB.mCenter.x)>CMax.x) CMax.x = fabsf(Nodes[i].mAABB.mCenter.x); \
|
|
if(fabsf(Nodes[i].mAABB.mCenter.y)>CMax.y) CMax.y = fabsf(Nodes[i].mAABB.mCenter.y); \
|
|
if(fabsf(Nodes[i].mAABB.mCenter.z)>CMax.z) CMax.z = fabsf(Nodes[i].mAABB.mCenter.z); \
|
|
if(fabsf(Nodes[i].mAABB.mExtents.x)>EMax.x) EMax.x = fabsf(Nodes[i].mAABB.mExtents.x); \
|
|
if(fabsf(Nodes[i].mAABB.mExtents.y)>EMax.y) EMax.y = fabsf(Nodes[i].mAABB.mExtents.y); \
|
|
if(fabsf(Nodes[i].mAABB.mExtents.z)>EMax.z) EMax.z = fabsf(Nodes[i].mAABB.mExtents.z); \
|
|
}
|
|
|
|
#define INIT_QUANTIZATION \
|
|
udword nbc=15; /* Keep one bit for sign */ \
|
|
udword nbe=15; /* Keep one bit for fix */ \
|
|
if(!gFixQuantized) nbe++; \
|
|
\
|
|
/* Compute quantization coeffs */ \
|
|
Point CQuantCoeff, EQuantCoeff; \
|
|
CQuantCoeff.x = CMax.x!=0.0f ? float((1<<nbc)-1)/CMax.x : 0.0f; \
|
|
CQuantCoeff.y = CMax.y!=0.0f ? float((1<<nbc)-1)/CMax.y : 0.0f; \
|
|
CQuantCoeff.z = CMax.z!=0.0f ? float((1<<nbc)-1)/CMax.z : 0.0f; \
|
|
EQuantCoeff.x = EMax.x!=0.0f ? float((1<<nbe)-1)/EMax.x : 0.0f; \
|
|
EQuantCoeff.y = EMax.y!=0.0f ? float((1<<nbe)-1)/EMax.y : 0.0f; \
|
|
EQuantCoeff.z = EMax.z!=0.0f ? float((1<<nbe)-1)/EMax.z : 0.0f; \
|
|
/* Compute and save dequantization coeffs */ \
|
|
mCenterCoeff.x = CQuantCoeff.x!=0.0f ? 1.0f / CQuantCoeff.x : 0.0f; \
|
|
mCenterCoeff.y = CQuantCoeff.y!=0.0f ? 1.0f / CQuantCoeff.y : 0.0f; \
|
|
mCenterCoeff.z = CQuantCoeff.z!=0.0f ? 1.0f / CQuantCoeff.z : 0.0f; \
|
|
mExtentsCoeff.x = EQuantCoeff.x!=0.0f ? 1.0f / EQuantCoeff.x : 0.0f; \
|
|
mExtentsCoeff.y = EQuantCoeff.y!=0.0f ? 1.0f / EQuantCoeff.y : 0.0f; \
|
|
mExtentsCoeff.z = EQuantCoeff.z!=0.0f ? 1.0f / EQuantCoeff.z : 0.0f; \
|
|
|
|
#define PERFORM_QUANTIZATION \
|
|
/* Quantize */ \
|
|
mNodes[i].mAABB.mCenter[0] = sword(Nodes[i].mAABB.mCenter.x * CQuantCoeff.x); \
|
|
mNodes[i].mAABB.mCenter[1] = sword(Nodes[i].mAABB.mCenter.y * CQuantCoeff.y); \
|
|
mNodes[i].mAABB.mCenter[2] = sword(Nodes[i].mAABB.mCenter.z * CQuantCoeff.z); \
|
|
mNodes[i].mAABB.mExtents[0] = uword(Nodes[i].mAABB.mExtents.x * EQuantCoeff.x); \
|
|
mNodes[i].mAABB.mExtents[1] = uword(Nodes[i].mAABB.mExtents.y * EQuantCoeff.y); \
|
|
mNodes[i].mAABB.mExtents[2] = uword(Nodes[i].mAABB.mExtents.z * EQuantCoeff.z); \
|
|
/* Fix quantized boxes */ \
|
|
if(gFixQuantized) \
|
|
{ \
|
|
/* Make sure the quantized box is still valid */ \
|
|
Point Max = Nodes[i].mAABB.mCenter + Nodes[i].mAABB.mExtents; \
|
|
Point Min = Nodes[i].mAABB.mCenter - Nodes[i].mAABB.mExtents; \
|
|
/* For each axis */ \
|
|
for(udword j=0;j<3;j++) \
|
|
{ /* Dequantize the box center */ \
|
|
float qc = float(mNodes[i].mAABB.mCenter[j]) * mCenterCoeff[j]; \
|
|
bool FixMe=true; \
|
|
do \
|
|
{ /* Dequantize the box extent */ \
|
|
float qe = float(mNodes[i].mAABB.mExtents[j]) * mExtentsCoeff[j]; \
|
|
/* Compare real & dequantized values */ \
|
|
if(qc+qe<Max[j] || qc-qe>Min[j]) mNodes[i].mAABB.mExtents[j]++; \
|
|
else FixMe=false; \
|
|
/* Prevent wrapping */ \
|
|
if(!mNodes[i].mAABB.mExtents[j]) \
|
|
{ \
|
|
mNodes[i].mAABB.mExtents[j]=0xffff; \
|
|
FixMe=false; \
|
|
} \
|
|
}while(FixMe); \
|
|
} \
|
|
}
|
|
|
|
#define REMAP_DATA(member) \
|
|
/* Fix data */ \
|
|
Data = Nodes[i].member; \
|
|
if(!(Data&1)) \
|
|
{ \
|
|
/* Compute box number */ \
|
|
size_t Nb = (Data - size_t(Nodes))/Nodes[i].GetNodeSize(); \
|
|
Data = (size_t) &mNodes[Nb]; \
|
|
} \
|
|
/* ...remapped */ \
|
|
mNodes[i].member = Data;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
/**
|
|
* Constructor.
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
AABBQuantizedTree::AABBQuantizedTree() : mNodes(null)
|
|
{
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
/**
|
|
* Destructor.
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
AABBQuantizedTree::~AABBQuantizedTree()
|
|
{
|
|
DELETEARRAY(mNodes);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
/**
|
|
* Builds the collision tree from a generic AABB tree.
|
|
* \param tree [in] generic AABB tree
|
|
* \return true if success
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
bool AABBQuantizedTree::Build(AABBTree* tree)
|
|
{
|
|
// Checkings
|
|
if(!tree) return false;
|
|
// Check the input tree is complete
|
|
udword NbTriangles = tree->GetNbPrimitives();
|
|
udword NbNodes = tree->GetNbNodes();
|
|
if(NbNodes!=NbTriangles*2-1) return false;
|
|
|
|
// Get nodes
|
|
mNbNodes = NbNodes;
|
|
DELETEARRAY(mNodes);
|
|
AABBCollisionNode* Nodes = new AABBCollisionNode[mNbNodes];
|
|
CHECKALLOC(Nodes);
|
|
|
|
// Build the tree
|
|
udword CurID = 1;
|
|
_BuildCollisionTree(Nodes, 0, CurID, tree);
|
|
|
|
// Quantize
|
|
{
|
|
mNodes = new AABBQuantizedNode[mNbNodes];
|
|
CHECKALLOC(mNodes);
|
|
|
|
// Get max values
|
|
FIND_MAX_VALUES
|
|
|
|
// Quantization
|
|
INIT_QUANTIZATION
|
|
|
|
// Quantize
|
|
size_t Data;
|
|
for(udword i=0;i<mNbNodes;i++)
|
|
{
|
|
PERFORM_QUANTIZATION
|
|
REMAP_DATA(mData)
|
|
}
|
|
|
|
DELETEARRAY(Nodes);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
/**
|
|
* Refits the collision tree after vertices have been modified.
|
|
* \param mesh_interface [in] mesh interface for current model
|
|
* \return true if success
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
bool AABBQuantizedTree::Refit(const MeshInterface* mesh_interface)
|
|
{
|
|
ASSERT(!"Not implemented since requantizing is painful !");
|
|
return false;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
/**
|
|
* Walks the tree and call the user back for each node.
|
|
* \param callback [in] walking callback
|
|
* \param user_data [in] callback's user data
|
|
* \return true if success
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
bool AABBQuantizedTree::Walk(GenericWalkingCallback callback, void* user_data) const
|
|
{
|
|
if(!callback) return false;
|
|
|
|
struct Local
|
|
{
|
|
static void _Walk(const AABBQuantizedNode* current_node, GenericWalkingCallback callback, void* user_data)
|
|
{
|
|
if(!current_node || !(callback)(current_node, user_data)) return;
|
|
|
|
if(!current_node->IsLeaf())
|
|
{
|
|
_Walk(current_node->GetPos(), callback, user_data);
|
|
_Walk(current_node->GetNeg(), callback, user_data);
|
|
}
|
|
}
|
|
};
|
|
Local::_Walk(mNodes, callback, user_data);
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
/**
|
|
* Constructor.
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
AABBQuantizedNoLeafTree::AABBQuantizedNoLeafTree() : mNodes(null)
|
|
{
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
/**
|
|
* Destructor.
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
AABBQuantizedNoLeafTree::~AABBQuantizedNoLeafTree()
|
|
{
|
|
DELETEARRAY(mNodes);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
/**
|
|
* Builds the collision tree from a generic AABB tree.
|
|
* \param tree [in] generic AABB tree
|
|
* \return true if success
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
bool AABBQuantizedNoLeafTree::Build(AABBTree* tree)
|
|
{
|
|
// Checkings
|
|
if(!tree) return false;
|
|
// Check the input tree is complete
|
|
udword NbTriangles = tree->GetNbPrimitives();
|
|
udword NbNodes = tree->GetNbNodes();
|
|
if(NbNodes!=NbTriangles*2-1) return false;
|
|
|
|
// Get nodes
|
|
mNbNodes = NbTriangles-1;
|
|
DELETEARRAY(mNodes);
|
|
AABBNoLeafNode* Nodes = new AABBNoLeafNode[mNbNodes];
|
|
CHECKALLOC(Nodes);
|
|
|
|
// Build the tree
|
|
udword CurID = 1;
|
|
_BuildNoLeafTree(Nodes, 0, CurID, tree);
|
|
ASSERT(CurID==mNbNodes);
|
|
|
|
// Quantize
|
|
{
|
|
mNodes = new AABBQuantizedNoLeafNode[mNbNodes];
|
|
CHECKALLOC(mNodes);
|
|
|
|
// Get max values
|
|
FIND_MAX_VALUES
|
|
|
|
// Quantization
|
|
INIT_QUANTIZATION
|
|
|
|
// Quantize
|
|
size_t Data;
|
|
for(udword i=0;i<mNbNodes;i++)
|
|
{
|
|
PERFORM_QUANTIZATION
|
|
REMAP_DATA(mPosData)
|
|
REMAP_DATA(mNegData)
|
|
}
|
|
|
|
DELETEARRAY(Nodes);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
/**
|
|
* Refits the collision tree after vertices have been modified.
|
|
* \param mesh_interface [in] mesh interface for current model
|
|
* \return true if success
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
bool AABBQuantizedNoLeafTree::Refit(const MeshInterface* mesh_interface)
|
|
{
|
|
ASSERT(!"Not implemented since requantizing is painful !");
|
|
return false;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
/**
|
|
* Walks the tree and call the user back for each node.
|
|
* \param callback [in] walking callback
|
|
* \param user_data [in] callback's user data
|
|
* \return true if success
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
bool AABBQuantizedNoLeafTree::Walk(GenericWalkingCallback callback, void* user_data) const
|
|
{
|
|
if(!callback) return false;
|
|
|
|
struct Local
|
|
{
|
|
static void _Walk(const AABBQuantizedNoLeafNode* current_node, GenericWalkingCallback callback, void* user_data)
|
|
{
|
|
if(!current_node || !(callback)(current_node, user_data)) return;
|
|
|
|
if(!current_node->HasPosLeaf()) _Walk(current_node->GetPos(), callback, user_data);
|
|
if(!current_node->HasNegLeaf()) _Walk(current_node->GetNeg(), callback, user_data);
|
|
}
|
|
};
|
|
Local::_Walk(mNodes, callback, user_data);
|
|
return true;
|
|
}
|