tsCollision.cpp
Engine/source/ts/tsCollision.cpp
Public Variables
bool
texGenAxis [18]
Detailed Description
Public Variables
bool gOpcodeInitialized
Point3F texGenAxis [18]
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#include "platform/platform.h" 25 26#include "ts/tsShapeInstance.h" 27#include "ts/tsMaterialList.h" 28#include "scene/sceneObject.h" 29#include "collision/convex.h" 30#include "collision/collision.h" 31#include "T3D/tsStatic.h" // TODO: We shouldn't have this dependancy! 32#include "T3D/physics/physicsPlugin.h" 33#include "T3D/physics/physicsCollision.h" 34#include "collision/concretePolyList.h" 35#include "collision/vertexPolyList.h" 36#include "platform/profiler.h" 37 38#include "opcode/Opcode.h" 39#include "opcode/Ice/IceAABB.h" 40#include "opcode/Ice/IcePoint.h" 41#include "opcode/OPC_AABBTree.h" 42#include "opcode/OPC_AABBCollider.h" 43 44static bool gOpcodeInitialized = false; 45 46//------------------------------------------------------------------------------------- 47// Collision methods 48//------------------------------------------------------------------------------------- 49 50bool TSShapeInstance::buildPolyList(AbstractPolyList * polyList, S32 dl) 51{ 52 // if dl==-1, nothing to do 53 if (dl==-1) 54 return false; 55 56 AssertFatal(dl>=0 && dl<mShape->details.size(),"TSShapeInstance::buildPolyList"); 57 58 // get subshape and object detail 59 const TSDetail * detail = &mShape->details[dl]; 60 S32 ss = detail->subShapeNum; 61 S32 od = detail->objectDetailNum; 62 63 // This detail does not have any geometry. 64 if ( ss < 0 ) 65 return false; 66 67 // nothing emitted yet... 68 bool emitted = false; 69 U32 surfaceKey = 0; 70 71 S32 start = mShape->subShapeFirstObject[ss]; 72 S32 end = mShape->subShapeNumObjects[ss] + start; 73 if (start<end) 74 { 75 MatrixF initialMat; 76 Point3F initialScale; 77 polyList->getTransform(&initialMat,&initialScale); 78 79 // set up for first object's node 80 MatrixF mat; 81 MatrixF scaleMat(true); 82 F32* p = scaleMat; 83 p[0] = initialScale.x; 84 p[5] = initialScale.y; 85 p[10] = initialScale.z; 86 const MatrixF *previousMat = &mMeshObjects[start].getTransform(); 87 mat.mul(initialMat,scaleMat); 88 mat.mul(*previousMat); 89 polyList->setTransform(&mat,Point3F(1, 1, 1)); 90 91 // run through objects and collide 92 for (S32 i=start; i<end; i++) 93 { 94 MeshObjectInstance * mesh = &mMeshObjects[i]; 95 96 if (od >= mesh->object->numMeshes) 97 continue; 98 99 if (&mesh->getTransform() != previousMat) 100 { 101 // different node from before, set up for this node 102 previousMat = &mesh->getTransform(); 103 104 if (previousMat != NULL) 105 { 106 mat.mul(initialMat,scaleMat); 107 mat.mul(*previousMat); 108 polyList->setTransform(&mat,Point3F(1, 1, 1)); 109 } 110 } 111 // collide... 112 emitted |= mesh->buildPolyList(od,polyList,surfaceKey,mMaterialList); 113 } 114 115 // restore original transform... 116 polyList->setTransform(&initialMat,initialScale); 117 } 118 119 return emitted; 120} 121 122bool TSShapeInstance::getFeatures(const MatrixF& mat, const Point3F& n, ConvexFeature* cf, S32 dl) 123{ 124 // if dl==-1, nothing to do 125 if (dl==-1) 126 return false; 127 128 AssertFatal(dl>=0 && dl<mShape->details.size(),"TSShapeInstance::buildPolyList"); 129 130 // get subshape and object detail 131 const TSDetail * detail = &mShape->details[dl]; 132 S32 ss = detail->subShapeNum; 133 S32 od = detail->objectDetailNum; 134 135 // nothing emitted yet... 136 bool emitted = false; 137 U32 surfaceKey = 0; 138 139 S32 start = mShape->subShapeFirstObject[ss]; 140 S32 end = mShape->subShapeNumObjects[ss] + start; 141 if (start<end) 142 { 143 MatrixF final; 144 const MatrixF* previousMat = &mMeshObjects[start].getTransform(); 145 final.mul(mat, *previousMat); 146 147 // run through objects and collide 148 for (S32 i=start; i<end; i++) 149 { 150 MeshObjectInstance * mesh = &mMeshObjects[i]; 151 152 if (od >= mesh->object->numMeshes) 153 continue; 154 155 if (&mesh->getTransform() != previousMat) 156 { 157 previousMat = &mesh->getTransform(); 158 final.mul(mat, *previousMat); 159 } 160 emitted |= mesh->getFeatures(od, final, n, cf, surfaceKey); 161 } 162 } 163 return emitted; 164} 165 166bool TSShapeInstance::castRay(const Point3F & a, const Point3F & b, RayInfo * rayInfo, S32 dl) 167{ 168 // if dl==-1, nothing to do 169 if (dl==-1) 170 return false; 171 172 AssertFatal(dl>=0 && dl<mShape->details.size(),"TSShapeInstance::castRay"); 173 174 // get subshape and object detail 175 const TSDetail * detail = &mShape->details[dl]; 176 S32 ss = detail->subShapeNum; 177 S32 od = detail->objectDetailNum; 178 179 // This detail has no geometry to hit. 180 if ( ss < 0 ) 181 return false; 182 183 S32 start = mShape->subShapeFirstObject[ss]; 184 S32 end = mShape->subShapeNumObjects[ss] + start; 185 RayInfo saveRay; 186 saveRay.t = 1.0f; 187 const MatrixF * saveMat = NULL; 188 bool found = false; 189 if (start<end) 190 { 191 Point3F ta, tb; 192 193 // set up for first object's node 194 MatrixF mat; 195 const MatrixF * previousMat = &mMeshObjects[start].getTransform(); 196 mat = *previousMat; 197 mat.inverse(); 198 mat.mulP(a,&ta); 199 mat.mulP(b,&tb); 200 201 // run through objects and collide 202 for (S32 i=start; i<end; i++) 203 { 204 MeshObjectInstance * mesh = &mMeshObjects[i]; 205 206 if (od >= mesh->object->numMeshes) 207 continue; 208 209 if (&mesh->getTransform() != previousMat) 210 { 211 // different node from before, set up for this node 212 previousMat = &mesh->getTransform(); 213 214 if (previousMat != NULL) 215 { 216 mat = *previousMat; 217 mat.inverse(); 218 mat.mulP(a,&ta); 219 mat.mulP(b,&tb); 220 } 221 } 222 // collide... 223 if (mesh->castRay(od,ta,tb,rayInfo, mMaterialList)) 224 { 225 if (!rayInfo) 226 return true; 227 228 if (rayInfo->t <= saveRay.t) 229 { 230 saveRay = *rayInfo; 231 saveMat = previousMat; 232 } 233 found = true; 234 } 235 } 236 } 237 238 // collide with any skins for this detail level... 239 // TODO: if ever... 240 241 // finalize the deal... 242 if (found) 243 { 244 *rayInfo = saveRay; 245 saveMat->mulV(rayInfo->normal); 246 rayInfo->point = b-a; 247 rayInfo->point *= rayInfo->t; 248 rayInfo->point += a; 249 } 250 return found; 251} 252 253bool TSShapeInstance::castRayRendered(const Point3F & a, const Point3F & b, RayInfo * rayInfo, S32 dl) 254{ 255 // if dl==-1, nothing to do 256 if (dl==-1) 257 return false; 258 259 AssertFatal(dl>=0 && dl<mShape->details.size(),"TSShapeInstance::castRayRendered"); 260 261 // get subshape and object detail 262 const TSDetail * detail = &mShape->details[dl]; 263 S32 ss = detail->subShapeNum; 264 S32 od = detail->objectDetailNum; 265 266 if ( ss == -1 ) 267 return false; 268 269 S32 start = mShape->subShapeFirstObject[ss]; 270 S32 end = mShape->subShapeNumObjects[ss] + start; 271 RayInfo saveRay; 272 saveRay.t = 1.0f; 273 const MatrixF * saveMat = NULL; 274 bool found = false; 275 if (start<end) 276 { 277 Point3F ta, tb; 278 279 // set up for first object's node 280 MatrixF mat; 281 const MatrixF * previousMat = &mMeshObjects[start].getTransform(); 282 mat = *previousMat; 283 mat.inverse(); 284 mat.mulP(a,&ta); 285 mat.mulP(b,&tb); 286 287 // run through objects and collide 288 for (S32 i=start; i<end; i++) 289 { 290 MeshObjectInstance * mesh = &mMeshObjects[i]; 291 292 if (od >= mesh->object->numMeshes) 293 continue; 294 295 if (&mesh->getTransform() != previousMat) 296 { 297 // different node from before, set up for this node 298 previousMat = &mesh->getTransform(); 299 300 if (previousMat != NULL) 301 { 302 mat = *previousMat; 303 mat.inverse(); 304 mat.mulP(a,&ta); 305 mat.mulP(b,&tb); 306 } 307 } 308 // collide... 309 if (mesh->castRayRendered(od,ta,tb,rayInfo, mMaterialList)) 310 { 311 if (!rayInfo) 312 return true; 313 314 if (rayInfo->t <= saveRay.t) 315 { 316 saveRay = *rayInfo; 317 saveMat = previousMat; 318 } 319 found = true; 320 } 321 } 322 } 323 324 // collide with any skins for this detail level... 325 // TODO: if ever... 326 327 // finalize the deal... 328 if (found) 329 { 330 *rayInfo = saveRay; 331 saveMat->mulV(rayInfo->normal); 332 rayInfo->point = b-a; 333 rayInfo->point *= rayInfo->t; 334 rayInfo->point += a; 335 } 336 return found; 337} 338 339Point3F TSShapeInstance::support(const Point3F & v, S32 dl) 340{ 341 // if dl==-1, nothing to do 342 AssertFatal(dl != -1, "Error, should never try to collide with a non-existant detail level!"); 343 AssertFatal(dl>=0 && dl<mShape->details.size(),"TSShapeInstance::support"); 344 345 // get subshape and object detail 346 const TSDetail * detail = &mShape->details[dl]; 347 S32 ss = detail->subShapeNum; 348 S32 od = detail->objectDetailNum; 349 350 S32 start = mShape->subShapeFirstObject[ss]; 351 S32 end = mShape->subShapeNumObjects[ss] + start; 352 353 F32 currMaxDP = -1e9f; 354 Point3F currSupport = Point3F(0, 0, 0); 355 const MatrixF * previousMat = NULL; 356 MatrixF mat; 357 if (start<end) 358 { 359 Point3F va; 360 361 // set up for first object's node 362 previousMat = &mMeshObjects[start].getTransform(); 363 mat = *previousMat; 364 mat.inverse(); 365 366 // run through objects and collide 367 for (S32 i=start; i<end; i++) 368 { 369 MeshObjectInstance * mesh = &mMeshObjects[i]; 370 371 if (od >= mesh->object->numMeshes) 372 continue; 373 374 TSMesh* physMesh = mesh->getMesh(od); 375 if (physMesh && !mesh->forceHidden && mesh->visible > 0.01f) 376 { 377 // collide... 378 if (&mesh->getTransform() != previousMat) 379 { 380 // different node from before, set up for this node 381 previousMat = &mesh->getTransform(); 382 mat = *previousMat; 383 mat.inverse(); 384 } 385 mat.mulV(v, &va); 386 physMesh->support(mesh->frame, va, &currMaxDP, &currSupport); 387 } 388 } 389 } 390 391 if (currMaxDP != -1e9f) 392 { 393 previousMat->mulP(currSupport); 394 return currSupport; 395 } 396 else 397 { 398 return Point3F(0, 0, 0); 399 } 400} 401 402void TSShapeInstance::computeBounds(S32 dl, Box3F & bounds) 403{ 404 // if dl==-1, nothing to do 405 if (dl==-1) 406 return; 407 408 AssertFatal(dl>=0 && dl<mShape->details.size(),"TSShapeInstance::computeBounds"); 409 410 // get subshape and object detail 411 const TSDetail * detail = &mShape->details[dl]; 412 S32 ss = detail->subShapeNum; 413 S32 od = detail->objectDetailNum; 414 415 // use shape bounds for imposter details 416 if (ss < 0) 417 { 418 bounds = mShape->mBounds; 419 return; 420 } 421 422 S32 start = mShape->subShapeFirstObject[ss]; 423 S32 end = mShape->subShapeNumObjects[ss] + start; 424 425 // run through objects and updating bounds as we go 426 bounds.minExtents.set( 10E30f, 10E30f, 10E30f); 427 bounds.maxExtents.set(-10E30f,-10E30f,-10E30f); 428 Box3F box; 429 for (S32 i=start; i<end; i++) 430 { 431 MeshObjectInstance * mesh = &mMeshObjects[i]; 432 433 if (od >= mesh->object->numMeshes) 434 continue; 435 436 if (mesh->getMesh(od)) 437 { 438 mesh->getMesh(od)->computeBounds(mesh->getTransform(),box, 0); // use frame 0 so TSSkinMesh uses skinned verts to compute bounds 439 bounds.minExtents.setMin(box.minExtents); 440 bounds.maxExtents.setMax(box.maxExtents); 441 } 442 } 443} 444 445//------------------------------------------------------------------------------------- 446// Object (MeshObjectInstance & PluginObjectInstance) collision methods 447//------------------------------------------------------------------------------------- 448 449bool TSShapeInstance::ObjectInstance::buildPolyList(S32 objectDetail, AbstractPolyList *polyList, U32 &surfaceKey, TSMaterialList *materials ) 450{ 451 TORQUE_UNUSED( objectDetail ); 452 TORQUE_UNUSED( polyList ); 453 TORQUE_UNUSED( surfaceKey ); 454 TORQUE_UNUSED( materials ); 455 456 AssertFatal(0,"TSShapeInstance::ObjectInstance::buildPolyList: no default method."); 457 return false; 458} 459 460bool TSShapeInstance::ObjectInstance::getFeatures(S32 objectDetail, const MatrixF& mat, const Point3F& n, ConvexFeature* cf, U32& surfaceKey) 461{ 462 TORQUE_UNUSED( objectDetail ); 463 TORQUE_UNUSED( mat ); 464 TORQUE_UNUSED( n ); 465 TORQUE_UNUSED( cf ); 466 TORQUE_UNUSED( surfaceKey ); 467 468 AssertFatal(0,"TSShapeInstance::ObjectInstance::buildPolyList: no default method."); 469 return false; 470} 471 472void TSShapeInstance::ObjectInstance::support(S32, const Point3F&, F32*, Point3F*) 473{ 474 AssertFatal(0,"TSShapeInstance::ObjectInstance::supprt: no default method."); 475} 476 477bool TSShapeInstance::ObjectInstance::castRay( S32 objectDetail, const Point3F &start, const Point3F &end, RayInfo *rayInfo, TSMaterialList *materials ) 478{ 479 TORQUE_UNUSED( objectDetail ); 480 TORQUE_UNUSED( start ); 481 TORQUE_UNUSED( end ); 482 TORQUE_UNUSED( rayInfo ); 483 484 AssertFatal(0,"TSShapeInstance::ObjectInstance::castRay: no default method."); 485 return false; 486} 487 488bool TSShapeInstance::MeshObjectInstance::buildPolyList( S32 objectDetail, AbstractPolyList *polyList, U32 &surfaceKey, TSMaterialList *materials ) 489{ 490 TSMesh * mesh = getMesh(objectDetail); 491 if (mesh && !forceHidden && visible>0.01f) 492 return mesh->buildPolyList(frame,polyList,surfaceKey,materials); 493 return false; 494} 495 496bool TSShapeInstance::MeshObjectInstance::getFeatures(S32 objectDetail, const MatrixF& mat, const Point3F& n, ConvexFeature* cf, U32& surfaceKey) 497{ 498 TSMesh* mesh = getMesh(objectDetail); 499 if (mesh && !forceHidden && visible > 0.01f) 500 return mesh->getFeatures(frame, mat, n, cf, surfaceKey); 501 return false; 502} 503 504void TSShapeInstance::MeshObjectInstance::support(S32 objectDetail, const Point3F& v, F32* currMaxDP, Point3F* currSupport) 505{ 506 TSMesh* mesh = getMesh(objectDetail); 507 if (mesh && !forceHidden && visible > 0.01f) 508 mesh->support(frame, v, currMaxDP, currSupport); 509} 510 511 512bool TSShapeInstance::MeshObjectInstance::castRay( S32 objectDetail, const Point3F & start, const Point3F & end, RayInfo * rayInfo, TSMaterialList* materials ) 513{ 514 TSMesh* mesh = getMesh( objectDetail ); 515 if( mesh && !forceHidden && visible > 0.01f ) 516 return mesh->castRay( frame, start, end, rayInfo, materials ); 517 return false; 518} 519 520bool TSShapeInstance::MeshObjectInstance::castRayRendered( S32 objectDetail, const Point3F & start, const Point3F & end, RayInfo * rayInfo, TSMaterialList* materials ) 521{ 522 TSMesh* mesh = getMesh( objectDetail ); 523 if( mesh && !forceHidden && visible > 0.01f ) 524 return mesh->castRayRendered( frame, start, end, rayInfo, materials ); 525 return false; 526} 527 528bool TSShapeInstance::ObjectInstance::castRayOpcode( S32 objectDetail, const Point3F & start, const Point3F & end, RayInfo *rayInfo, TSMaterialList* materials ) 529{ 530 TORQUE_UNUSED( objectDetail ); 531 TORQUE_UNUSED( start ); 532 TORQUE_UNUSED( end ); 533 TORQUE_UNUSED( rayInfo ); 534 TORQUE_UNUSED( materials ); 535 536 return false; 537} 538 539bool TSShapeInstance::ObjectInstance::buildPolyListOpcode( S32 objectDetail, AbstractPolyList *polyList, U32 &surfaceKey, TSMaterialList *materials ) 540{ 541 TORQUE_UNUSED( objectDetail ); 542 TORQUE_UNUSED( polyList ); 543 TORQUE_UNUSED( surfaceKey ); 544 TORQUE_UNUSED( materials ); 545 546 return false; 547} 548 549bool TSShapeInstance::ObjectInstance::buildConvexOpcode( const MatrixF &mat, S32 objectDetail, const Box3F &bounds, Convex *c, Convex *list ) 550{ 551 TORQUE_UNUSED( mat ); 552 TORQUE_UNUSED( objectDetail ); 553 TORQUE_UNUSED( bounds ); 554 TORQUE_UNUSED( c ); 555 TORQUE_UNUSED( list ); 556 557 return false; 558} 559 560bool TSShapeInstance::MeshObjectInstance::castRayOpcode( S32 objectDetail, const Point3F & start, const Point3F & end, RayInfo *info, TSMaterialList* materials ) 561{ 562 TSMesh * mesh = getMesh(objectDetail); 563 if (mesh && !forceHidden && visible>0.01f) 564 return mesh->castRayOpcode(start, end, info, materials); 565 return false; 566} 567 568bool TSShapeInstance::MeshObjectInstance::buildPolyListOpcode( S32 objectDetail, AbstractPolyList *polyList, const Box3F &box, TSMaterialList *materials ) 569{ 570 TSMesh * mesh = getMesh(objectDetail); 571 if ( mesh && !forceHidden && visible > 0.01f && box.isOverlapped( mesh->getBounds() ) ) 572 return mesh->buildPolyListOpcode(frame,polyList,box,materials); 573 return false; 574} 575 576bool TSShapeInstance::MeshObjectInstance::buildConvexOpcode( const MatrixF &mat, S32 objectDetail, const Box3F &bounds, Convex *c, Convex *list) 577{ 578 TSMesh * mesh = getMesh(objectDetail); 579 if ( mesh && !forceHidden && visible > 0.01f && bounds.isOverlapped( mesh->getBounds() ) ) 580 return mesh->buildConvexOpcode(mat, bounds, c, list); 581 return false; 582} 583 584bool TSShapeInstance::buildPolyListOpcode( S32 dl, AbstractPolyList *polyList, const Box3F &box ) 585{ 586 PROFILE_SCOPE( TSShapeInstance_buildPolyListOpcode_MeshObjInst ); 587 588 if (dl==-1) 589 return false; 590 591 AssertFatal(dl>=0 && dl<mShape->details.size(),"TSShapeInstance::buildPolyListOpcode"); 592 593 // get subshape and object detail 594 const TSDetail * detail = &mShape->details[dl]; 595 S32 ss = detail->subShapeNum; 596 if ( ss < 0 ) 597 return false; 598 599 S32 od = detail->objectDetailNum; 600 601 // nothing emitted yet... 602 bool emitted = false; 603 604 S32 start = mShape->subShapeFirstObject[ss]; 605 S32 end = mShape->subShapeNumObjects[ss] + start; 606 if (start<end) 607 { 608 MatrixF initialMat; 609 Point3F initialScale; 610 polyList->getTransform(&initialMat,&initialScale); 611 612 // set up for first object's node 613 MatrixF mat; 614 MatrixF scaleMat(true); 615 F32* p = scaleMat; 616 p[0] = initialScale.x; 617 p[5] = initialScale.y; 618 p[10] = initialScale.z; 619 const MatrixF * previousMat = &mMeshObjects[start].getTransform(); 620 mat.mul(initialMat,scaleMat); 621 mat.mul(*previousMat); 622 polyList->setTransform(&mat,Point3F(1, 1, 1)); 623 624 // Update our bounding box... 625 Box3F localBox = box; 626 MatrixF otherMat = mat; 627 otherMat.inverse(); 628 otherMat.mul(localBox); 629 630 // run through objects and collide 631 for (S32 i=start; i<end; i++) 632 { 633 MeshObjectInstance * mesh = &mMeshObjects[i]; 634 635 if (od >= mesh->object->numMeshes) 636 continue; 637 638 if (&mesh->getTransform() != previousMat) 639 { 640 // different node from before, set up for this node 641 previousMat = &mesh->getTransform(); 642 643 if (previousMat != NULL) 644 { 645 mat.mul(initialMat,scaleMat); 646 mat.mul(*previousMat); 647 polyList->setTransform(&mat,Point3F(1, 1, 1)); 648 649 // Update our bounding box... 650 otherMat = mat; 651 otherMat.inverse(); 652 localBox = box; 653 otherMat.mul(localBox); 654 } 655 } 656 657 // collide... 658 emitted |= mesh->buildPolyListOpcode(od,polyList,localBox,mMaterialList); 659 } 660 661 // restore original transform... 662 polyList->setTransform(&initialMat,initialScale); 663 } 664 665 return emitted; 666} 667 668bool TSShapeInstance::castRayOpcode( S32 dl, const Point3F & startPos, const Point3F & endPos, RayInfo *info) 669{ 670 // if dl==-1, nothing to do 671 if (dl==-1) 672 return false; 673 674 AssertFatal(dl>=0 && dl<mShape->details.size(),"TSShapeInstance::castRayOpcode"); 675 676 info->t = 100.f; 677 678 // get subshape and object detail 679 const TSDetail * detail = &mShape->details[dl]; 680 S32 ss = detail->subShapeNum; 681 if ( ss < 0 ) 682 return false; 683 684 S32 od = detail->objectDetailNum; 685 686 // nothing emitted yet... 687 bool emitted = false; 688 689 const MatrixF* saveMat = NULL; 690 S32 start = mShape->subShapeFirstObject[ss]; 691 S32 end = mShape->subShapeNumObjects[ss] + start; 692 if (start<end) 693 { 694 MatrixF mat; 695 const MatrixF * previousMat = &mMeshObjects[start].getTransform(); 696 mat = *previousMat; 697 mat.inverse(); 698 Point3F localStart, localEnd; 699 mat.mulP(startPos, &localStart); 700 mat.mulP(endPos, &localEnd); 701 702 // run through objects and collide 703 for (S32 i=start; i<end; i++) 704 { 705 MeshObjectInstance * mesh = &mMeshObjects[i]; 706 707 if (od >= mesh->object->numMeshes) 708 continue; 709 710 if (&mesh->getTransform() != previousMat) 711 { 712 // different node from before, set up for this node 713 previousMat = &mesh->getTransform(); 714 715 if (previousMat != NULL) 716 { 717 mat = *previousMat; 718 mat.inverse(); 719 mat.mulP(startPos, &localStart); 720 mat.mulP(endPos, &localEnd); 721 } 722 } 723 724 // collide... 725 if ( mesh->castRayOpcode(od,localStart, localEnd, info, mMaterialList) ) 726 { 727 saveMat = previousMat; 728 emitted = true; 729 } 730 } 731 } 732 733 if ( emitted ) 734 { 735 saveMat->mulV(info->normal); 736 info->point = endPos - startPos; 737 info->point *= info->t; 738 info->point += startPos; 739 } 740 741 return emitted; 742} 743 744bool TSShapeInstance::buildConvexOpcode( const MatrixF &objMat, const Point3F &objScale, S32 dl, const Box3F &bounds, Convex *c, Convex *list ) 745{ 746 AssertFatal(dl>=0 && dl<mShape->details.size(),"TSShapeInstance::buildConvexOpcode"); 747 748 // get subshape and object detail 749 const TSDetail * detail = &mShape->details[dl]; 750 S32 ss = detail->subShapeNum; 751 S32 od = detail->objectDetailNum; 752 753 // nothing emitted yet... 754 bool emitted = false; 755 756 S32 start = mShape->subShapeFirstObject[ss]; 757 S32 end = mShape->subShapeNumObjects[ss] + start; 758 if (start<end) 759 { 760 MatrixF initialMat = objMat; 761 Point3F initialScale = objScale; 762 763 // set up for first object's node 764 MatrixF mat; 765 MatrixF scaleMat(true); 766 F32* p = scaleMat; 767 p[0] = initialScale.x; 768 p[5] = initialScale.y; 769 p[10] = initialScale.z; 770 const MatrixF * previousMat = &mMeshObjects[start].getTransform(); 771 mat.mul(initialMat,scaleMat); 772 mat.mul(*previousMat); 773 774 // Update our bounding box... 775 Box3F localBox = bounds; 776 MatrixF otherMat = mat; 777 otherMat.inverse(); 778 otherMat.mul(localBox); 779 780 // run through objects and collide 781 for (S32 i=start; i<end; i++) 782 { 783 MeshObjectInstance * mesh = &mMeshObjects[i]; 784 785 if (od >= mesh->object->numMeshes) 786 continue; 787 788 if (&mesh->getTransform() != previousMat) 789 { 790 // different node from before, set up for this node 791 previousMat = &mesh->getTransform(); 792 793 if (previousMat != NULL) 794 { 795 mat.mul(initialMat,scaleMat); 796 mat.mul(*previousMat); 797 798 // Update our bounding box... 799 otherMat = mat; 800 otherMat.inverse(); 801 localBox = bounds; 802 otherMat.mul(localBox); 803 } 804 } 805 806 // collide... note we pass the original mech transform 807 // here so that the convex data returned is in mesh space. 808 emitted |= mesh->buildConvexOpcode(*previousMat,od,localBox,c, list); 809 } 810 } 811 812 return emitted; 813} 814 815void TSShape::findColDetails( bool useVisibleMesh, Vector<S32> *outDetails, Vector<S32> *outLOSDetails ) const 816{ 817 PROFILE_SCOPE( TSShape_findColDetails ); 818 819 if ( useVisibleMesh ) 820 { 821 // If we're using the visible mesh for collision then 822 // find the highest detail and use that. 823 824 U32 highestDetail = -1; 825 F32 highestSize = -F32_MAX; 826 827 for ( U32 i = 0; i < details.size(); i++ ) 828 { 829 // Make sure we skip any details that shouldn't be rendered 830 if ( details[i].size < 0 ) 831 continue; 832 833 /* 834 // Also make sure we skip any collision details with a size. 835 const String &name = names[details[i].nameIndex]; 836 if ( dStrStartsWith( name, "Collision" ) || 837 dStrStartsWith( name, "LOS" ) ) 838 continue; 839 */ 840 841 // Otherwise test against the current highest size 842 if ( details[i].size > highestSize ) 843 { 844 highestDetail = i; 845 highestSize = details[i].size; 846 } 847 } 848 849 // We use the same detail for both raycast and collisions. 850 if ( highestDetail != -1 ) 851 { 852 outDetails->push_back( highestDetail ); 853 if ( outLOSDetails ) 854 outLOSDetails->push_back( highestDetail ); 855 } 856 857 return; 858 } 859 860 // Detail meshes starting with COL or LOS is considered 861 // to be a collision mesh. 862 // 863 // The LOS (light of sight) details are used for raycasts. 864 865 for ( U32 i = 0; i < details.size(); i++ ) 866 { 867 const String &name = names[ details[i].nameIndex ]; 868 if ( !dStrStartsWith( name, "Collision" ) ) 869 continue; 870 871 outDetails->push_back(i); 872 873 // If we're not returning LOS details then skip out. 874 if ( !outLOSDetails ) 875 continue; 876 877 // The way LOS works is that it will check to see if there is a LOS detail that matches 878 // the the collision detail + 1 + MaxCollisionShapes (this variable name should change in 879 // the future). If it can't find a matching LOS it will simply use the collision instead. 880 // We check for any "unmatched" LOS's further down. 881 882 // Extract the detail number from the name. 883 S32 number = 0; 884 String::GetTrailingNumber( name, number ); 885 886 // Look for a matching LOS collision detail. 887 // 888 // TODO: Fix the old 9 detail offset which is there 889 // because you cannot have two detail markers with 890 // the same detail number. 891 // 892 const S32 LOSOverrideOffset = 9; 893 String buff = String::ToString( "LOS-%d", mAbs( number ) + LOSOverrideOffset ); 894 S32 los = findDetail( buff ); 895 896 // If we didn't find the lod detail then use the 897 // normal collision detail for LOS tests. 898 if ( los == -1 ) 899 los = i; 900 901 outLOSDetails->push_back( los ); 902 } 903 904 // If we're not returning LOS details then skip out. 905 if ( !outLOSDetails ) 906 return; 907 908 // Snag any "unmatched" LOS details and put 909 // them at the end of the list. 910 for ( U32 i = 0; i < details.size(); i++ ) 911 { 912 const String &name = names[ details[i].nameIndex ]; 913 if ( !dStrStartsWith( name, "LOS" ) ) 914 continue; 915 916 // See if we already have this LOS 917 bool found = false; 918 for (U32 j = 0; j < outLOSDetails->size(); j++) 919 { 920 if ( (*outLOSDetails)[j] == i ) 921 { 922 found = true; 923 break; 924 } 925 } 926 927 if ( !found ) 928 outLOSDetails->push_back(i); 929 } 930} 931 932PhysicsCollision* TSShape::buildColShape( bool useVisibleMesh, const Point3F &scale ) 933{ 934 return _buildColShapes( useVisibleMesh, scale, NULL, false ); 935} 936 937void TSShape::buildColShapes( bool useVisibleMesh, const Point3F &scale, Vector< CollisionShapeInfo> *list ) 938{ 939 _buildColShapes( useVisibleMesh, scale, list, true ); 940} 941 942PhysicsCollision* TSShape::_buildColShapes( bool useVisibleMesh, const Point3F &scale, Vector< CollisionShapeInfo> *list, bool perMesh ) 943{ 944 PROFILE_SCOPE( TSShape_buildColShapes ); 945 946 if ( !PHYSICSMGR ) 947 return NULL; 948 949 PhysicsCollision *colShape = NULL; 950 U32 surfaceKey = 0; 951 952 if ( useVisibleMesh ) 953 { 954 // Here we build triangle collision meshes from the 955 // visible detail levels. 956 957 // A negative subshape on the detail means we don't have geometry. 958 const TSShape::Detail &detail = details[0]; 959 if ( detail.subShapeNum < 0 ) 960 return NULL; 961 962 // We don't try to optimize the triangles we're given 963 // and assume the art was created properly for collision. 964 ConcretePolyList polyList; 965 polyList.setTransform( &MatrixF::Identity, scale ); 966 967 // Create the collision meshes. 968 S32 start = subShapeFirstObject[ detail.subShapeNum ]; 969 S32 end = start + subShapeNumObjects[ detail.subShapeNum ]; 970 for ( S32 o=start; o < end; o++ ) 971 { 972 const TSShape::Object &object = objects[o]; 973 if ( detail.objectDetailNum >= object.numMeshes ) 974 continue; 975 976 // No mesh or no verts.... nothing to do. 977 TSMesh *mesh = meshes[ object.startMeshIndex + detail.objectDetailNum ]; 978 if ( !mesh || mesh->mNumVerts == 0 ) 979 continue; 980 981 // Gather the mesh triangles. 982 polyList.clear(); 983 mesh->buildPolyList( 0, &polyList, surfaceKey, NULL ); 984 985 // Create the collision shape if we haven't already. 986 if ( !colShape ) 987 colShape = PHYSICSMGR->createCollision(); 988 989 // Get the object space mesh transform. 990 MatrixF localXfm; 991 getNodeWorldTransform( object.nodeIndex, &localXfm ); 992 993 colShape->addTriangleMesh( polyList.mVertexList.address(), 994 polyList.mVertexList.size(), 995 polyList.mIndexList.address(), 996 polyList.mIndexList.size() / 3, 997 localXfm ); 998 999 if ( perMesh ) 1000 { 1001 list->increment(); 1002 list->last().colNode = -1; 1003 list->last().colShape = colShape; 1004 colShape = NULL; 1005 } 1006 } 1007 1008 // Return what we built... if anything. 1009 return colShape; 1010 } 1011 1012 1013 // Scan out the collision hulls... 1014 // 1015 // TODO: We need to support LOS collision for physics. 1016 // 1017 for ( U32 i = 0; i < details.size(); i++ ) 1018 { 1019 const TSShape::Detail &detail = details[i]; 1020 const String &name = names[detail.nameIndex]; 1021 1022 // Is this a valid collision detail. 1023 if ( !dStrStartsWith( name, "Collision" ) || detail.subShapeNum < 0 ) 1024 continue; 1025 1026 // Now go thru the meshes for this detail. 1027 S32 start = subShapeFirstObject[ detail.subShapeNum ]; 1028 S32 end = start + subShapeNumObjects[ detail.subShapeNum ]; 1029 if ( start >= end ) 1030 continue; 1031 1032 for ( S32 o=start; o < end; o++ ) 1033 { 1034 const TSShape::Object &object = objects[o]; 1035 const String &meshName = names[ object.nameIndex ]; 1036 1037 if ( object.numMeshes <= detail.objectDetailNum ) 1038 continue; 1039 1040 // No mesh, a flat bounds, or no verts.... nothing to do. 1041 TSMesh *mesh = meshes[ object.startMeshIndex + detail.objectDetailNum ]; 1042 if ( !mesh || mesh->getBounds().isEmpty() || mesh->mNumVerts == 0 ) 1043 continue; 1044 1045 // We need the default mesh transform. 1046 MatrixF localXfm; 1047 getNodeWorldTransform( object.nodeIndex, &localXfm ); 1048 Point3F t = localXfm.getPosition(); 1049 t.convolve(scale); 1050 localXfm.setPosition(t); 1051 1052 // We have some sort of collision shape... so allocate it. 1053 if ( !colShape ) 1054 colShape = PHYSICSMGR->createCollision(); 1055 1056 // We have geometry... what is it? 1057 if ( dStrStartsWith( meshName, "Colbox" ) ) 1058 { 1059 // The bounds define the box extents directly. 1060 Point3F halfWidth = mesh->getBounds().getExtents() * scale * 0.5f; 1061 1062 // Add the offset to the center of the bounds 1063 // into the local space transform. 1064 MatrixF centerXfm( true ); 1065 Point3F t = mesh->getBounds().getCenter(); 1066 t.convolve(scale); 1067 centerXfm.setPosition(t); 1068 localXfm.mul( centerXfm ); 1069 1070 colShape->addBox( halfWidth, localXfm ); 1071 } 1072 else if ( dStrStartsWith( meshName, "Colsphere" ) ) 1073 { 1074 // Get a sphere inscribed to the bounds. 1075 Point3F extents = mesh->getBounds().getExtents() * scale; 1076 F32 radius = extents.least() * 0.5f; 1077 1078 // Add the offset to the center of the bounds 1079 // into the local space transform. 1080 MatrixF primXfm( true ); 1081 Point3F t = mesh->getBounds().getCenter(); 1082 t.convolve(scale); 1083 primXfm.setPosition(t); 1084 localXfm.mul( primXfm ); 1085 1086 colShape->addSphere( radius, localXfm ); 1087 } 1088 else if ( dStrStartsWith( meshName, "Colcapsule" ) ) 1089 { 1090 // Use the smallest extent as the radius for the capsule. 1091 Point3F extents = mesh->getBounds().getExtents() * scale; 1092 F32 radius = extents.least() * 0.5f; 1093 1094 // We need to center the capsule and align it to the Y axis. 1095 MatrixF primXfm( true ); 1096 Point3F t = mesh->getBounds().getCenter(); 1097 t.convolve(scale); 1098 primXfm.setPosition(t); 1099 1100 // Use the longest axis as the capsule height. 1101 F32 height = -radius * 2.0f; 1102 if ( extents.x > extents.y && extents.x > extents.z ) 1103 { 1104 primXfm.setColumn( 0, Point3F( 0, 0, 1 ) ); 1105 primXfm.setColumn( 1, Point3F( 1, 0, 0 ) ); 1106 primXfm.setColumn( 2, Point3F( 0, 1, 0 ) ); 1107 height += extents.x; 1108 } 1109 else if ( extents.z > extents.x && extents.z > extents.y ) 1110 { 1111 primXfm.setColumn( 0, Point3F( 0, 1, 0 ) ); 1112 primXfm.setColumn( 1, Point3F( 0, 0, 1 ) ); 1113 primXfm.setColumn( 2, Point3F( 1, 0, 0 ) ); 1114 height += extents.z; 1115 } 1116 else 1117 height += extents.y; 1118 1119 // Add the primitive transform into the local transform. 1120 localXfm.mul( primXfm ); 1121 1122 // If we didn't find a positive height then fallback to 1123 // creating a sphere which is better than nothing. 1124 if ( height > 0.0f ) 1125 colShape->addCapsule( radius, height, localXfm ); 1126 else 1127 colShape->addSphere( radius, localXfm ); 1128 } 1129 else if ( dStrStartsWith( meshName, "Colmesh" ) ) 1130 { 1131 // For a triangle mesh we gather the triangles raw from the 1132 // mesh and don't do any optimizations or cleanup. 1133 ConcretePolyList polyList; 1134 polyList.setTransform( &MatrixF::Identity, scale ); 1135 mesh->buildPolyList( 0, &polyList, surfaceKey, NULL ); 1136 colShape->addTriangleMesh( polyList.mVertexList.address(), 1137 polyList.mVertexList.size(), 1138 polyList.mIndexList.address(), 1139 polyList.mIndexList.size() / 3, 1140 localXfm ); 1141 } 1142 else 1143 { 1144 // Any other mesh name we assume as a generic convex hull. 1145 // 1146 // Collect the verts using the vertex polylist which will 1147 // filter out duplicates. This is importaint as the convex 1148 // generators can sometimes fail with duplicate verts. 1149 // 1150 VertexPolyList polyList; 1151 MatrixF meshMat( localXfm ); 1152 1153 polyList.setTransform( &MatrixF::Identity, scale ); 1154 mesh->buildPolyList( 0, &polyList, surfaceKey, NULL ); 1155 colShape->addConvex( polyList.getVertexList().address(), 1156 polyList.getVertexList().size(), 1157 meshMat ); 1158 } 1159 1160 if ( perMesh ) 1161 { 1162 list->increment(); 1163 1164 S32 detailNum; 1165 String::GetTrailingNumber( name, detailNum ); 1166 1167 String str = String::ToString( "%s%i", meshName.c_str(), detailNum ); 1168 S32 found = findNode( str ); 1169 1170 if ( found == -1 ) 1171 { 1172 str = str.replace('-','_'); 1173 found = findNode( str ); 1174 } 1175 1176 list->last().colNode = found; 1177 list->last().colShape = colShape; 1178 1179 colShape = NULL; 1180 } 1181 1182 } // objects 1183 1184 } // details 1185 1186 return colShape; 1187} 1188 1189bool TSMesh::buildPolyListOpcode( const S32 od, AbstractPolyList *polyList, const Box3F &nodeBox, TSMaterialList *materials ) 1190{ 1191 PROFILE_SCOPE( TSMesh_buildPolyListOpcode ); 1192 1193 // This is small... there is no win for preallocating it. 1194 Opcode::AABBCollider opCollider; 1195 opCollider.SetPrimitiveTests( true ); 1196 1197 // This isn't really needed within the AABBCollider as 1198 // we don't use temporal coherance... use a static to 1199 // remove the allocation overhead. 1200 static Opcode::AABBCache opCache; 1201 1202 IceMaths::AABB opBox; 1203 opBox.SetMinMax( Point( nodeBox.minExtents.x, nodeBox.minExtents.y, nodeBox.minExtents.z ), 1204 Point( nodeBox.maxExtents.x, nodeBox.maxExtents.y, nodeBox.maxExtents.z ) ); 1205 Opcode::CollisionAABB opCBox(opBox); 1206 1207 if ( !opCollider.Collide( opCache, opCBox, *mOptTree ) ) 1208 return false; 1209 1210 U32 count = opCollider.GetNbTouchedPrimitives(); 1211 const udword *idx = opCollider.GetTouchedPrimitives(); 1212 1213 Opcode::VertexPointers vp; 1214 U32 plIdx[3]; 1215 S32 j; 1216 Point3F tmp; 1217 const IceMaths::Point **verts; 1218 const Opcode::MeshInterface *mi = mOptTree->GetMeshInterface(); 1219 1220 for ( S32 i=0; i < count; i++ ) 1221 { 1222 // Get the triangle... 1223 mi->GetTriangle( vp, idx[i] ); 1224 verts = vp.Vertex; 1225 1226 // And register it in the polylist... 1227 polyList->begin( NULL, i ); 1228 1229 for( j = 2; j > -1; j-- ) 1230 { 1231 tmp.set( verts[j]->x, verts[j]->y, verts[j]->z ); 1232 plIdx[j] = polyList->addPoint( tmp ); 1233 polyList->vertex( plIdx[j] ); 1234 } 1235 1236 polyList->plane( plIdx[0], plIdx[2], plIdx[1] ); 1237 1238 polyList->end(); 1239 } 1240 1241 // TODO: Add a polyList->getCount() so we can see if we 1242 // got clipped polys and didn't really emit anything. 1243 return count > 0; 1244} 1245 1246bool TSMesh::buildConvexOpcode( const MatrixF &meshToObjectMat, const Box3F &nodeBox, Convex *convex, Convex *list ) 1247{ 1248 PROFILE_SCOPE( TSMesh_buildConvexOpcode ); 1249 1250 // This is small... there is no win for preallocating it. 1251 Opcode::AABBCollider opCollider; 1252 opCollider.SetPrimitiveTests( true ); 1253 1254 // This isn't really needed within the AABBCollider as 1255 // we don't use temporal coherance... use a static to 1256 // remove the allocation overhead. 1257 static Opcode::AABBCache opCache; 1258 1259 IceMaths::AABB opBox; 1260 opBox.SetMinMax( Point( nodeBox.minExtents.x, nodeBox.minExtents.y, nodeBox.minExtents.z ), 1261 Point( nodeBox.maxExtents.x, nodeBox.maxExtents.y, nodeBox.maxExtents.z ) ); 1262 Opcode::CollisionAABB opCBox(opBox); 1263 1264 if( !opCollider.Collide( opCache, opCBox, *mOptTree ) ) 1265 return false; 1266 1267 U32 cnt = opCollider.GetNbTouchedPrimitives(); 1268 const udword *idx = opCollider.GetTouchedPrimitives(); 1269 1270 Opcode::VertexPointers vp; 1271 for ( S32 i = 0; i < cnt; i++ ) 1272 { 1273 // First, check our active convexes for a potential match (and clean things 1274 // up, too.) 1275 const U32 curIdx = idx[i]; 1276 1277 // See if the square already exists as part of the working set. 1278 bool gotMatch = false; 1279 CollisionWorkingList& wl = convex->getWorkingList(); 1280 for ( CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext ) 1281 { 1282 if( itr->mConvex->getType() != TSPolysoupConvexType ) 1283 continue; 1284 1285 const TSStaticPolysoupConvex *chunkc = static_cast<TSStaticPolysoupConvex*>( itr->mConvex ); 1286 1287 if( chunkc->getObject() != TSStaticPolysoupConvex::smCurObject ) 1288 continue; 1289 1290 if( chunkc->mesh != this ) 1291 continue; 1292 1293 if( chunkc->idx != curIdx ) 1294 continue; 1295 1296 // A match! Don't need to add it. 1297 gotMatch = true; 1298 break; 1299 } 1300 1301 if( gotMatch ) 1302 continue; 1303 1304 // Get the triangle... 1305 mOptTree->GetMeshInterface()->GetTriangle( vp, idx[i] ); 1306 1307 Point3F a( vp.Vertex[0]->x, vp.Vertex[0]->y, vp.Vertex[0]->z ); 1308 Point3F b( vp.Vertex[1]->x, vp.Vertex[1]->y, vp.Vertex[1]->z ); 1309 Point3F c( vp.Vertex[2]->x, vp.Vertex[2]->y, vp.Vertex[2]->z ); 1310 1311 // Transform the result into object space! 1312 meshToObjectMat.mulP( a ); 1313 meshToObjectMat.mulP( b ); 1314 meshToObjectMat.mulP( c ); 1315 1316 PlaneF p( c, b, a ); 1317 Point3F peak = ((a + b + c) / 3.0f) - (p * 0.15f); 1318 1319 // Set up the convex... 1320 TSStaticPolysoupConvex *cp = new TSStaticPolysoupConvex(); 1321 1322 list->registerObject( cp ); 1323 convex->addToWorkingList( cp ); 1324 1325 cp->mesh = this; 1326 cp->idx = curIdx; 1327 cp->mObject = TSStaticPolysoupConvex::smCurObject; 1328 1329 cp->normal = p; 1330 cp->verts[0] = a; 1331 cp->verts[1] = b; 1332 cp->verts[2] = c; 1333 cp->verts[3] = peak; 1334 1335 // Update the bounding box. 1336 Box3F &bounds = cp->box; 1337 bounds.minExtents.set( F32_MAX, F32_MAX, F32_MAX ); 1338 bounds.maxExtents.set( -F32_MAX, -F32_MAX, -F32_MAX ); 1339 1340 bounds.minExtents.setMin( a ); 1341 bounds.minExtents.setMin( b ); 1342 bounds.minExtents.setMin( c ); 1343 bounds.minExtents.setMin( peak ); 1344 1345 bounds.maxExtents.setMax( a ); 1346 bounds.maxExtents.setMax( b ); 1347 bounds.maxExtents.setMax( c ); 1348 bounds.maxExtents.setMax( peak ); 1349 } 1350 1351 return true; 1352} 1353 1354void TSMesh::prepOpcodeCollision() 1355{ 1356 // Make sure opcode is loaded! 1357 if( !gOpcodeInitialized ) 1358 { 1359 Opcode::InitOpcode(); 1360 gOpcodeInitialized = true; 1361 } 1362 1363 // Don't re init if we already have something... 1364 if ( mOptTree ) 1365 return; 1366 1367 // Ok, first set up a MeshInterface 1368 Opcode::MeshInterface *mi = new Opcode::MeshInterface(); 1369 mOpMeshInterface = mi; 1370 1371 // Figure out how many triangles we have... 1372 U32 triCount = 0; 1373 const U32 base = 0; 1374 for ( U32 i = 0; i < mPrimitives.size(); i++ ) 1375 { 1376 TSDrawPrimitive & draw = mPrimitives[i]; 1377 const U32 start = draw.start; 1378 1379 AssertFatal( draw.matIndex & TSDrawPrimitive::Indexed,"TSMesh::buildPolyList (1)" ); 1380 1381 // gonna depend on what kind of primitive it is... 1382 if ( (draw.matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Triangles ) 1383 triCount += draw.numElements / 3; 1384 else 1385 { 1386 // Have to walk the tristrip to get a count... may have degenerates 1387 U32 idx0 = base + mIndices[start + 0]; 1388 U32 idx1; 1389 U32 idx2 = base + mIndices[start + 1]; 1390 U32 * nextIdx = &idx1; 1391 for ( S32 j = 2; j < draw.numElements; j++ ) 1392 { 1393 *nextIdx = idx2; 1394 // nextIdx = (j%2)==0 ? &idx0 : &idx1; 1395 nextIdx = (U32*) ( (dsize_t)nextIdx ^ (dsize_t)&idx0 ^ (dsize_t)&idx1); 1396 idx2 = base + mIndices[start + j]; 1397 if ( idx0 == idx1 || idx0 == idx2 || idx1 == idx2 ) 1398 continue; 1399 1400 triCount++; 1401 } 1402 } 1403 } 1404 1405 // Just do the first trilist for now. 1406 mi->SetNbVertices( mVertexData.isReady() ? mNumVerts : mVerts.size() ); 1407 mi->SetNbTriangles( triCount ); 1408 1409 // Stuff everything into appropriate arrays. 1410 IceMaths::IndexedTriangle *its = new IceMaths::IndexedTriangle[ mi->GetNbTriangles() ], *curIts = its; 1411 IceMaths::Point *pts = new IceMaths::Point[ mi->GetNbVertices() ]; 1412 1413 mOpTris = its; 1414 mOpPoints = pts; 1415 1416 // add the polys... 1417 for ( U32 i = 0; i < mPrimitives.size(); i++ ) 1418 { 1419 TSDrawPrimitive & draw = mPrimitives[i]; 1420 const U32 start = draw.start; 1421 1422 AssertFatal( draw.matIndex & TSDrawPrimitive::Indexed,"TSMesh::buildPolyList (1)" ); 1423 1424 const U32 matIndex = draw.matIndex & TSDrawPrimitive::MaterialMask; 1425 1426 // gonna depend on what kind of primitive it is... 1427 if ( (draw.matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Triangles ) 1428 { 1429 for ( S32 j = 0; j < draw.numElements; ) 1430 { 1431 curIts->mVRef[2] = base + mIndices[start + j + 0]; 1432 curIts->mVRef[1] = base + mIndices[start + j + 1]; 1433 curIts->mVRef[0] = base + mIndices[start + j + 2]; 1434 curIts->mMatIdx = matIndex; 1435 curIts++; 1436 1437 j += 3; 1438 } 1439 } 1440 else 1441 { 1442 AssertFatal( (draw.matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Strip,"TSMesh::buildPolyList (2)" ); 1443 1444 U32 idx0 = base + mIndices[start + 0]; 1445 U32 idx1; 1446 U32 idx2 = base + mIndices[start + 1]; 1447 U32 * nextIdx = &idx1; 1448 for ( S32 j = 2; j < draw.numElements; j++ ) 1449 { 1450 *nextIdx = idx2; 1451 // nextIdx = (j%2)==0 ? &idx0 : &idx1; 1452 nextIdx = (U32*) ( (dsize_t)nextIdx ^ (dsize_t)&idx0 ^ (dsize_t)&idx1); 1453 idx2 = base + mIndices[start + j]; 1454 if ( idx0 == idx1 || idx0 == idx2 || idx1 == idx2 ) 1455 continue; 1456 1457 curIts->mVRef[2] = idx0; 1458 curIts->mVRef[1] = idx1; 1459 curIts->mVRef[0] = idx2; 1460 curIts->mMatIdx = matIndex; 1461 curIts++; 1462 } 1463 } 1464 } 1465 1466 AssertFatal( (curIts - its) == mi->GetNbTriangles(), "Triangle count mismatch!" ); 1467 1468 for( S32 i = 0; i < mi->GetNbVertices(); i++ ) 1469 { 1470 if( mVertexData.isReady() ) 1471 { 1472 const __TSMeshVertexBase &vertData = mVertexData.getBase(i); 1473 pts[i].Set( vertData.vert().x, vertData.vert().y, vertData.vert().z ); 1474 } 1475 else 1476 { 1477 pts[i].Set( mVerts[i].x, mVerts[i].y, mVerts[i].z ); 1478 } 1479 } 1480 1481 mi->SetPointers( its, pts ); 1482 1483 // Ok, we've got a mesh interface populated, now let's build a thingy to collide against. 1484 mOptTree = new Opcode::Model(); 1485 1486 Opcode::OPCODECREATE opcc; 1487 1488 opcc.mCanRemap = true; 1489 opcc.mIMesh = mi; 1490 opcc.mKeepOriginal = false; 1491 opcc.mNoLeaf = false; 1492 opcc.mQuantized = false; 1493 opcc.mSettings.mLimit = 1; 1494 1495 mOptTree->Build( opcc ); 1496} 1497 1498static Point3F texGenAxis[18] = 1499{ 1500 Point3F(0,0,1), Point3F(1,0,0), Point3F(0,-1,0), 1501 Point3F(0,0,-1), Point3F(1,0,0), Point3F(0,1,0), 1502 Point3F(1,0,0), Point3F(0,1,0), Point3F(0,0,1), 1503 Point3F(-1,0,0), Point3F(0,1,0), Point3F(0,0,-1), 1504 Point3F(0,1,0), Point3F(1,0,0), Point3F(0,0,1), 1505 Point3F(0,-1,0), Point3F(-1,0,0), Point3F(0,0,-1) 1506}; 1507 1508 1509bool TSMesh::castRayOpcode( const Point3F &start, const Point3F &end, RayInfo *info, TSMaterialList *materials ) 1510{ 1511 Opcode::RayCollider ray; 1512 Opcode::CollisionFaces cfs; 1513 1514 IceMaths::Point dir(end.x - start.x, end.y - start.y, end.z - start.z ); 1515 const F32 rayLen = dir.Magnitude(); 1516 IceMaths::Ray vec( Point(start.x, start.y, start.z), dir.Normalize() ); 1517 1518 ray.SetDestination( &cfs); 1519 ray.SetFirstContact( false ); 1520 ray.SetClosestHit( true ); 1521 ray.SetPrimitiveTests( true ); 1522 ray.SetCulling( true ); 1523 ray.SetMaxDist( rayLen ); 1524 1525 AssertFatal( ray.ValidateSettings() == NULL, "invalid ray settings" ); 1526 1527 // Do collision. 1528 bool safety = ray.Collide( vec, *mOptTree ); 1529 AssertFatal( safety, "TSMesh::castRayOpcode - no good ray collide!" ); 1530 1531 // If no hit, just skip out. 1532 if( cfs.GetNbFaces() == 0 ) 1533 return false; 1534 1535 // Got a hit! 1536 AssertFatal( cfs.GetNbFaces() == 1, "bad" ); 1537 const Opcode::CollisionFace &face = cfs.GetFaces()[0]; 1538 1539 // If the cast was successful let's check if the t value is less than what we had 1540 // and toggle the collision boolean 1541 // Stupid t... i prefer coffee 1542 const F32 t = face.mDistance / rayLen; 1543 1544 if( t < 0.0f || t > 1.0f ) 1545 return false; 1546 1547 if( t <= info->t ) 1548 { 1549 info->t = t; 1550 1551 // Calculate the normal. 1552 Opcode::VertexPointers vp; 1553 mOptTree->GetMeshInterface()->GetTriangle( vp, face.mFaceID ); 1554 1555 if ( materials && vp.MatIdx >= 0 && vp.MatIdx < materials->size() ) 1556 info->material = materials->getMaterialInst( vp.MatIdx ); 1557 1558 // Get the two edges. 1559 IceMaths::Point baseVert = *vp.Vertex[0]; 1560 IceMaths::Point a = *vp.Vertex[1] - baseVert; 1561 IceMaths::Point b = *vp.Vertex[2] - baseVert; 1562 1563 IceMaths::Point n; 1564 n.Cross( a, b ); 1565 n.Normalize(); 1566 1567 info->normal.set( n.x, n.y, n.z ); 1568 1569 // generate UV coordinate across mesh based on 1570 // matching normals, this isn't done by default and is 1571 // primarily of interest in matching a collision point to 1572 // either a GUI control coordinate or finding a hit pixel in texture space 1573 if (info->generateTexCoord) 1574 { 1575 baseVert = *vp.Vertex[0]; 1576 a = *vp.Vertex[1]; 1577 b = *vp.Vertex[2]; 1578 1579 Point3F facePoint = (1.0f - face.mU - face.mV) * Point3F(baseVert.x, baseVert.y, baseVert.z) 1580 + face.mU * Point3F(a.x, a.y, a.z) + face.mV * Point3F(b.x, b.y, b.z); 1581 1582 U32 faces[1024]; 1583 U32 numFaces = 0; 1584 for (U32 i = 0; i < mOptTree->GetMeshInterface()->GetNbTriangles(); i++) 1585 { 1586 if ( i == face.mFaceID ) 1587 { 1588 faces[numFaces++] = i; 1589 } 1590 else 1591 { 1592 IceMaths::Point n2; 1593 1594 mOptTree->GetMeshInterface()->GetTriangle( vp, i ); 1595 1596 baseVert = *vp.Vertex[0]; 1597 a = *vp.Vertex[1] - baseVert; 1598 b = *vp.Vertex[2] - baseVert; 1599 n2.Cross( a, b ); 1600 n2.Normalize(); 1601 1602 F32 eps = .01f; 1603 if ( mFabs(n.x - n2.x) < eps && mFabs(n.y - n2.y) < eps && mFabs(n.z - n2.z) < eps) 1604 { 1605 faces[numFaces++] = i; 1606 } 1607 } 1608 1609 if (numFaces == 1024) 1610 { 1611 // too many faces in this collision mesh for UV generation 1612 return true; 1613 } 1614 1615 } 1616 1617 Point3F min(F32_MAX, F32_MAX, F32_MAX); 1618 Point3F max(-F32_MAX, -F32_MAX, -F32_MAX); 1619 1620 for (U32 i = 0; i < numFaces; i++) 1621 { 1622 mOptTree->GetMeshInterface()->GetTriangle( vp, faces[i] ); 1623 1624 for ( U32 j =0; j < 3; j++) 1625 { 1626 a = *vp.Vertex[j]; 1627 1628 if (a.x < min.x) 1629 min.x = a.x; 1630 if (a.y < min.y) 1631 min.y = a.y; 1632 if (a.z < min.z) 1633 min.z = a.z; 1634 1635 if (a.x > max.x) 1636 max.x = a.x; 1637 if (a.y > max.y) 1638 max.y = a.y; 1639 if (a.z > max.z) 1640 max.z = a.z; 1641 1642 } 1643 1644 } 1645 1646 // slerp 1647 Point3F divSafe = (max - min); 1648 if (divSafe.x == 0.0f) divSafe.x = POINT_EPSILON; 1649 if (divSafe.y == 0.0f) divSafe.y = POINT_EPSILON; 1650 if (divSafe.z == 0.0f) divSafe.z = POINT_EPSILON; 1651 1652 Point3F s = ( (max - min) - (facePoint - min) ) / divSafe; 1653 1654 // compute axis 1655 S32 bestAxis = 0; 1656 F32 best = 0.f; 1657 1658 for (U32 i = 0 ; i < 6 ; i++) 1659 { 1660 F32 dot = mDot (info->normal, texGenAxis[i*3]); 1661 if (dot > best) 1662 { 1663 best = dot; 1664 bestAxis = i; 1665 } 1666 } 1667 1668 Point3F xv = texGenAxis[bestAxis*3+1]; 1669 Point3F yv = texGenAxis[bestAxis*3+2]; 1670 1671 S32 sv, tv; 1672 1673 if (xv.x) 1674 sv = 0; 1675 else if (xv.y) 1676 sv = 1; 1677 else 1678 sv = 2; 1679 1680 if (yv.x) 1681 tv = 0; 1682 else if (yv.y) 1683 tv = 1; 1684 else 1685 tv = 2; 1686 1687 // handle coord translation 1688 if (bestAxis == 2 || bestAxis == 3) 1689 { 1690 S32 x = sv; 1691 sv = tv; 1692 tv = x; 1693 1694 if (yv.z < 0) 1695 s[sv] = 1.f - s[sv]; 1696 } 1697 1698 if (bestAxis < 2) 1699 { 1700 if (yv.y < 0) 1701 s[sv] = 1.f - s[sv]; 1702 } 1703 1704 if (bestAxis > 3) 1705 { 1706 s[sv] = 1.f - s[sv]; 1707 if (yv.z > 0) 1708 s[tv] = 1.f - s[tv]; 1709 1710 } 1711 1712 // done! 1713 info->texCoord.set(s[sv], s[tv]); 1714 1715 } 1716 1717 return true; 1718 } 1719 1720 return false; 1721} 1722