1 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
3 * OPCODE - Optimized Collision Detection
4 * Copyright (C) 2001 Pierre Terdiman
5 * Homepage: http://www.codercorner.com/Opcode.htm
7 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
9 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
11 * Contains code for hybrid models.
12 * \file OPC_HybridModel.cpp
13 * \author Pierre Terdiman
16 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
18 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
20 * An hybrid collision model.
24 * Opcode really shines for mesh-mesh collision, especially when meshes are deeply overlapping
25 * (it typically outperforms RAPID in those cases).
27 * Unfortunately this is not the typical scenario in games.
29 * For close-proximity cases, especially for volume-mesh queries, it's relatively easy to run faster
30 * than Opcode, that suffers from a relatively high setup time.
32 * In particular, Opcode's "vanilla" trees in those cases -can- run faster. They can also use -less-
33 * memory than the optimized ones, when you let the system stop at ~10 triangles / leaf for example
34 * (i.e. when you don't use "complete" trees). However, those trees tend to fragment memory quite a
35 * lot, increasing cache misses : since they're not "complete", we can't predict the final number of
36 * nodes and we have to allocate nodes on-the-fly. For the same reasons we can't use Opcode's "optimized"
37 * trees here, since they rely on a known layout to perform the "optimization".
41 * Hybrid trees try to combine best of both worlds :
43 * - they use a maximum limit of 16 triangles/leaf. "16" is used so that we'll be able to save the
44 * number of triangles using 4 bits only.
46 * - they're still "complete" trees thanks to a two-passes building phase. First we create a "vanilla"
47 * AABB-tree with Opcode, limited to 16 triangles/leaf. Then we create a *second* vanilla tree, this
48 * time using the leaves of the first one. The trick is : this second tree is now "complete"... so we
49 * can further transform it into an Opcode's optimized tree.
51 * - then we run the collision queries on that standard Opcode tree. The only difference is that leaf
52 * nodes contain indices to leaf nodes of another tree. Also, we have to skip all primitive tests in
53 * Opcode optimized trees, since our leaves don't contain triangles anymore.
55 * - finally, for each collided leaf, we simply loop through 16 triangles max, and collide them with
56 * the bounding volume used in the query (we only support volume-vs-mesh queries here, not mesh-vs-mesh)
58 * All of that is wrapped in this "hybrid model" that contains the minimal data required for this to work.
59 * It's a mix between old "vanilla" trees, and old "optimized" trees.
63 * - If we use them for dynamic models, we're left with a very small number of leaf nodes to refit. It
64 * might be a bit faster since we have less nodes to write back.
66 * - In rigid body simulation, using temporal coherence and sleeping objects greatly reduce the actual
67 * influence of one tree over another (i.e. the speed difference is often invisible). So memory is really
68 * the key element to consider, and in this regard hybrid trees are just better.
70 * Information to take home:
72 * - they're not slower (they're faster or slower depending on cases, overall there's no significant
73 * difference *as long as objects don't interpenetrate too much* - in which case Opcode's optimized trees
74 * are still notably faster)
77 * \author Pierre Terdiman
81 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
83 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
87 using namespace Opcode
;
89 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
93 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
94 HybridModel::HybridModel() :
102 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
106 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
107 HybridModel::~HybridModel()
112 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
114 * Releases everything.
116 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
117 void HybridModel::Release()
120 DELETEARRAY(mIndices
);
121 DELETEARRAY(mTriangles
);
137 DELETEARRAY(mLeaves
);
142 LeafTriangles
* mTriangles
;
143 const dTriIndex
* mBase
;
146 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
148 * Builds a collision model.
149 * \param create [in] model creation structure
150 * \return true if success
152 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
153 bool HybridModel::Build(const OPCODECREATE
& create
)
156 if(!create
.mIMesh
|| !create
.mIMesh
->IsValid()) return false;
158 // Look for degenerate faces.
159 //udword NbDegenerate = create.mIMesh->CheckTopology();
160 //if(NbDegenerate) Log("OPCODE WARNING: found %d degenerate faces in model! Collision might report wrong results!\n", NbDegenerate);
161 // We continue nonetheless....
163 Release(); // Make sure previous tree has been discarded
165 // 1-1) Setup mesh interface automatically
166 SetMeshInterface(create
.mIMesh
);
169 AABBTree
* LeafTree
= null
;
172 // 2) Build a generic AABB Tree.
173 mSource
= new AABBTree
;
176 // 2-1) Setup a builder. Our primitives here are triangles from input mesh,
177 // so we use an AABBTreeOfTrianglesBuilder.....
179 AABBTreeOfTrianglesBuilder TB
;
180 TB
.mIMesh
= create
.mIMesh
;
181 TB
.mNbPrimitives
= create
.mIMesh
->GetNbTriangles();
182 TB
.mSettings
= create
.mSettings
;
183 TB
.mSettings
.mLimit
= 16; // ### Hardcoded, but maybe we could let the user choose 8 / 16 / 32 ...
184 if(!mSource
->Build(&TB
)) goto FreeAndExit
;
187 // 2-2) Here's the trick : create *another* AABB tree using the leaves of the first one (which are boxes, this time)
190 // A callback to count leaf nodes
191 static bool CountLeaves(const AABBTreeNode
* current
, udword
/*depth*/, void* user_data
)
193 if(current
->IsLeaf())
195 Internal
* Data
= (Internal
*)user_data
;
201 // A callback to setup leaf nodes in our internal structures
202 static bool SetupLeafData(const AABBTreeNode
* current
, udword
/*depth*/, void* user_data
)
204 if(current
->IsLeaf())
206 Internal
* Data
= (Internal
*)user_data
;
208 // Get current leaf's box
209 Data
->mLeaves
[Data
->mNbLeaves
] = *current
->GetAABB();
212 udword Index
= udword((size_t(current
->GetPrimitives()) - size_t(Data
->mBase
)) / sizeof(udword
));
213 Data
->mTriangles
[Data
->mNbLeaves
].SetData(current
->GetNbPrimitives(), Index
);
221 // Walk the tree & count number of leaves
223 mSource
->Walk(Local::CountLeaves
, &Data
);
224 mNbLeaves
= Data
.mNbLeaves
; // Keep track of it
226 // Special case for 1-leaf meshes
229 mModelCode
|= OPC_SINGLE_NODE
;
234 // Allocate our structures
235 Data
.mLeaves
= new AABB
[Data
.mNbLeaves
]; CHECKALLOC(Data
.mLeaves
);
236 mTriangles
= new LeafTriangles
[Data
.mNbLeaves
]; CHECKALLOC(mTriangles
);
238 // Walk the tree again & setup leaf data
239 Data
.mTriangles
= mTriangles
;
240 Data
.mBase
= mSource
->GetIndices();
241 Data
.mNbLeaves
= 0; // Reset for incoming walk
242 mSource
->Walk(Local::SetupLeafData
, &Data
);
244 // Handle source indices
246 bool MustKeepIndices
= true;
249 // We try to get rid of source indices (saving more ram!) by reorganizing triangle arrays...
250 // Remap can fail when we use callbacks => keep track of indices in that case (it still
251 // works, only using more memory)
252 if(create
.mIMesh
->RemapClient(mSource
->GetNbPrimitives(), mSource
->GetIndices()))
254 MustKeepIndices
= false;
260 // Keep track of source indices (from vanilla tree)
261 mNbPrimitives
= mSource
->GetNbPrimitives();
262 mIndices
= new udword
[mNbPrimitives
];
263 CopyMemory(mIndices
, mSource
->GetIndices(), mNbPrimitives
*sizeof(udword
));
267 // Now, create our optimized tree using previous leaf nodes
268 LeafTree
= new AABBTree
;
269 CHECKALLOC(LeafTree
);
271 AABBTreeOfAABBsBuilder TB
; // Now using boxes !
272 TB
.mSettings
= create
.mSettings
;
273 TB
.mSettings
.mLimit
= 1; // We now want a complete tree so that we can "optimize" it
274 TB
.mNbPrimitives
= Data
.mNbLeaves
;
275 TB
.mAABBArray
= Data
.mLeaves
;
276 if(!LeafTree
->Build(&TB
)) goto FreeAndExit
;
279 // 3) Create an optimized tree according to user-settings
280 if(!CreateTree(create
.mNoLeaf
, create
.mQuantized
)) goto FreeAndExit
;
282 // 3-2) Create optimized tree
283 if(!mTree
->Build(LeafTree
)) goto FreeAndExit
;
288 FreeAndExit
: // Allow me this one...
289 DELETESINGLE(LeafTree
);
291 // 3-3) Delete generic tree if needed
292 if(!create
.mKeepOriginal
) DELETESINGLE(mSource
);
297 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
299 * Gets the number of bytes used by the tree.
300 * \return amount of bytes used
302 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
303 udword
HybridModel::GetUsedBytes() const
305 udword UsedBytes
= 0;
306 if(mTree
) UsedBytes
+= mTree
->GetUsedBytes();
307 if(mIndices
) UsedBytes
+= mNbPrimitives
* sizeof(udword
); // mIndices
308 if(mTriangles
) UsedBytes
+= mNbLeaves
* sizeof(LeafTriangles
); // mTriangles
312 inline_
void ComputeMinMax(Point
& min
, Point
& max
, const VertexPointers
& vp
)
314 // Compute triangle's AABB = a leaf box
315 #ifdef OPC_USE_FCOMI // a 15% speedup on my machine, not much
316 min
.x
= FCMin3(vp
.Vertex
[0]->x
, vp
.Vertex
[1]->x
, vp
.Vertex
[2]->x
);
317 max
.x
= FCMax3(vp
.Vertex
[0]->x
, vp
.Vertex
[1]->x
, vp
.Vertex
[2]->x
);
319 min
.y
= FCMin3(vp
.Vertex
[0]->y
, vp
.Vertex
[1]->y
, vp
.Vertex
[2]->y
);
320 max
.y
= FCMax3(vp
.Vertex
[0]->y
, vp
.Vertex
[1]->y
, vp
.Vertex
[2]->y
);
322 min
.z
= FCMin3(vp
.Vertex
[0]->z
, vp
.Vertex
[1]->z
, vp
.Vertex
[2]->z
);
323 max
.z
= FCMax3(vp
.Vertex
[0]->z
, vp
.Vertex
[1]->z
, vp
.Vertex
[2]->z
);
327 min
.Min(*vp
.Vertex
[1]);
328 max
.Max(*vp
.Vertex
[1]);
329 min
.Min(*vp
.Vertex
[2]);
330 max
.Max(*vp
.Vertex
[2]);
334 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
336 * Refits the collision model. This can be used to handle dynamic meshes. Usage is:
337 * 1. modify your mesh vertices (keep the topology constant!)
338 * 2. refit the tree (call this method)
339 * \return true if success
341 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
342 bool HybridModel::Refit()
344 if(!mIMesh
) return false;
345 if(!mTree
) return false;
347 if(IsQuantized()) return false;
348 if(HasLeafNodes()) return false;
350 const LeafTriangles
* LT
= GetLeafTriangles();
351 const udword
* Indices
= GetIndices();
358 udword Index
= mTree
->GetNbNodes();
359 AABBNoLeafNode
* Nodes
= const_cast<AABBNoLeafNode
*>(static_cast<const AABBNoLeafNode
*>(static_cast<AABBNoLeafTree
*>(mTree
)->GetNodes()));
362 AABBNoLeafNode
& Current
= Nodes
[Index
];
364 if(Current
.HasPosLeaf())
366 const LeafTriangles
& CurrentLeaf
= LT
[Current
.GetPosPrimitive()];
368 Min
.SetPlusInfinity();
369 Max
.SetMinusInfinity();
371 Point TmpMin
, TmpMax
;
373 // Each leaf box has a set of triangles
374 udword NbTris
= CurrentLeaf
.GetNbTriangles();
377 const udword
* T
= &Indices
[CurrentLeaf
.GetTriangleIndex()];
379 // Loop through triangles and test each of them
382 mIMesh
->GetTriangle(VP
, *T
++, VC
);
383 ComputeMinMax(TmpMin
, TmpMax
, VP
);
390 udword BaseIndex
= CurrentLeaf
.GetTriangleIndex();
392 // Loop through triangles and test each of them
395 mIMesh
->GetTriangle(VP
, BaseIndex
++, VC
);
396 ComputeMinMax(TmpMin
, TmpMax
, VP
);
404 const CollisionAABB
& CurrentBox
= Current
.GetPos()->mAABB
;
405 CurrentBox
.GetMin(Min
);
406 CurrentBox
.GetMax(Max
);
409 if(Current
.HasNegLeaf())
411 const LeafTriangles
& CurrentLeaf
= LT
[Current
.GetNegPrimitive()];
413 Min_
.SetPlusInfinity();
414 Max_
.SetMinusInfinity();
416 Point TmpMin
, TmpMax
;
418 // Each leaf box has a set of triangles
419 udword NbTris
= CurrentLeaf
.GetNbTriangles();
422 const udword
* T
= &Indices
[CurrentLeaf
.GetTriangleIndex()];
424 // Loop through triangles and test each of them
427 mIMesh
->GetTriangle(VP
, *T
++, VC
);
428 ComputeMinMax(TmpMin
, TmpMax
, VP
);
435 udword BaseIndex
= CurrentLeaf
.GetTriangleIndex();
437 // Loop through triangles and test each of them
440 mIMesh
->GetTriangle(VP
, BaseIndex
++, VC
);
441 ComputeMinMax(TmpMin
, TmpMax
, VP
);
449 const CollisionAABB
& CurrentBox
= Current
.GetNeg()->mAABB
;
450 CurrentBox
.GetMin(Min_
);
451 CurrentBox
.GetMax(Max_
);
454 Min
.x
= FCMin2(Min
.x
, Min_
.x
);
455 Max
.x
= FCMax2(Max
.x
, Max_
.x
);
456 Min
.y
= FCMin2(Min
.y
, Min_
.y
);
457 Max
.y
= FCMax2(Max
.y
, Max_
.y
);
458 Min
.z
= FCMin2(Min
.z
, Min_
.z
);
459 Max
.z
= FCMax2(Max
.z
, Max_
.z
);
464 Current
.mAABB
.SetMinMax(Min
, Max
);