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_HybridModel.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 
16 
19 
81 
84 // Precompiled Header
85 #include "Stdafx.h"
86 
87 
88 namespace Opcode
89 {
90 
92 
97  mNbLeaves (0),
98  mTriangles (null),
99  mNbPrimitives (0),
100  mIndices (null)
101 {
102 }
103 
105 
110 {
111  Release();
112 }
113 
115 
118 void HybridModel::Release()
120 {
121  ReleaseBase();
122  DELETEARRAY(mIndices);
123  DELETEARRAY(mTriangles);
124  mNbLeaves = 0;
125  mNbPrimitives = 0;
126 }
127 
128  struct Internal
129  {
131  {
132  mNbLeaves = 0;
133  mLeaves = null;
134  mTriangles = null;
135  mBase = null;
136  }
138  {
140  }
141 
145  const udword* mBase;
146  };
147 
149 
154 bool HybridModel::Build(const OPCODECREATE& create)
156 {
157  // 1) Checkings
158  if(!create.mIMesh || !create.mIMesh->IsValid()) return false;
159 
160  // Look for degenerate faces.
161  udword NbDegenerate = create.mIMesh->CheckTopology();
162 // if(NbDegenerate) Log("OPCODE WARNING: found %lu degenerate faces in model! Collision might report wrong results!\n", NbDegenerate);
163  // We continue nonetheless....
164 
165  Release(); // Make sure previous tree has been discarded
166 
167  // 1-1) Setup mesh interface automatically
168  SetMeshInterface(create.mIMesh);
169 
170  bool Status = false;
171  AABBTree* LeafTree = null;
172  Internal Data;
173 
174  // 2) Build a generic AABB Tree.
175  mSource = new AABBTree;
177 
178  // 2-1) Setup a builder. Our primitives here are triangles from input mesh,
179  // so we use an AABBTreeOfTrianglesBuilder.....
180  {
182  TB.mIMesh = create.mIMesh;
183  TB.mNbPrimitives = create.mIMesh->GetNbTriangles();
184  TB.mSettings = create.mSettings;
185  TB.mSettings.mLimit = 16; // ### Hardcoded, but maybe we could let the user choose 8 / 16 / 32 ...
186  if(!mSource->Build(&TB)) goto FreeAndExit;
187  }
188 
189  // 2-2) Here's the trick : create *another* AABB tree using the leaves of the first one (which are boxes, this time)
190  struct Local
191  {
192  // A callback to count leaf nodes
193  static bool CountLeaves(const AABBTreeNode* current, udword /*depth*/, void* user_data)
194  {
195  if(current->IsLeaf())
196  {
197  Internal* Data = (Internal*)user_data;
198  Data->mNbLeaves++;
199  }
200  return true;
201  }
202 
203  // A callback to setup leaf nodes in our internal structures
204  static bool SetupLeafData(const AABBTreeNode* current, udword /*depth*/, void* user_data)
205  {
206  if(current->IsLeaf())
207  {
208  Internal* Data = (Internal*)user_data;
209 
210  // Get current leaf's box
211  Data->mLeaves[Data->mNbLeaves] = *current->GetAABB();
212 
213  // Setup leaf data
214  udword Index = (uintptr_t(current->GetPrimitives()) - uintptr_t(Data->mBase))/sizeof(udword);
215  Data->mTriangles[Data->mNbLeaves].SetData(current->GetNbPrimitives(), Index);
216 
217  Data->mNbLeaves++;
218  }
219  return true;
220  }
221  };
222 
223  // Walk the tree & count number of leaves
224  Data.mNbLeaves = 0;
225  mSource->Walk(Local::CountLeaves, &Data);
226  mNbLeaves = Data.mNbLeaves; // Keep track of it
227 
228  // Special case for 1-leaf meshes
229  if(mNbLeaves==1)
230  {
232  Status = true;
233  goto FreeAndExit;
234  }
235 
236  // Allocate our structures
237  Data.mLeaves = new AABB[Data.mNbLeaves]; CHECKALLOC(Data.mLeaves);
238  mTriangles = new LeafTriangles[Data.mNbLeaves]; CHECKALLOC(mTriangles);
239 
240  // Walk the tree again & setup leaf data
241  Data.mTriangles = mTriangles;
242  Data.mBase = mSource->GetIndices();
243  Data.mNbLeaves = 0; // Reset for incoming walk
244  mSource->Walk(Local::SetupLeafData, &Data);
245 
246  // Handle source indices
247  {
248  bool MustKeepIndices = true;
249  if(create.mCanRemap)
250  {
251  // We try to get rid of source indices (saving more ram!) by reorganizing triangle arrays...
252  // Remap can fail when we use callbacks => keep track of indices in that case (it still
253  // works, only using more memory)
255  {
256  MustKeepIndices = false;
257  }
258  }
259 
260  if(MustKeepIndices)
261  {
262  // Keep track of source indices (from vanilla tree)
263  mNbPrimitives = mSource->GetNbPrimitives();
264  mIndices = new udword[mNbPrimitives];
265  CopyMemory(mIndices, mSource->GetIndices(), mNbPrimitives*sizeof(udword));
266  }
267  }
268 
269  // Now, create our optimized tree using previous leaf nodes
270  LeafTree = new AABBTree;
271  CHECKALLOC(LeafTree);
272  {
273  AABBTreeOfAABBsBuilder TB; // Now using boxes !
274  TB.mSettings = create.mSettings;
275  TB.mSettings.mLimit = 1; // We now want a complete tree so that we can "optimize" it
276  TB.mNbPrimitives = Data.mNbLeaves;
277  TB.mAABBArray = Data.mLeaves;
278  if(!LeafTree->Build(&TB)) goto FreeAndExit;
279  }
280 
281  // 3) Create an optimized tree according to user-settings
282  if(!CreateTree(create.mNoLeaf, create.mQuantized)) goto FreeAndExit;
283 
284  // 3-2) Create optimized tree
285  if(!mTree->Build(LeafTree)) goto FreeAndExit;
286 
287  // Finally ok...
288  Status = true;
289 
290 FreeAndExit: // Allow me this one...
291  DELETESINGLE(LeafTree);
292 
293  // 3-3) Delete generic tree if needed
294  if(!create.mKeepOriginal) DELETESINGLE(mSource);
295 
296  return Status;
297 }
298 
300 
306 {
307  udword UsedBytes = 0;
308  if(mTree) UsedBytes += mTree->GetUsedBytes();
309  if(mIndices) UsedBytes += mNbPrimitives * sizeof(udword); // mIndices
310  if(mTriangles) UsedBytes += mNbLeaves * sizeof(LeafTriangles); // mTriangles
311  return UsedBytes;
312 }
313 
315 {
316  // Compute triangle's AABB = a leaf box
317 #ifdef OPC_USE_FCOMI // a 15% speedup on my machine, not much
318  min.x = FCMin3(vp.Vertex[0]->x, vp.Vertex[1]->x, vp.Vertex[2]->x);
319  max.x = FCMax3(vp.Vertex[0]->x, vp.Vertex[1]->x, vp.Vertex[2]->x);
320 
321  min.y = FCMin3(vp.Vertex[0]->y, vp.Vertex[1]->y, vp.Vertex[2]->y);
322  max.y = FCMax3(vp.Vertex[0]->y, vp.Vertex[1]->y, vp.Vertex[2]->y);
323 
324  min.z = FCMin3(vp.Vertex[0]->z, vp.Vertex[1]->z, vp.Vertex[2]->z);
325  max.z = FCMax3(vp.Vertex[0]->z, vp.Vertex[1]->z, vp.Vertex[2]->z);
326 #else
327  min = *vp.Vertex[0];
328  max = *vp.Vertex[0];
329  min.Min(*vp.Vertex[1]);
330  max.Max(*vp.Vertex[1]);
331  min.Min(*vp.Vertex[2]);
332  max.Max(*vp.Vertex[2]);
333 #endif
334 }
335 
337 
343 bool HybridModel::Refit()
345 {
346  if(!mIMesh) return false;
347  if(!mTree) return false;
348 
349  if(IsQuantized()) return false;
350  if(HasLeafNodes()) return false;
351 
352  const LeafTriangles* LT = GetLeafTriangles();
353  const udword* Indices = GetIndices();
354 
355  // Bottom-up update
356  VertexPointers VP;
357  Point Min,Max;
358  Point Min_,Max_;
359  udword Index = mTree->GetNbNodes();
360  AABBNoLeafNode* Nodes = (AABBNoLeafNode*)((AABBNoLeafTree*)mTree)->GetNodes();
361  while(Index--)
362  {
363  AABBNoLeafNode& Current = Nodes[Index];
364 
365  if(Current.HasPosLeaf())
366  {
367  const LeafTriangles& CurrentLeaf = LT[Current.GetPosPrimitive()];
368 
369  Min.SetPlusInfinity();
370  Max.SetMinusInfinity();
371 
372  Point TmpMin, TmpMax;
373 
374  // Each leaf box has a set of triangles
375  udword NbTris = CurrentLeaf.GetNbTriangles();
376  if(Indices)
377  {
378  const udword* T = &Indices[CurrentLeaf.GetTriangleIndex()];
379 
380  // Loop through triangles and test each of them
381  while(NbTris--)
382  {
383  mIMesh->GetTriangle(VP, *T++);
384  OPComputeMinMax(TmpMin, TmpMax, VP);
385  Min.Min(TmpMin);
386  Max.Max(TmpMax);
387  }
388  }
389  else
390  {
391  udword BaseIndex = CurrentLeaf.GetTriangleIndex();
392 
393  // Loop through triangles and test each of them
394  while(NbTris--)
395  {
396  mIMesh->GetTriangle(VP, BaseIndex++);
397  OPComputeMinMax(TmpMin, TmpMax, VP);
398  Min.Min(TmpMin);
399  Max.Max(TmpMax);
400  }
401  }
402  }
403  else
404  {
405  const CollisionAABB& CurrentBox = Current.GetPos()->mAABB;
406  CurrentBox.GetMin(Min);
407  CurrentBox.GetMax(Max);
408  }
409 
410  if(Current.HasNegLeaf())
411  {
412  const LeafTriangles& CurrentLeaf = LT[Current.GetNegPrimitive()];
413 
414  Min_.SetPlusInfinity();
415  Max_.SetMinusInfinity();
416 
417  Point TmpMin, TmpMax;
418 
419  // Each leaf box has a set of triangles
420  udword NbTris = CurrentLeaf.GetNbTriangles();
421  if(Indices)
422  {
423  const udword* T = &Indices[CurrentLeaf.GetTriangleIndex()];
424 
425  // Loop through triangles and test each of them
426  while(NbTris--)
427  {
428  mIMesh->GetTriangle(VP, *T++);
429  OPComputeMinMax(TmpMin, TmpMax, VP);
430  Min_.Min(TmpMin);
431  Max_.Max(TmpMax);
432  }
433  }
434  else
435  {
436  udword BaseIndex = CurrentLeaf.GetTriangleIndex();
437 
438  // Loop through triangles and test each of them
439  while(NbTris--)
440  {
441  mIMesh->GetTriangle(VP, BaseIndex++);
442  OPComputeMinMax(TmpMin, TmpMax, VP);
443  Min_.Min(TmpMin);
444  Max_.Max(TmpMax);
445  }
446  }
447  }
448  else
449  {
450  const CollisionAABB& CurrentBox = Current.GetNeg()->mAABB;
451  CurrentBox.GetMin(Min_);
452  CurrentBox.GetMax(Max_);
453  }
454 #ifdef OPC_USE_FCOMI
455  Min.x = FCMin2(Min.x, Min_.x);
456  Max.x = FCMax2(Max.x, Max_.x);
457  Min.y = FCMin2(Min.y, Min_.y);
458  Max.y = FCMax2(Max.y, Max_.y);
459  Min.z = FCMin2(Min.z, Min_.z);
460  Max.z = FCMax2(Max.z, Max_.z);
461 #else
462  Min.Min(Min_);
463  Max.Max(Max_);
464 #endif
465  Current.mAABB.SetMinMax(Min, Max);
466  }
467  return true;
468 }
469 
470 }
471 // namespace Opcode
472