Torque3D Documentation / _generateds / tsCollision.cpp

tsCollision.cpp

Engine/source/ts/tsCollision.cpp

More...

Public Variables

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