[216] | 1 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
| 2 | /* |
---|
| 3 | * OPCODE - Optimized Collision Detection |
---|
| 4 | * Copyright (C) 2001 Pierre Terdiman |
---|
| 5 | * Homepage: http://www.codercorner.com/Opcode.htm |
---|
| 6 | */ |
---|
| 7 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
| 8 | |
---|
| 9 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
| 10 | /** |
---|
| 11 | * Contains code for hybrid models. |
---|
| 12 | * \file OPC_HybridModel.cpp |
---|
| 13 | * \author Pierre Terdiman |
---|
| 14 | * \date May, 18, 2003 |
---|
| 15 | */ |
---|
| 16 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
| 17 | |
---|
| 18 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
| 19 | /** |
---|
| 20 | * An hybrid collision model. |
---|
| 21 | * |
---|
| 22 | * The problem : |
---|
| 23 | * |
---|
| 24 | * Opcode really shines for mesh-mesh collision, especially when meshes are deeply overlapping |
---|
| 25 | * (it typically outperforms RAPID in those cases). |
---|
| 26 | * |
---|
| 27 | * Unfortunately this is not the typical scenario in games. |
---|
| 28 | * |
---|
| 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. |
---|
| 31 | * |
---|
| 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". |
---|
| 38 | * |
---|
| 39 | * Hybrid trees : |
---|
| 40 | * |
---|
| 41 | * Hybrid trees try to combine best of both worlds : |
---|
| 42 | * |
---|
| 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. |
---|
| 45 | * |
---|
| 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. |
---|
| 50 | * |
---|
| 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. |
---|
| 54 | * |
---|
| 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) |
---|
| 57 | * |
---|
| 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. |
---|
| 60 | * |
---|
| 61 | * Extra advantages: |
---|
| 62 | * |
---|
| 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. |
---|
| 65 | * |
---|
| 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. |
---|
| 69 | * |
---|
| 70 | * Information to take home: |
---|
| 71 | * - they use less ram |
---|
| 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) |
---|
| 75 | * |
---|
| 76 | * \class HybridModel |
---|
| 77 | * \author Pierre Terdiman |
---|
| 78 | * \version 1.3 |
---|
| 79 | * \date May, 18, 2003 |
---|
| 80 | */ |
---|
| 81 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
| 82 | |
---|
| 83 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
| 84 | // Precompiled Header |
---|
| 85 | #include "Stdafx.h" |
---|
| 86 | |
---|
| 87 | using namespace Opcode; |
---|
| 88 | |
---|
| 89 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
| 90 | /** |
---|
| 91 | * Constructor. |
---|
| 92 | */ |
---|
| 93 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
| 94 | HybridModel::HybridModel() : |
---|
| 95 | mNbLeaves (0), |
---|
| 96 | mNbPrimitives (0), |
---|
| 97 | mTriangles (null), |
---|
| 98 | mIndices (null) |
---|
| 99 | { |
---|
| 100 | } |
---|
| 101 | |
---|
| 102 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
| 103 | /** |
---|
| 104 | * Destructor. |
---|
| 105 | */ |
---|
| 106 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
| 107 | HybridModel::~HybridModel() |
---|
| 108 | { |
---|
| 109 | Release(); |
---|
| 110 | } |
---|
| 111 | |
---|
| 112 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
| 113 | /** |
---|
| 114 | * Releases everything. |
---|
| 115 | */ |
---|
| 116 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
| 117 | void HybridModel::Release() |
---|
| 118 | { |
---|
| 119 | ReleaseBase(); |
---|
| 120 | DELETEARRAY(mIndices); |
---|
| 121 | DELETEARRAY(mTriangles); |
---|
| 122 | mNbLeaves = 0; |
---|
| 123 | mNbPrimitives = 0; |
---|
| 124 | } |
---|
| 125 | |
---|
| 126 | struct Internal |
---|
| 127 | { |
---|
| 128 | Internal() |
---|
| 129 | { |
---|
| 130 | mNbLeaves = 0; |
---|
| 131 | mLeaves = null; |
---|
| 132 | mTriangles = null; |
---|
| 133 | mBase = null; |
---|
| 134 | } |
---|
| 135 | ~Internal() |
---|
| 136 | { |
---|
| 137 | DELETEARRAY(mLeaves); |
---|
| 138 | } |
---|
| 139 | |
---|
| 140 | udword mNbLeaves; |
---|
| 141 | AABB* mLeaves; |
---|
| 142 | LeafTriangles* mTriangles; |
---|
| 143 | const udword* mBase; |
---|
| 144 | }; |
---|
| 145 | |
---|
| 146 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
| 147 | /** |
---|
| 148 | * Builds a collision model. |
---|
| 149 | * \param create [in] model creation structure |
---|
| 150 | * \return true if success |
---|
| 151 | */ |
---|
| 152 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
| 153 | bool HybridModel::Build(const OPCODECREATE& create) |
---|
| 154 | { |
---|
| 155 | // 1) Checkings |
---|
| 156 | if(!create.mIMesh || !create.mIMesh->IsValid()) return false; |
---|
| 157 | |
---|
| 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.... |
---|
| 162 | |
---|
| 163 | Release(); // Make sure previous tree has been discarded |
---|
| 164 | |
---|
| 165 | // 1-1) Setup mesh interface automatically |
---|
| 166 | SetMeshInterface(create.mIMesh); |
---|
| 167 | |
---|
| 168 | bool Status = false; |
---|
| 169 | AABBTree* LeafTree = null; |
---|
| 170 | Internal Data; |
---|
| 171 | |
---|
| 172 | // 2) Build a generic AABB Tree. |
---|
| 173 | mSource = new AABBTree; |
---|
| 174 | CHECKALLOC(mSource); |
---|
| 175 | |
---|
| 176 | // 2-1) Setup a builder. Our primitives here are triangles from input mesh, |
---|
| 177 | // so we use an AABBTreeOfTrianglesBuilder..... |
---|
| 178 | { |
---|
| 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; |
---|
| 185 | } |
---|
| 186 | |
---|
| 187 | // 2-2) Here's the trick : create *another* AABB tree using the leaves of the first one (which are boxes, this time) |
---|
| 188 | struct Local |
---|
| 189 | { |
---|
| 190 | // A callback to count leaf nodes |
---|
| 191 | static bool CountLeaves(const AABBTreeNode* current, udword depth, void* user_data) |
---|
| 192 | { |
---|
| 193 | if(current->IsLeaf()) |
---|
| 194 | { |
---|
| 195 | Internal* Data = (Internal*)user_data; |
---|
| 196 | Data->mNbLeaves++; |
---|
| 197 | } |
---|
| 198 | return true; |
---|
| 199 | } |
---|
| 200 | |
---|
| 201 | // A callback to setup leaf nodes in our internal structures |
---|
| 202 | static bool SetupLeafData(const AABBTreeNode* current, udword depth, void* user_data) |
---|
| 203 | { |
---|
| 204 | if(current->IsLeaf()) |
---|
| 205 | { |
---|
| 206 | Internal* Data = (Internal*)user_data; |
---|
| 207 | |
---|
| 208 | // Get current leaf's box |
---|
| 209 | Data->mLeaves[Data->mNbLeaves] = *current->GetAABB(); |
---|
| 210 | |
---|
| 211 | // Setup leaf data |
---|
| 212 | udword Index = udword((size_t(current->GetPrimitives()) - size_t(Data->mBase)) / sizeof(udword)); |
---|
| 213 | Data->mTriangles[Data->mNbLeaves].SetData(current->GetNbPrimitives(), Index); |
---|
| 214 | |
---|
| 215 | Data->mNbLeaves++; |
---|
| 216 | } |
---|
| 217 | return true; |
---|
| 218 | } |
---|
| 219 | }; |
---|
| 220 | |
---|
| 221 | // Walk the tree & count number of leaves |
---|
| 222 | Data.mNbLeaves = 0; |
---|
| 223 | mSource->Walk(Local::CountLeaves, &Data); |
---|
| 224 | mNbLeaves = Data.mNbLeaves; // Keep track of it |
---|
| 225 | |
---|
| 226 | // Special case for 1-leaf meshes |
---|
| 227 | if(mNbLeaves==1) |
---|
| 228 | { |
---|
| 229 | mModelCode |= OPC_SINGLE_NODE; |
---|
| 230 | Status = true; |
---|
| 231 | goto FreeAndExit; |
---|
| 232 | } |
---|
| 233 | |
---|
| 234 | // Allocate our structures |
---|
| 235 | Data.mLeaves = new AABB[Data.mNbLeaves]; CHECKALLOC(Data.mLeaves); |
---|
| 236 | mTriangles = new LeafTriangles[Data.mNbLeaves]; CHECKALLOC(mTriangles); |
---|
| 237 | |
---|
| 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); |
---|
| 243 | |
---|
| 244 | // Handle source indices |
---|
| 245 | { |
---|
| 246 | bool MustKeepIndices = true; |
---|
| 247 | if(create.mCanRemap) |
---|
| 248 | { |
---|
| 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())) |
---|
| 253 | { |
---|
| 254 | MustKeepIndices = false; |
---|
| 255 | } |
---|
| 256 | } |
---|
| 257 | |
---|
| 258 | if(MustKeepIndices) |
---|
| 259 | { |
---|
| 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)); |
---|
| 264 | } |
---|
| 265 | } |
---|
| 266 | |
---|
| 267 | // Now, create our optimized tree using previous leaf nodes |
---|
| 268 | LeafTree = new AABBTree; |
---|
| 269 | CHECKALLOC(LeafTree); |
---|
| 270 | { |
---|
| 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; |
---|
| 277 | } |
---|
| 278 | |
---|
| 279 | // 3) Create an optimized tree according to user-settings |
---|
| 280 | if(!CreateTree(create.mNoLeaf, create.mQuantized)) goto FreeAndExit; |
---|
| 281 | |
---|
| 282 | // 3-2) Create optimized tree |
---|
| 283 | if(!mTree->Build(LeafTree)) goto FreeAndExit; |
---|
| 284 | |
---|
| 285 | // Finally ok... |
---|
| 286 | Status = true; |
---|
| 287 | |
---|
| 288 | FreeAndExit: // Allow me this one... |
---|
| 289 | DELETESINGLE(LeafTree); |
---|
| 290 | |
---|
| 291 | // 3-3) Delete generic tree if needed |
---|
| 292 | if(!create.mKeepOriginal) DELETESINGLE(mSource); |
---|
| 293 | |
---|
| 294 | return Status; |
---|
| 295 | } |
---|
| 296 | |
---|
| 297 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
| 298 | /** |
---|
| 299 | * Gets the number of bytes used by the tree. |
---|
| 300 | * \return amount of bytes used |
---|
| 301 | */ |
---|
| 302 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
| 303 | udword HybridModel::GetUsedBytes() const |
---|
| 304 | { |
---|
| 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 |
---|
| 309 | return UsedBytes; |
---|
| 310 | } |
---|
| 311 | |
---|
| 312 | inline_ void ComputeMinMax(Point& min, Point& max, const VertexPointers& vp) |
---|
| 313 | { |
---|
| 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); |
---|
| 318 | |
---|
| 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); |
---|
| 321 | |
---|
| 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); |
---|
| 324 | #else |
---|
| 325 | min = *vp.Vertex[0]; |
---|
| 326 | max = *vp.Vertex[0]; |
---|
| 327 | min.Min(*vp.Vertex[1]); |
---|
| 328 | max.Max(*vp.Vertex[1]); |
---|
| 329 | min.Min(*vp.Vertex[2]); |
---|
| 330 | max.Max(*vp.Vertex[2]); |
---|
| 331 | #endif |
---|
| 332 | } |
---|
| 333 | |
---|
| 334 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
| 335 | /** |
---|
| 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 |
---|
| 340 | */ |
---|
| 341 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
---|
| 342 | bool HybridModel::Refit() |
---|
| 343 | { |
---|
| 344 | if(!mIMesh) return false; |
---|
| 345 | if(!mTree) return false; |
---|
| 346 | |
---|
| 347 | if(IsQuantized()) return false; |
---|
| 348 | if(HasLeafNodes()) return false; |
---|
| 349 | |
---|
| 350 | const LeafTriangles* LT = GetLeafTriangles(); |
---|
| 351 | const udword* Indices = GetIndices(); |
---|
| 352 | |
---|
| 353 | // Bottom-up update |
---|
| 354 | VertexPointers VP; |
---|
| 355 | Point Min,Max; |
---|
| 356 | Point Min_,Max_; |
---|
| 357 | udword Index = mTree->GetNbNodes(); |
---|
| 358 | AABBNoLeafNode* Nodes = (AABBNoLeafNode*)((AABBNoLeafTree*)mTree)->GetNodes(); |
---|
| 359 | while(Index--) |
---|
| 360 | { |
---|
| 361 | AABBNoLeafNode& Current = Nodes[Index]; |
---|
| 362 | |
---|
| 363 | if(Current.HasPosLeaf()) |
---|
| 364 | { |
---|
| 365 | const LeafTriangles& CurrentLeaf = LT[Current.GetPosPrimitive()]; |
---|
| 366 | |
---|
| 367 | Min.SetPlusInfinity(); |
---|
| 368 | Max.SetMinusInfinity(); |
---|
| 369 | |
---|
| 370 | Point TmpMin, TmpMax; |
---|
| 371 | |
---|
| 372 | // Each leaf box has a set of triangles |
---|
| 373 | udword NbTris = CurrentLeaf.GetNbTriangles(); |
---|
| 374 | if(Indices) |
---|
| 375 | { |
---|
| 376 | const udword* T = &Indices[CurrentLeaf.GetTriangleIndex()]; |
---|
| 377 | |
---|
| 378 | // Loop through triangles and test each of them |
---|
| 379 | while(NbTris--) |
---|
| 380 | { |
---|
| 381 | mIMesh->GetTriangle(VP, *T++); |
---|
| 382 | ComputeMinMax(TmpMin, TmpMax, VP); |
---|
| 383 | Min.Min(TmpMin); |
---|
| 384 | Max.Max(TmpMax); |
---|
| 385 | } |
---|
| 386 | } |
---|
| 387 | else |
---|
| 388 | { |
---|
| 389 | udword BaseIndex = CurrentLeaf.GetTriangleIndex(); |
---|
| 390 | |
---|
| 391 | // Loop through triangles and test each of them |
---|
| 392 | while(NbTris--) |
---|
| 393 | { |
---|
| 394 | mIMesh->GetTriangle(VP, BaseIndex++); |
---|
| 395 | ComputeMinMax(TmpMin, TmpMax, VP); |
---|
| 396 | Min.Min(TmpMin); |
---|
| 397 | Max.Max(TmpMax); |
---|
| 398 | } |
---|
| 399 | } |
---|
| 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 | const LeafTriangles& CurrentLeaf = LT[Current.GetNegPrimitive()]; |
---|
| 411 | |
---|
| 412 | Min_.SetPlusInfinity(); |
---|
| 413 | Max_.SetMinusInfinity(); |
---|
| 414 | |
---|
| 415 | Point TmpMin, TmpMax; |
---|
| 416 | |
---|
| 417 | // Each leaf box has a set of triangles |
---|
| 418 | udword NbTris = CurrentLeaf.GetNbTriangles(); |
---|
| 419 | if(Indices) |
---|
| 420 | { |
---|
| 421 | const udword* T = &Indices[CurrentLeaf.GetTriangleIndex()]; |
---|
| 422 | |
---|
| 423 | // Loop through triangles and test each of them |
---|
| 424 | while(NbTris--) |
---|
| 425 | { |
---|
| 426 | mIMesh->GetTriangle(VP, *T++); |
---|
| 427 | ComputeMinMax(TmpMin, TmpMax, VP); |
---|
| 428 | Min_.Min(TmpMin); |
---|
| 429 | Max_.Max(TmpMax); |
---|
| 430 | } |
---|
| 431 | } |
---|
| 432 | else |
---|
| 433 | { |
---|
| 434 | udword BaseIndex = CurrentLeaf.GetTriangleIndex(); |
---|
| 435 | |
---|
| 436 | // Loop through triangles and test each of them |
---|
| 437 | while(NbTris--) |
---|
| 438 | { |
---|
| 439 | mIMesh->GetTriangle(VP, BaseIndex++); |
---|
| 440 | ComputeMinMax(TmpMin, TmpMax, VP); |
---|
| 441 | Min_.Min(TmpMin); |
---|
| 442 | Max_.Max(TmpMax); |
---|
| 443 | } |
---|
| 444 | } |
---|
| 445 | } |
---|
| 446 | else |
---|
| 447 | { |
---|
| 448 | const CollisionAABB& CurrentBox = Current.GetNeg()->mAABB; |
---|
| 449 | CurrentBox.GetMin(Min_); |
---|
| 450 | CurrentBox.GetMax(Max_); |
---|
| 451 | } |
---|
| 452 | #ifdef OPC_USE_FCOMI |
---|
| 453 | Min.x = FCMin2(Min.x, Min_.x); |
---|
| 454 | Max.x = FCMax2(Max.x, Max_.x); |
---|
| 455 | Min.y = FCMin2(Min.y, Min_.y); |
---|
| 456 | Max.y = FCMax2(Max.y, Max_.y); |
---|
| 457 | Min.z = FCMin2(Min.z, Min_.z); |
---|
| 458 | Max.z = FCMax2(Max.z, Max_.z); |
---|
| 459 | #else |
---|
| 460 | Min.Min(Min_); |
---|
| 461 | Max.Max(Max_); |
---|
| 462 | #endif |
---|
| 463 | Current.mAABB.SetMinMax(Min, Max); |
---|
| 464 | } |
---|
| 465 | return true; |
---|
| 466 | } |
---|