Vegastrike 0.5.1 rc1  1.0
Original sources for Vegastrike Evolved
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
OPC_OptimizedTree.cpp
Go to the documentation of this file.
1 /*
3  * OPCODE - Optimized Collision Detection
4  * Copyright (C) 2001 Pierre Terdiman
5  * Homepage: http://www.codercorner.com/Opcode.htm
6  */
8 
10 
21 
24 
32 
35 
43 
46 
54 
57 
65 
68 // Precompiled Header
69 #include "Stdafx.h"
70 
71 
72 using namespace Opcode;
73 
74 
78 static bool gFixQuantized = true;
79 
81 
100 static void _BuildCollisionTree(AABBCollisionNode* linear, const udword box_id, udword& current_id, const AABBTreeNode* current_node)
102 {
103  // Current node from input tree is "current_node". Must be flattened into "linear[boxid]".
104 
105  // Store the AABB
106  current_node->GetAABB()->GetCenter(linear[box_id].mAABB.mCenter);
107  current_node->GetAABB()->GetExtents(linear[box_id].mAABB.mExtents);
108  // Store remaining info
109  if(current_node->IsLeaf())
110  {
111  // The input tree must be complete => i.e. one primitive/leaf
112  OPASSERT(current_node->GetNbPrimitives()==1);
113  // Get the primitive index from the input tree
114  udword PrimitiveIndex = current_node->GetPrimitives()[0];
115  // Setup box data as the primitive index, marked as leaf
116  linear[box_id].mData = (PrimitiveIndex<<1)|1;
117  }
118  else
119  {
120  // To make the negative one implicit, we must store P and N in successive order
121  udword PosID = current_id++; // Get a new id for positive child
122  udword NegID = current_id++; // Get a new id for negative child
123  // Setup box data as the forthcoming new P pointer
124  linear[box_id].mData = (uintptr_t)&linear[PosID];
125  // Make sure it's not marked as leaf
126  OPASSERT(!(linear[box_id].mData&1));
127  // Recurse with new IDs
128  _BuildCollisionTree(linear, PosID, current_id, current_node->GetPos());
129  _BuildCollisionTree(linear, NegID, current_id, current_node->GetNeg());
130  }
131 }
132 
134 
151 static void _BuildNoLeafTree(AABBNoLeafNode* linear, const udword box_id, udword& current_id, const AABBTreeNode* current_node)
153 {
154  const AABBTreeNode* P = current_node->GetPos();
155  const AABBTreeNode* N = current_node->GetNeg();
156  // Leaf nodes here?!
157  OPASSERT(P);
158  OPASSERT(N);
159  // Internal node => keep the box
160  current_node->GetAABB()->GetCenter(linear[box_id].mAABB.mCenter);
161  current_node->GetAABB()->GetExtents(linear[box_id].mAABB.mExtents);
162 
163  if(P->IsLeaf())
164  {
165  // The input tree must be complete => i.e. one primitive/leaf
166  OPASSERT(P->GetNbPrimitives()==1);
167  // Get the primitive index from the input tree
168  udword PrimitiveIndex = P->GetPrimitives()[0];
169  // Setup prev box data as the primitive index, marked as leaf
170  linear[box_id].mPosData = (PrimitiveIndex<<1)|1;
171  }
172  else
173  {
174  // Get a new id for positive child
175  udword PosID = current_id++;
176  // Setup box data
177  linear[box_id].mPosData = (uintptr_t)&linear[PosID];
178  // Make sure it's not marked as leaf
179  OPASSERT(!(linear[box_id].mPosData&1));
180  // Recurse
181  _BuildNoLeafTree(linear, PosID, current_id, P);
182  }
183 
184  if(N->IsLeaf())
185  {
186  // The input tree must be complete => i.e. one primitive/leaf
187  OPASSERT(N->GetNbPrimitives()==1);
188  // Get the primitive index from the input tree
189  udword PrimitiveIndex = N->GetPrimitives()[0];
190  // Setup prev box data as the primitive index, marked as leaf
191  linear[box_id].mNegData = (PrimitiveIndex<<1)|1;
192  }
193  else
194  {
195  // Get a new id for negative child
196  udword NegID = current_id++;
197  // Setup box data
198  linear[box_id].mNegData = (uintptr_t)&linear[NegID];
199  // Make sure it's not marked as leaf
200  OPASSERT(!(linear[box_id].mNegData&1));
201  // Recurse
202  _BuildNoLeafTree(linear, NegID, current_id, N);
203  }
204 }
205 
207 
210 AABBCollisionTree::AABBCollisionTree() : mNodes(null)
212 {
213 }
214 
216 
219 AABBCollisionTree::~AABBCollisionTree()
221 {
222  DELETEARRAY(mNodes);
223 }
224 
226 
233 {
234  // Checkings
235  if(!tree) return false;
236  // Check the input tree is complete
237  udword NbTriangles = tree->GetNbPrimitives();
238  udword NbNodes = tree->GetNbNodes();
239  if(NbNodes!=NbTriangles*2-1) return false;
240 
241  // Get nodes
242  if(mNbNodes!=NbNodes) // Same number of nodes => keep moving
243  {
244  mNbNodes = NbNodes;
245  DELETEARRAY(mNodes);
246  mNodes = new AABBCollisionNode[mNbNodes];
247  CHECKALLOC(mNodes);
248  }
249 
250  // Build the tree
251  udword CurID = 1;
252  _BuildCollisionTree(mNodes, 0, CurID, tree);
253  OPASSERT(CurID==mNbNodes);
254 
255  return true;
256 }
257 
259 
264 bool AABBCollisionTree::Refit(const MeshInterface* /*mesh_interface*/)
266 {
267  OPASSERT(!"Not implemented since AABBCollisionTrees have twice as more nodes to refit as AABBNoLeafTrees!");
268  return false;
269 }
270 
272 
278 bool AABBCollisionTree::Walk(GenericWalkingCallback callback, void* user_data) const
280 {
281  if(!callback) return false;
282 
283  struct Local
284  {
285  static void _Walk(const AABBCollisionNode* current_node, GenericWalkingCallback callback, void* user_data)
286  {
287  if(!current_node || !(callback)(current_node, user_data)) return;
288 
289  if(!current_node->IsLeaf())
290  {
291  _Walk(current_node->GetPos(), callback, user_data);
292  _Walk(current_node->GetNeg(), callback, user_data);
293  }
294  }
295  };
296  Local::_Walk(mNodes, callback, user_data);
297  return true;
298 }
299 
300 
302 
305 AABBNoLeafTree::AABBNoLeafTree() : mNodes(null)
307 {
308 }
309 
311 
314 AABBNoLeafTree::~AABBNoLeafTree()
316 {
317  DELETEARRAY(mNodes);
318 }
319 
321 
328 {
329  // Checkings
330  if(!tree) return false;
331  // Check the input tree is complete
332  udword NbTriangles = tree->GetNbPrimitives();
333  udword NbNodes = tree->GetNbNodes();
334  if(NbNodes!=NbTriangles*2-1) return false;
335 
336  // Get nodes
337  if(mNbNodes!=NbTriangles-1) // Same number of nodes => keep moving
338  {
339  mNbNodes = NbTriangles-1;
340  DELETEARRAY(mNodes);
341  mNodes = new AABBNoLeafNode[mNbNodes];
342  CHECKALLOC(mNodes);
343  }
344 
345  // Build the tree
346  udword CurID = 1;
347  _BuildNoLeafTree(mNodes, 0, CurID, tree);
348  OPASSERT(CurID==mNbNodes);
349 
350  return true;
351 }
352 
354 {
355  // Compute triangle's AABB = a leaf box
356 #ifdef OPC_USE_FCOMI // a 15% speedup on my machine, not much
357  min.x = FCMin3(vp.Vertex[0]->x, vp.Vertex[1]->x, vp.Vertex[2]->x);
358  max.x = FCMax3(vp.Vertex[0]->x, vp.Vertex[1]->x, vp.Vertex[2]->x);
359 
360  min.y = FCMin3(vp.Vertex[0]->y, vp.Vertex[1]->y, vp.Vertex[2]->y);
361  max.y = FCMax3(vp.Vertex[0]->y, vp.Vertex[1]->y, vp.Vertex[2]->y);
362 
363  min.z = FCMin3(vp.Vertex[0]->z, vp.Vertex[1]->z, vp.Vertex[2]->z);
364  max.z = FCMax3(vp.Vertex[0]->z, vp.Vertex[1]->z, vp.Vertex[2]->z);
365 #else
366  min = *vp.Vertex[0];
367  max = *vp.Vertex[0];
368  min.Min(*vp.Vertex[1]);
369  max.Max(*vp.Vertex[1]);
370  min.Min(*vp.Vertex[2]);
371  max.Max(*vp.Vertex[2]);
372 #endif
373 }
374 
376 
381 bool AABBNoLeafTree::Refit(const MeshInterface* mesh_interface)
383 {
384  // Checkings
385  if(!mesh_interface) return false;
386 
387  // Bottom-up update
388  VertexPointers VP;
389  Point Min,Max;
390  Point Min_,Max_;
391  udword Index = mNbNodes;
392  while(Index--)
393  {
394  AABBNoLeafNode& Current = mNodes[Index];
395 
396  if(Current.HasPosLeaf())
397  {
398  mesh_interface->GetTriangle(VP, Current.GetPosPrimitive());
399  OPComputeMinMax(Min, Max, VP);
400  }
401  else
402  {
403  const CollisionAABB& CurrentBox = Current.GetPos()->mAABB;
404  CurrentBox.GetMin(Min);
405  CurrentBox.GetMax(Max);
406  }
407 
408  if(Current.HasNegLeaf())
409  {
410  mesh_interface->GetTriangle(VP, Current.GetNegPrimitive());
411  OPComputeMinMax(Min_, Max_, VP);
412  }
413  else
414  {
415  const CollisionAABB& CurrentBox = Current.GetNeg()->mAABB;
416  CurrentBox.GetMin(Min_);
417  CurrentBox.GetMax(Max_);
418  }
419 #ifdef OPC_USE_FCOMI
420  Min.x = FCMin2(Min.x, Min_.x);
421  Max.x = FCMax2(Max.x, Max_.x);
422  Min.y = FCMin2(Min.y, Min_.y);
423  Max.y = FCMax2(Max.y, Max_.y);
424  Min.z = FCMin2(Min.z, Min_.z);
425  Max.z = FCMax2(Max.z, Max_.z);
426 #else
427  Min.Min(Min_);
428  Max.Max(Max_);
429 #endif
430  Current.mAABB.SetMinMax(Min, Max);
431  }
432  return true;
433 }
434 
436 
442 bool AABBNoLeafTree::Walk(GenericWalkingCallback callback, void* user_data) const
444 {
445  if(!callback) return false;
446 
447  struct Local
448  {
449  static void _Walk(const AABBNoLeafNode* current_node, GenericWalkingCallback callback, void* user_data)
450  {
451  if(!current_node || !(callback)(current_node, user_data)) return;
452 
453  if(!current_node->HasPosLeaf()) _Walk(current_node->GetPos(), callback, user_data);
454  if(!current_node->HasNegLeaf()) _Walk(current_node->GetNeg(), callback, user_data);
455  }
456  };
457  Local::_Walk(mNodes, callback, user_data);
458  return true;
459 }
460 
461 // Quantization notes:
462 // - We could use the highest bits of mData to store some more quantized bits. Dequantization code
463 // would be slightly more complex, but number of overlap tests would be reduced (and anyhow those
464 // bits are currently wasted). Of course it's not possible if we move to 16 bits mData.
465 // - Something like "16 bits floats" could be tested, to bypass the int-to-float conversion.
466 // - A dedicated BV-BV test could be used, dequantizing while testing for overlap. (i.e. it's some
467 // lazy-dequantization which may save some work in case of early exits). At the very least some
468 // muls could be saved by precomputing several more matrices. But maybe not worth the pain.
469 // - Do we need to dequantize anyway? Not doing the extents-related muls only implies the box has
470 // been scaled, for example.
471 // - The deeper we move into the hierarchy, the smaller the extents should be. May not need a fixed
472 // number of quantization bits. Even better, could probably be best delta-encoded.
473 
474 
475 // Find max values. Some people asked why I wasn't simply using the first node. Well, I can't.
476 // I'm not looking for (min, max) values like in a standard AABB, I'm looking for the extremal
477 // centers/extents in order to quantize them. The first node would only give a single center and
478 // a single extents. While extents would be the biggest, the center wouldn't.
479 #define FIND_MAX_VALUES \
480  /* Get max values */ \
481  Point CMax(MIN_FLOAT, MIN_FLOAT, MIN_FLOAT); \
482  Point EMax(MIN_FLOAT, MIN_FLOAT, MIN_FLOAT); \
483  for(i=0;i<mNbNodes;i++) \
484  { \
485  if(fabsf(Nodes[i].mAABB.mCenter.x)>CMax.x) CMax.x = fabsf(Nodes[i].mAABB.mCenter.x); \
486  if(fabsf(Nodes[i].mAABB.mCenter.y)>CMax.y) CMax.y = fabsf(Nodes[i].mAABB.mCenter.y); \
487  if(fabsf(Nodes[i].mAABB.mCenter.z)>CMax.z) CMax.z = fabsf(Nodes[i].mAABB.mCenter.z); \
488  if(fabsf(Nodes[i].mAABB.mExtents.x)>EMax.x) EMax.x = fabsf(Nodes[i].mAABB.mExtents.x); \
489  if(fabsf(Nodes[i].mAABB.mExtents.y)>EMax.y) EMax.y = fabsf(Nodes[i].mAABB.mExtents.y); \
490  if(fabsf(Nodes[i].mAABB.mExtents.z)>EMax.z) EMax.z = fabsf(Nodes[i].mAABB.mExtents.z); \
491  }
492 
493 #define INIT_QUANTIZATION \
494  udword nbc=15; /* Keep one bit for sign */ \
495  udword nbe=15; /* Keep one bit for fix */ \
496  if(!gFixQuantized) nbe++; \
497  \
498  /* Compute quantization coeffs */ \
499  Point CQuantCoeff, EQuantCoeff; \
500  CQuantCoeff.x = CMax.x!=0.0f ? float((1<<nbc)-1)/CMax.x : 0.0f; \
501  CQuantCoeff.y = CMax.y!=0.0f ? float((1<<nbc)-1)/CMax.y : 0.0f; \
502  CQuantCoeff.z = CMax.z!=0.0f ? float((1<<nbc)-1)/CMax.z : 0.0f; \
503  EQuantCoeff.x = EMax.x!=0.0f ? float((1<<nbe)-1)/EMax.x : 0.0f; \
504  EQuantCoeff.y = EMax.y!=0.0f ? float((1<<nbe)-1)/EMax.y : 0.0f; \
505  EQuantCoeff.z = EMax.z!=0.0f ? float((1<<nbe)-1)/EMax.z : 0.0f; \
506  /* Compute and save dequantization coeffs */ \
507  mCenterCoeff.x = CQuantCoeff.x!=0.0f ? 1.0f / CQuantCoeff.x : 0.0f; \
508  mCenterCoeff.y = CQuantCoeff.y!=0.0f ? 1.0f / CQuantCoeff.y : 0.0f; \
509  mCenterCoeff.z = CQuantCoeff.z!=0.0f ? 1.0f / CQuantCoeff.z : 0.0f; \
510  mExtentsCoeff.x = EQuantCoeff.x!=0.0f ? 1.0f / EQuantCoeff.x : 0.0f; \
511  mExtentsCoeff.y = EQuantCoeff.y!=0.0f ? 1.0f / EQuantCoeff.y : 0.0f; \
512  mExtentsCoeff.z = EQuantCoeff.z!=0.0f ? 1.0f / EQuantCoeff.z : 0.0f; \
513 
514 #define PERFORM_QUANTIZATION \
515  /* Quantize */ \
516  mNodes[i].mAABB.mCenter[0] = sword(Nodes[i].mAABB.mCenter.x * CQuantCoeff.x); \
517  mNodes[i].mAABB.mCenter[1] = sword(Nodes[i].mAABB.mCenter.y * CQuantCoeff.y); \
518  mNodes[i].mAABB.mCenter[2] = sword(Nodes[i].mAABB.mCenter.z * CQuantCoeff.z); \
519  mNodes[i].mAABB.mExtents[0] = uword(Nodes[i].mAABB.mExtents.x * EQuantCoeff.x); \
520  mNodes[i].mAABB.mExtents[1] = uword(Nodes[i].mAABB.mExtents.y * EQuantCoeff.y); \
521  mNodes[i].mAABB.mExtents[2] = uword(Nodes[i].mAABB.mExtents.z * EQuantCoeff.z); \
522  /* Fix quantized boxes */ \
523  if(gFixQuantized) \
524  { \
525  /* Make sure the quantized box is still valid */ \
526  Point Max = Nodes[i].mAABB.mCenter + Nodes[i].mAABB.mExtents; \
527  Point Min = Nodes[i].mAABB.mCenter - Nodes[i].mAABB.mExtents; \
528  /* For each axis */ \
529  for(udword j=0;j<3;j++) \
530  { /* Dequantize the box center */ \
531  if (fabs (mExtentsCoeff[j]) < 0.00001) \
532  { \
533  mNodes[i].mAABB.mExtents[j]=0xffff; \
534  } \
535  else \
536  { \
537  float qc = float(mNodes[i].mAABB.mCenter[j]) * mCenterCoeff[j]; \
538  bool FixMe=true; \
539  do \
540  { /* Dequantize the box extent */ \
541  float qe = float(mNodes[i].mAABB.mExtents[j]) * mExtentsCoeff[j]; \
542  /* Compare real & dequantized values */ \
543  if(qc+qe<Max[j] || qc-qe>Min[j]) mNodes[i].mAABB.mExtents[j]++; \
544  else FixMe=false; \
545  /* Prevent wrapping */ \
546  if(!mNodes[i].mAABB.mExtents[j]) \
547  { \
548  mNodes[i].mAABB.mExtents[j]=0xffff; \
549  FixMe=false; \
550  } \
551  }while(FixMe); \
552  } \
553  } \
554  }
555 
556 #define REMAP_DATA(member) \
557  /* Fix data */ \
558  Data = Nodes[i].member; \
559  if(!(Data&1)) \
560  { \
561  /* Compute box number */ \
562  udword Nb = (Data - uintptr_t(Nodes))/Nodes[i].GetNodeSize(); \
563  Data = uintptr_t(&mNodes[Nb]); \
564  } \
565  /* ...remapped */ \
566  mNodes[i].member = Data;
567 
569 
572 AABBQuantizedTree::AABBQuantizedTree() : mNodes(null)
574 {
575 }
576 
578 
581 AABBQuantizedTree::~AABBQuantizedTree()
583 {
584  DELETEARRAY(mNodes);
585 }
586 
588 
595 {
596  // Checkings
597  if(!tree) return false;
598  // Check the input tree is complete
599  udword NbTriangles = tree->GetNbPrimitives();
600  udword NbNodes = tree->GetNbNodes();
601  if(NbNodes!=NbTriangles*2-1) return false;
602 
603  // Get nodes
604  mNbNodes = NbNodes;
605  DELETEARRAY(mNodes);
607  CHECKALLOC(Nodes);
608 
609  // Build the tree
610  udword CurID = 1;
611  _BuildCollisionTree(Nodes, 0, CurID, tree);
612 
613  // Quantize
614  {
615  mNodes = new AABBQuantizedNode[mNbNodes];
616  CHECKALLOC(mNodes);
617 
618  udword i;
619  // Get max values
621 
622  // Quantization
624 
625  // Quantize
626  uintptr_t Data;
627  for(i=0;i<mNbNodes;i++)
628  {
630  REMAP_DATA(mData)
631  }
632 
633  DELETEARRAY(Nodes);
634  }
635 
636  return true;
637 }
638 
640 
645 bool AABBQuantizedTree::Refit(const MeshInterface* /*mesh_interface*/)
647 {
648  OPASSERT(!"Not implemented since requantizing is painful !");
649  return false;
650 }
651 
653 
659 bool AABBQuantizedTree::Walk(GenericWalkingCallback callback, void* user_data) const
661 {
662  if(!callback) return false;
663 
664  struct Local
665  {
666  static void _Walk(const AABBQuantizedNode* current_node, GenericWalkingCallback callback, void* user_data)
667  {
668  if(!current_node || !(callback)(current_node, user_data)) return;
669 
670  if(!current_node->IsLeaf())
671  {
672  _Walk(current_node->GetPos(), callback, user_data);
673  _Walk(current_node->GetNeg(), callback, user_data);
674  }
675  }
676  };
677  Local::_Walk(mNodes, callback, user_data);
678  return true;
679 }
680 
681 
682 
684 
687 AABBQuantizedNoLeafTree::AABBQuantizedNoLeafTree() : mNodes(null)
689 {
690 }
691 
693 
696 AABBQuantizedNoLeafTree::~AABBQuantizedNoLeafTree()
698 {
699  DELETEARRAY(mNodes);
700 }
701 
703 
710 {
711  // Checkings
712  if(!tree) return false;
713  // Check the input tree is complete
714  udword NbTriangles = tree->GetNbPrimitives();
715  udword NbNodes = tree->GetNbNodes();
716  if(NbNodes!=NbTriangles*2-1) return false;
717 
718  // Get nodes
719  mNbNodes = NbTriangles-1;
720  DELETEARRAY(mNodes);
721  AABBNoLeafNode* Nodes = new AABBNoLeafNode[mNbNodes];
722  CHECKALLOC(Nodes);
723 
724  // Build the tree
725  udword CurID = 1;
726  _BuildNoLeafTree(Nodes, 0, CurID, tree);
727  OPASSERT(CurID==mNbNodes);
728 
729  // Quantize
730  {
731  mNodes = new AABBQuantizedNoLeafNode[mNbNodes];
732  CHECKALLOC(mNodes);
733 
734  udword i;
735  // Get max values
737 
738  // Quantization
740 
741  // Quantize
742  uintptr_t Data;
743  for(i=0;i<mNbNodes;i++)
744  {
746  REMAP_DATA(mPosData)
747  REMAP_DATA(mNegData)
748  }
749 
750  DELETEARRAY(Nodes);
751  }
752 
753  return true;
754 }
755 
757 
762 bool AABBQuantizedNoLeafTree::Refit(const MeshInterface* /*mesh_interface*/)
764 {
765  OPASSERT(!"Not implemented since requantizing is painful !");
766  return false;
767 }
768 
770 
776 bool AABBQuantizedNoLeafTree::Walk(GenericWalkingCallback callback, void* user_data) const
778 {
779  if(!callback) return false;
780 
781  struct Local
782  {
783  static void _Walk(const AABBQuantizedNoLeafNode* current_node, GenericWalkingCallback callback, void* user_data)
784  {
785  if(!current_node || !(callback)(current_node, user_data)) return;
786 
787  if(!current_node->HasPosLeaf()) _Walk(current_node->GetPos(), callback, user_data);
788  if(!current_node->HasNegLeaf()) _Walk(current_node->GetNeg(), callback, user_data);
789  }
790  };
791  Local::_Walk(mNodes, callback, user_data);
792  return true;
793 }
794