tsShape.cpp

Engine/source/ts/tsShape.cpp

More...

Public Defines

define

Public Variables

gTempNodeTransforms (__FILE__, __LINE__)

Detailed Description

Public Defines

tsalloc() 

Public Variables

Vector< MatrixF > gTempNodeTransforms (__FILE__, __LINE__)
   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#include "ts/tsShape.h"
  26
  27#include "ts/tsLastDetail.h"
  28#include "ts/tsMaterialList.h"
  29#include "core/stringTable.h"
  30#include "console/console.h"
  31#include "ts/tsShapeInstance.h"
  32#include "collision/convex.h"
  33#include "materials/matInstance.h"
  34#include "materials/materialManager.h"
  35#include "math/mathIO.h"
  36#include "core/util/endian.h"
  37#include "core/stream/fileStream.h"
  38#include "console/compiler.h"
  39#include "core/fileObject.h"
  40
  41#ifdef TORQUE_COLLADA
  42extern TSShape* loadColladaShape(const Torque::Path &path);
  43#endif
  44
  45#ifdef TORQUE_ASSIMP
  46extern TSShape* assimpLoadShape(const Torque::Path &path);
  47#endif
  48
  49/// most recent version -- this is the version we write
  50S32 TSShape::smVersion = 28;
  51/// the version currently being read...valid only during a read
  52S32 TSShape::smReadVersion = -1;
  53const U32 TSShape::smMostRecentExporterVersion = DTS_EXPORTER_CURRENT_VERSION;
  54
  55F32 TSShape::smAlphaOutLastDetail = -1.0f;
  56F32 TSShape::smAlphaInBillboard = 0.15f;
  57F32 TSShape::smAlphaOutBillboard = 0.15f;
  58F32 TSShape::smAlphaInDefault = -1.0f;
  59F32 TSShape::smAlphaOutDefault = -1.0f;
  60
  61// don't bother even loading this many of the highest detail levels (but
  62// always load last renderable detail)
  63S32 TSShape::smNumSkipLoadDetails = 0;
  64
  65bool TSShape::smInitOnRead = true;
  66bool TSShape::smUseHardwareSkinning = true;
  67U32 TSShape::smMaxSkinBones = 70;
  68
  69
  70TSShape::TSShape()
  71{
  72   mExporterVersion = 124;
  73   mSmallestVisibleSize = 2;
  74   mSmallestVisibleDL = 0;
  75   mRadius = 0;
  76   mFlags = 0;
  77   tubeRadius = 0;
  78   data = NULL;
  79   materialList = NULL;
  80   mReadVersion = -1; // -1 means constructed from scratch (e.g., in exporter or no read yet)
  81   mSequencesConstructed = false;
  82   mShapeData = NULL;
  83   mShapeDataSize = 0;
  84   mVertexSize = 0;
  85   mUseDetailFromScreenError = false;
  86   mNeedReinit = false;
  87
  88   mDetailLevelLookup.setSize( 1 );
  89   mDetailLevelLookup[0].set( -1, 0 );
  90
  91   VECTOR_SET_ASSOCIATION(sequences);
  92   VECTOR_SET_ASSOCIATION(nodeRotations);
  93   VECTOR_SET_ASSOCIATION(nodeTranslations);
  94   VECTOR_SET_ASSOCIATION(nodeUniformScales);
  95   VECTOR_SET_ASSOCIATION(nodeAlignedScales);
  96   VECTOR_SET_ASSOCIATION(nodeArbitraryScaleRots);
  97   VECTOR_SET_ASSOCIATION(nodeArbitraryScaleFactors);
  98   VECTOR_SET_ASSOCIATION(groundRotations);
  99   VECTOR_SET_ASSOCIATION(groundTranslations);
 100   VECTOR_SET_ASSOCIATION(triggers);
 101   VECTOR_SET_ASSOCIATION(billboardDetails);
 102   VECTOR_SET_ASSOCIATION(detailCollisionAccelerators);
 103   VECTOR_SET_ASSOCIATION(names);
 104
 105   VECTOR_SET_ASSOCIATION( nodes );
 106   VECTOR_SET_ASSOCIATION( objects );
 107   VECTOR_SET_ASSOCIATION( objectStates );
 108   VECTOR_SET_ASSOCIATION( subShapeFirstNode );
 109   VECTOR_SET_ASSOCIATION( subShapeFirstObject );
 110   VECTOR_SET_ASSOCIATION( detailFirstSkin );
 111   VECTOR_SET_ASSOCIATION( subShapeNumNodes );
 112   VECTOR_SET_ASSOCIATION( subShapeNumObjects );
 113   VECTOR_SET_ASSOCIATION( details );
 114   VECTOR_SET_ASSOCIATION( defaultRotations );
 115   VECTOR_SET_ASSOCIATION( defaultTranslations );
 116
 117   VECTOR_SET_ASSOCIATION( subShapeFirstTranslucentObject );
 118   VECTOR_SET_ASSOCIATION( meshes );
 119
 120   VECTOR_SET_ASSOCIATION( alphaIn );
 121   VECTOR_SET_ASSOCIATION( alphaOut );
 122}
 123
 124TSShape::~TSShape()
 125{
 126   delete materialList;
 127
 128   S32 i;
 129
 130   // everything left over here is a legit mesh
 131   for (i=0; i<meshes.size(); i++)
 132   {
 133      if (!meshes[i])
 134         continue;
 135
 136      // Handle meshes that were either assembled with the shape or added later
 137      if (((S8*)meshes[i] >= mShapeData) && ((S8*)meshes[i] < (mShapeData + mShapeDataSize)))
 138         destructInPlace(meshes[i]);
 139      else
 140         delete meshes[i];
 141   }
 142
 143   for (i=0; i<billboardDetails.size(); i++)
 144   {
 145      delete billboardDetails[i];
 146      billboardDetails[i] = NULL;
 147   }
 148   billboardDetails.clear();
 149
 150   // Delete any generated accelerators
 151   S32 dca;
 152   for (dca = 0; dca < detailCollisionAccelerators.size(); dca++)
 153   {
 154      ConvexHullAccelerator* accel = detailCollisionAccelerators[dca];
 155      if (accel != NULL) {
 156         delete [] accel->vertexList;
 157         delete [] accel->normalList;
 158         for (S32 j = 0; j < accel->numVerts; j++)
 159            delete [] accel->emitStrings[j];
 160         delete [] accel->emitStrings;
 161         delete accel;
 162      }
 163   }
 164   for (dca = 0; dca < detailCollisionAccelerators.size(); dca++)
 165      detailCollisionAccelerators[dca] = NULL;
 166
 167   if( mShapeData )
 168      delete[] mShapeData;
 169}
 170
 171const String& TSShape::getName( S32 nameIndex ) const
 172{
 173   AssertFatal(nameIndex>=0 && nameIndex<names.size(),"TSShape::getName");
 174   return names[nameIndex];
 175}
 176
 177const String& TSShape::getMeshName( S32 meshIndex ) const
 178{
 179   S32 nameIndex = objects[meshIndex].nameIndex;
 180   if ( nameIndex < 0 )
 181      return String::EmptyString;
 182
 183   return names[nameIndex];
 184}
 185
 186const String& TSShape::getNodeName( S32 nodeIndex ) const
 187{   
 188   S32 nameIdx = nodes[nodeIndex].nameIndex;
 189   if ( nameIdx < 0 )
 190      return String::EmptyString;
 191
 192   return names[nameIdx];
 193}
 194
 195const String& TSShape::getSequenceName( S32 seqIndex ) const
 196{
 197   AssertFatal(seqIndex >= 0 && seqIndex<sequences.size(),"TSShape::getSequenceName index beyond range");
 198
 199   S32 nameIdx = sequences[seqIndex].nameIndex;
 200   if ( nameIdx < 0 )
 201      return String::EmptyString;
 202
 203   return names[nameIdx];
 204}
 205
 206S32 TSShape::findName(const String &name) const
 207{
 208   for (S32 i=0; i<names.size(); i++)
 209   {
 210      if (names[i].equal( name, String::NoCase ))
 211         return i;
 212   }
 213  
 214   return -1;
 215}
 216
 217const String& TSShape::getTargetName( S32 mapToNameIndex ) const
 218{
 219   S32 targetCount = materialList->getMaterialNameList().size();
 220
 221   if(mapToNameIndex < 0 || mapToNameIndex >= targetCount)
 222      return String::EmptyString;
 223
 224   return materialList->getMaterialNameList()[mapToNameIndex];
 225}
 226
 227S32 TSShape::getTargetCount() const
 228{
 229   if(!this)
 230      return -1;
 231
 232   return materialList->getMaterialNameList().size();
 233
 234}
 235
 236S32 TSShape::findNode(S32 nameIndex) const
 237{
 238   for (S32 i=0; i<nodes.size(); i++)
 239      if (nodes[i].nameIndex==nameIndex)
 240         return i;
 241   return -1;
 242}
 243
 244S32 TSShape::findObject(S32 nameIndex) const
 245{
 246   for (S32 i=0; i<objects.size(); i++)
 247      if (objects[i].nameIndex==nameIndex)
 248         return i;
 249   return -1;
 250}
 251
 252S32 TSShape::findDetail(S32 nameIndex) const
 253{
 254   for (S32 i=0; i<details.size(); i++)
 255      if (details[i].nameIndex==nameIndex)
 256         return i;
 257   return -1;
 258}
 259
 260S32 TSShape::findDetailBySize(S32 size) const
 261{
 262   for (S32 i=0; i<details.size(); i++)
 263      if (details[i].size==<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a>)
 264         return i;
 265   return -1;
 266}
 267
 268S32 TSShape::findSequence(S32 nameIndex) const
 269{
 270   for (S32 i=0; i<sequences.size(); i++)
 271      if (sequences[i].nameIndex==nameIndex)
 272         return i;
 273   return -1;
 274}
 275
 276bool TSShape::findMeshIndex(const String& meshName, S32& objIndex, S32& meshIndex)
 277{
 278   // Determine the object name and detail size from the mesh name
 279   S32 detailSize = 999;  
 280   objIndex = findObject(String::GetTrailingNumber(meshName, detailSize));
 281   if (objIndex < 0)
 282      return false;
 283
 284   // Determine the subshape this object belongs to
 285   S32 subShapeIndex = getSubShapeForObject(objIndex);
 286   AssertFatal(subShapeIndex < subShapeFirstObject.size(), "Could not find subshape for object!");
 287
 288   // Get the detail levels for the subshape
 289   Vector<S32> validDetails;
 290   getSubShapeDetails(subShapeIndex, validDetails);
 291
 292   // Find the detail with the correct size
 293   for (meshIndex = 0; meshIndex < validDetails.size(); meshIndex++)
 294   {
 295      const TSShape::Detail& det = details[validDetails[meshIndex]];
 296      if (detailSize == det.size)
 297         return true;
 298   }
 299
 300   return false;
 301}
 302
 303bool TSShape::needsBufferUpdate()
 304{
 305   // No buffer? definitely need an update!
 306   if (mVertexSize == 0 || mShapeVertexData.size == 0)
 307      return true;
 308
 309   // Check if we have modified vertex data
 310   for (Vector<TSMesh*>::iterator iter = meshes.begin(); iter != meshes.end(); iter++)
 311   {
 312      TSMesh *mesh = *iter;
 313      if (!mesh ||
 314         (mesh->getMeshType() != TSMesh::StandardMeshType &&
 315            mesh->getMeshType() != TSMesh::SkinMeshType))
 316         continue;
 317
 318      // NOTE: cant use mVertexData.isReady since that might not be init'd at this stage
 319      if (mesh->mVertSize == 0)
 320         return true;
 321   }
 322
 323   return false;
 324}
 325
 326TSMesh* TSShape::findMesh(const String& meshName)
 327{
 328   S32 objIndex, meshIndex;
 329   if (!findMeshIndex(meshName, objIndex, meshIndex))
 330      return 0;
 331   return meshes[objects[objIndex].startMeshIndex + meshIndex];
 332}
 333
 334S32 TSShape::getSubShapeForNode(S32 nodeIndex)
 335{
 336   for (S32 i = 0; i < subShapeFirstNode.size(); i++)
 337   {
 338      S32 start = subShapeFirstNode[i];
 339      S32 end = start + subShapeNumNodes[i];
 340      if ((nodeIndex >= start) && (nodeIndex < end))
 341         return i;;
 342   }
 343   return -1;
 344}
 345
 346S32 TSShape::getSubShapeForObject(S32 objIndex)
 347{
 348   for (S32 i = 0; i < subShapeFirstObject.size(); i++)
 349   {
 350      S32 start = subShapeFirstObject[i];
 351      S32 end = start + subShapeNumObjects[i];
 352      if ((objIndex >= start) && (objIndex < end))
 353         return i;
 354   }
 355   return -1;
 356}
 357
 358void TSShape::getSubShapeDetails(S32 subShapeIndex, Vector<S32>& validDetails)
 359{
 360   validDetails.clear();
 361   for (S32 i = 0; i < details.size(); i++)
 362   {
 363      if ((details[i].subShapeNum == subShapeIndex) ||
 364          (details[i].subShapeNum < 0))
 365          validDetails.push_back(i);
 366   }
 367}
 368
 369void TSShape::getNodeWorldTransform(S32 nodeIndex, MatrixF* mat) const
 370{
 371   if ( nodeIndex == -1 )
 372   {
 373      mat->identity();
 374   }
 375   else
 376   {
 377      // Calculate the world transform of the given node
 378      defaultRotations[nodeIndex].getQuatF().setMatrix(mat);
 379      mat->setPosition(defaultTranslations[nodeIndex]);
 380
 381      S32 parentIndex = nodes[nodeIndex].parentIndex;
 382      while (parentIndex != -1)
 383      {
 384         MatrixF mat2(*mat);
 385         defaultRotations[parentIndex].getQuatF().setMatrix(mat);
 386         mat->setPosition(defaultTranslations[parentIndex]);
 387         mat->mul(mat2);
 388
 389         parentIndex = nodes[parentIndex].parentIndex;
 390      }
 391   }
 392}
 393
 394void TSShape::getNodeObjects(S32 nodeIndex, Vector<S32>& nodeObjects)
 395{
 396   for (S32 i = 0; i < objects.size(); i++)
 397   {
 398      if ((nodeIndex == -1) || (objects[i].nodeIndex == nodeIndex))
 399         nodeObjects.push_back(i);
 400   }
 401}
 402
 403void TSShape::getNodeChildren(S32 nodeIndex, Vector<S32>& nodeChildren)
 404{
 405   for (S32 i = 0; i < nodes.size(); i++)
 406   {
 407      if (nodes[i].parentIndex == nodeIndex)
 408         nodeChildren.push_back(i);
 409   }
 410}
 411
 412void TSShape::getObjectDetails(S32 objIndex, Vector<S32>& objDetails)
 413{
 414   // Get the detail levels for this subshape
 415   Vector<S32> validDetails;
 416   getSubShapeDetails(getSubShapeForObject(objIndex), validDetails);
 417
 418   // Get the non-null details for this object
 419   const TSShape::Object& obj = objects[objIndex];
 420   for (S32 i = 0; i < obj.numMeshes; i++)
 421   {
 422      if (meshes[obj.startMeshIndex + i])
 423         objDetails.push_back(validDetails[i]);
 424   }
 425}
 426
 427void TSShape::init()
 428{
 429   initObjects();
 430   initVertexFeatures();
 431   initMaterialList();
 432   mNeedReinit = false;
 433}
 434
 435void TSShape::initObjects()
 436{
 437   S32 numSubShapes = subShapeFirstNode.size();
 438   AssertFatal(numSubShapes == subShapeFirstObject.size(), "TSShape::initObjects");
 439
 440   S32 i, j;
 441
 442   // set up parent/child relationships on nodes and objects
 443   for (i = 0; i<nodes.size(); i++)
 444      nodes[i].firstObject = nodes[i].firstChild = nodes[i].nextSibling = -1;
 445   for (i = 0; i<nodes.size(); i++)
 446   {
 447      S32 parentIndex = nodes[i].parentIndex;
 448      if (parentIndex >= 0)
 449      {
 450         if (nodes[parentIndex].firstChild<0)
 451            nodes[parentIndex].firstChild = i;
 452         else
 453         {
 454            S32 child = nodes[parentIndex].firstChild;
 455            while (nodes[child].nextSibling >= 0)
 456               child = nodes[child].nextSibling;
 457            nodes[child].nextSibling = i;
 458         }
 459      }
 460   }
 461   for (i = 0; i<objects.size(); i++)
 462   {
 463      objects[i].nextSibling = -1;
 464
 465      S32 nodeIndex = objects[i].nodeIndex;
 466      if (nodeIndex >= 0)
 467      {
 468         if (nodes[nodeIndex].firstObject<0)
 469            nodes[nodeIndex].firstObject = i;
 470         else
 471         {
 472            S32 objectIndex = nodes[nodeIndex].firstObject;
 473            while (objects[objectIndex].nextSibling >= 0)
 474               objectIndex = objects[objectIndex].nextSibling;
 475            objects[objectIndex].nextSibling = i;
 476         }
 477      }
 478   }
 479
 480   mFlags = 0;
 481   for (i = 0; i<sequences.size(); i++)
 482   {
 483      if (!sequences[i].animatesScale())
 484         continue;
 485
 486      U32 curVal = mFlags & AnyScale;
 487      U32 newVal = sequences[i].flags & AnyScale;
 488      mFlags &= ~(AnyScale);
 489      mFlags |= getMax(curVal, newVal); // take the larger value (can only convert upwards)
 490   }
 491
 492   // set up alphaIn and alphaOut vectors...
 493   alphaIn.setSize(details.size());
 494   alphaOut.setSize(details.size());
 495
 496   for (i = 0; i<details.size(); i++)
 497   {
 498      if (details[i].size<0)
 499      {
 500         // we don't care...
 501         alphaIn[i] = 0.0f;
 502         alphaOut[i] = 0.0f;
 503      }
 504      else if (i + 1 == details.size() || details[i + 1].size<0)
 505      {
 506         alphaIn[i] = 0.0f;
 507         alphaOut[i] = smAlphaOutLastDetail;
 508      }
 509      else
 510      {
 511         if (details[i + 1].subShapeNum<0)
 512         {
 513            // following detail is a billboard detail...treat special...
 514            alphaIn[i] = smAlphaInBillboard;
 515            alphaOut[i] = smAlphaOutBillboard;
 516         }
 517         else
 518         {
 519            // next detail is normal detail
 520            alphaIn[i] = smAlphaInDefault;
 521            alphaOut[i] = smAlphaOutDefault;
 522         }
 523      }
 524   }
 525
 526   for (i = mSmallestVisibleDL - 1; i >= 0; i--)
 527   {
 528      if (i<smNumSkipLoadDetails)
 529      {
 530         // this detail level renders when pixel size
 531         // is larger than our cap...zap all the meshes and decals
 532         // associated with it and use the next detail level
 533         // instead...
 534         S32 ss = details[i].subShapeNum;
 535         S32 od = details[i].objectDetailNum;
 536
 537         if (ss == details[i + 1].subShapeNum && od == details[i + 1].objectDetailNum)
 538            // doh! already done this one (init can be called multiple times on same shape due
 539            // to sequence importing).
 540            continue;
 541         details[i].subShapeNum = details[i + 1].subShapeNum;
 542         details[i].objectDetailNum = details[i + 1].objectDetailNum;
 543      }
 544   }
 545
 546   for (i = 0; i<details.size(); i++)
 547   {
 548      S32 count = 0;
 549      S32 ss = details[i].subShapeNum;
 550      S32 od = details[i].objectDetailNum;
 551      if (ss<0)
 552      {
 553         // billboard detail...
 554         details[i].polyCount = 2;
 555         continue;
 556      }
 557      S32 start = subShapeFirstObject[ss];
 558      S32 end = start + subShapeNumObjects[ss];
 559      for (j = start; j<end; j++)
 560      {
 561         Object & obj = objects[j];
 562         if (od<obj.numMeshes)
 563         {
 564            TSMesh * mesh = meshes[obj.startMeshIndex + od];
 565            count += mesh ? mesh->getNumPolys() : 0;
 566         }
 567      }
 568      details[i].polyCount = count;
 569   }
 570
 571   // Init the collision accelerator array.  Note that we don't compute the
 572   //  accelerators until the app requests them
 573   {
 574      S32 dca;
 575      for (dca = 0; dca < detailCollisionAccelerators.size(); dca++)
 576      {
 577         ConvexHullAccelerator* accel = detailCollisionAccelerators[dca];
 578         if (accel != NULL) {
 579            delete[] accel->vertexList;
 580            delete[] accel->normalList;
 581            for (S32 vertID = 0; vertID < accel->numVerts; vertID++)
 582               delete[] accel->emitStrings[vertID];
 583            delete[] accel->emitStrings;
 584            delete accel;
 585         }
 586      }
 587
 588      detailCollisionAccelerators.setSize(details.size());
 589      for (dca = 0; dca < detailCollisionAccelerators.size(); dca++)
 590         detailCollisionAccelerators[dca] = NULL;
 591   }
 592
 593   // Assign mesh parents & format
 594   for (Vector<TSMesh*>::iterator iter = meshes.begin(); iter != meshes.end(); iter++)
 595   {
 596      TSMesh *mesh = *iter;
 597      if (!mesh)
 598         continue;
 599
 600      if (mesh->mParentMesh >= meshes.size())
 601      {
 602         Con::warnf("Mesh %i has a bad parentMeshObject (%i)", iter - meshes.begin(), mesh->mParentMesh);
 603      }
 604
 605      if (mesh->mParentMesh >= 0 && mesh->mParentMesh < meshes.size())
 606      {
 607         mesh->mParentMeshObject = meshes[mesh->mParentMesh];
 608      }
 609      else
 610      {
 611         mesh->mParentMeshObject = NULL;
 612      }
 613
 614      mesh->mVertexFormat = &mVertexFormat;
 615   }
 616}
 617
 618void TSShape::initVertexBuffers()
 619{
 620   // Assumes mVertexData is valid
 621   if (!mShapeVertexData.vertexDataReady)
 622   {
 623      AssertFatal(false, "WTF");
 624   }
 625
 626   U32 destIndices = 0;
 627   U32 destPrims = 0;
 628
 629   for (Vector<TSMesh*>::iterator iter = meshes.begin(); iter != meshes.end(); iter++)
 630   {
 631      TSMesh *mesh = *iter;
 632      if (!mesh ||
 633         (mesh->getMeshType() != TSMesh::StandardMeshType &&
 634            mesh->getMeshType() != TSMesh::SkinMeshType))
 635         continue;
 636
 637      destIndices += mesh->mIndices.size();
 638      destPrims += mesh->mPrimitives.size();
 639   }
 640
 641   // For HW skinning we can just use the static buffer
 642   if (TSShape::smUseHardwareSkinning)
 643   {
 644      getVertexBuffer(mShapeVertexBuffer, GFXBufferTypeStatic);
 645   }
 646
 647   // Also the IBO
 648   mShapeVertexIndices.set(GFX, destIndices, destPrims, GFXBufferTypeStatic);
 649   U16 *indicesStart = NULL;
 650   mShapeVertexIndices.lock(&indicesStart, NULL);
 651   U16 *ibIndices = indicesStart;
 652   GFXPrimitive *piInput = mShapeVertexIndices->mPrimitiveArray;
 653   U32 vertStart = 0;
 654   U32 primStart = 0;
 655   U32 indStart = 0;
 656
 657   // Create VBO
 658   for (Vector<TSMesh*>::iterator iter = meshes.begin(); iter != meshes.end(); iter++)
 659   {
 660      TSMesh *mesh = *iter;
 661      if (!mesh ||
 662         (mesh->getMeshType() != TSMesh::StandardMeshType &&
 663            mesh->getMeshType() != TSMesh::SkinMeshType))
 664         continue;
 665
 666      // Make the offset vbo
 667      mesh->mPrimBufferOffset = primStart;
 668
 669      // Dump primitives to locked buffer
 670      mesh->dumpPrimitives(vertStart, indStart, piInput, ibIndices);
 671
 672      AssertFatal(mesh->mVertOffset / mVertexSize == vertStart, "offset mismatch");
 673
 674      vertStart += mesh->mNumVerts;
 675      primStart += mesh->mPrimitives.size();
 676      indStart += mesh->mIndices.size();
 677
 678      mesh->mVB = mShapeVertexBuffer;
 679      mesh->mPB = mShapeVertexIndices;
 680
 681      // Advance
 682      piInput += mesh->mPrimitives.size();
 683      ibIndices += mesh->mIndices.size();
 684
 685      if (TSSkinMesh::smDebugSkinVerts && mesh->getMeshType() == TSMesh::SkinMeshType)
 686      {
 687         static_cast<TSSkinMesh*>(mesh)->printVerts();
 688      }
 689   }
 690
 691#ifdef TORQUE_DEBUG
 692   // Verify prims
 693   if (TSSkinMesh::smDebugSkinVerts)
 694   {
 695      U32 vertsInBuffer = mShapeVertexData.size / mVertexSize;
 696      U32 indsInBuffer = ibIndices - indicesStart;
 697
 698      for (U32 primID = 0; primID < primStart; primID++)
 699      {
 700         GFXPrimitive &prim = mShapeVertexIndices->mPrimitiveArray[primID];
 701
 702         if (prim.type != GFXTriangleList && prim.type != GFXTriangleStrip)
 703         {
 704            AssertFatal(false, "Unexpected triangle list");
 705         }
 706
 707         if (prim.type == GFXTriangleStrip)
 708            continue;
 709
 710         AssertFatal(prim.startVertex < vertsInBuffer, "wrong start vertex");
 711         AssertFatal((prim.startVertex + prim.numVertices) <= vertsInBuffer, "too many verts");
 712         AssertFatal(prim.startIndex + (prim.numPrimitives * 3) <= indsInBuffer, "too many inds");
 713
 714         for (U32 i = prim.startIndex; i < prim.startIndex + (prim.numPrimitives * 3); i++)
 715         {
 716            if (indicesStart[i] >= vertsInBuffer)
 717            {
 718               AssertFatal(false, "vert not in buffer");
 719            }
 720            U16 idx = indicesStart[i];
 721            if (idx < prim.minIndex)
 722            {
 723               AssertFatal(false, "index out of minIndex range");
 724            }
 725         }
 726      }
 727   }
 728#endif
 729
 730   mShapeVertexIndices.unlock();
 731}
 732
 733void TSShape::getVertexBuffer(TSVertexBufferHandle &vb, GFXBufferType bufferType)
 734{
 735   vb.set(GFX, mVertexSize, &mVertexFormat, mShapeVertexData.size / mVertexSize, bufferType);
 736
 737   U8 *vertPtr = vb.lock();
 738   dMemcpy(vertPtr, mShapeVertexData.base, mShapeVertexData.size);
 739   vb.unlock();
 740}
 741
 742void TSShape::initVertexBufferPointers()
 743{
 744   if (mBasicVertexFormat.vertexSize == -1)
 745      return;
 746   AssertFatal(mVertexSize == mBasicVertexFormat.vertexSize, "vertex size mismatch");
 747
 748   for (Vector<TSMesh*>::iterator iter = meshes.begin(); iter != meshes.end(); iter++)
 749   {
 750      TSMesh *mesh = *iter;
 751      if (mesh &&
 752         (mesh->getMeshType() == TSMesh::StandardMeshType ||
 753            mesh->getMeshType() == TSMesh::SkinMeshType))
 754      {
 755         // Set buffer
 756         AssertFatal(mesh->mNumVerts == 0 || mesh->mNumVerts >= mesh->vertsPerFrame, "invalid verts per frame");
 757         if (mesh->mVertSize > 0 && !mesh->mVertexData.isReady())
 758         {
 759            U32 boneOffset = 0;
 760            U32 texCoordOffset = 0;
 761            AssertFatal(mesh->mVertSize == mVertexFormat.getSizeInBytes(), "mismatch in format size");
 762
 763            if (mBasicVertexFormat.boneOffset >= 0)
 764            {
 765               boneOffset = mBasicVertexFormat.boneOffset;
 766            }
 767
 768            if (mBasicVertexFormat.texCoordOffset >= 0)
 769            {
 770               texCoordOffset = mBasicVertexFormat.texCoordOffset;
 771            }
 772
 773            // Initialize the vertex data
 774            mesh->mVertexData.set(mShapeVertexData.base + mesh->mVertOffset, mesh->mVertSize, mesh->mNumVerts, texCoordOffset, boneOffset, false);
 775            mesh->mVertexData.setReady(true);
 776         }
 777      }
 778   }
 779}
 780
 781void TSShape::initVertexFeatures()
 782{
 783
 784   if (!needsBufferUpdate())
 785   {
 786      // Init format from basic format
 787      mVertexFormat.clear();
 788      mBasicVertexFormat.getFormat(mVertexFormat);
 789      mVertexSize = mVertexFormat.getSizeInBytes();
 790
 791      initVertexBufferPointers();
 792
 793      for (Vector<TSMesh*>::iterator iter = meshes.begin(); iter != meshes.end(); iter++)
 794      {
 795         TSMesh *mesh = *iter;
 796         if (mesh &&
 797            (mesh->getMeshType() == TSMesh::SkinMeshType))
 798         {
 799            static_cast<TSSkinMesh*>(mesh)->createSkinBatchData();
 800         }
 801      }
 802
 803      // Make sure VBO is init'd
 804      initVertexBuffers();
 805      return;
 806   }
 807
 808   // Cleanout VBO
 809   mShapeVertexBuffer = NULL;
 810
 811   // Make sure mesh has verts stored in mesh data, we're recreating the buffer
 812   TSBasicVertexFormat basicFormat;
 813   
 814   initVertexBufferPointers();
 815
 816   for (Vector<TSMesh*>::iterator iter = meshes.begin(); iter != meshes.end(); iter++)
 817   {
 818      TSMesh *mesh = *iter;
 819      if (mesh &&
 820         (mesh->getMeshType() == TSMesh::StandardMeshType ||
 821            mesh->getMeshType() == TSMesh::SkinMeshType))
 822      {
 823         // Make sure we have everything in the vert lists
 824         mesh->makeEditable();
 825
 826         // We need the skin batching data here to determine bone counts
 827         if (mesh->getMeshType() == TSMesh::SkinMeshType)
 828         {
 829            static_cast<TSSkinMesh*>(mesh)->createSkinBatchData();
 830         }
 831
 832         basicFormat.addMeshRequirements(mesh);
 833      }
 834   }
 835
 836   mVertexFormat.clear();
 837   mBasicVertexFormat = basicFormat;
 838   mBasicVertexFormat.getFormat(mVertexFormat);
 839   mBasicVertexFormat.vertexSize = mVertexFormat.getSizeInBytes();
 840   mVertexSize = mBasicVertexFormat.vertexSize;
 841
 842   U32 destVertex = 0;
 843   U32 destIndices = 0;
 844
 845   // Go fix up meshes to include defaults for optional features
 846   // and initialize them if they're not a skin mesh.
 847   U32 count = 0;
 848   for (Vector<TSMesh*>::iterator iter = meshes.begin(); iter != meshes.end(); iter++)
 849   {
 850      TSMesh *mesh = *iter;
 851      if (!mesh ||
 852         (mesh->getMeshType() != TSMesh::StandardMeshType &&
 853            mesh->getMeshType() != TSMesh::SkinMeshType))
 854         continue;
 855
 856      mesh->mVertSize = mVertexSize;
 857      mesh->mVertOffset = destVertex;
 858
 859      destVertex += mesh->mVertSize * mesh->getNumVerts();
 860      destIndices += mesh->mIndices.size();
 861
 862      count += 1;
 863   }
 864
 865   // Don't set up if we have no meshes
 866   if (count == 0)
 867   {
 868      mShapeVertexData.set(NULL, 0);
 869      mShapeVertexData.vertexDataReady = false;
 870      return;
 871   }
 872
 873   // Now we can create the VBO
 874   mShapeVertexData.set(NULL, 0);
 875   U8 *vertexData = (U8*)dMalloc_aligned(destVertex, 16);
 876   U8 *vertexDataPtr = vertexData;
 877   mShapeVertexData.set(vertexData, destVertex);
 878
 879   // Create VBO
 880   for (Vector<TSMesh*>::iterator iter = meshes.begin(); iter != meshes.end(); iter++)
 881   {
 882      TSMesh *mesh = *iter;
 883
 884      if (!mesh ||
 885         (mesh->getMeshType() != TSMesh::StandardMeshType &&
 886            mesh->getMeshType() != TSMesh::SkinMeshType))
 887         continue;
 888
 889      U32 boneOffset = 0;
 890      U32 texCoordOffset = 0;
 891      AssertFatal(mesh->mVertSize == mVertexFormat.getSizeInBytes(), "mismatch in format size");
 892
 893      if (mBasicVertexFormat.boneOffset >= 0)
 894      {
 895         boneOffset = mBasicVertexFormat.boneOffset;
 896      }
 897
 898      if (mBasicVertexFormat.texCoordOffset >= 0)
 899      {
 900         texCoordOffset = mBasicVertexFormat.texCoordOffset;
 901      }
 902
 903      // Dump everything
 904      mesh->mVertexData.setReady(false);
 905      mesh->mVertSize = mVertexSize;
 906      AssertFatal(mesh->mVertOffset == vertexDataPtr - vertexData, "vertex offset mismatch");
 907      mesh->mNumVerts = mesh->getNumVerts();
 908
 909      // Correct bad meshes
 910      if (mesh->mNumVerts != 0 && mesh->vertsPerFrame > mesh->mNumVerts)
 911      {
 912         Con::warnf("Shape mesh has bad vertsPerFrame (%i, should be <= %i)", mesh->vertsPerFrame, mesh->mNumVerts);
 913         mesh->vertsPerFrame = mesh->mNumVerts;
 914      }
 915
 916      mesh->mVertexData.set(mShapeVertexData.base + mesh->mVertOffset, mesh->mVertSize, mesh->mNumVerts, texCoordOffset, boneOffset, false);
 917      mesh->convertToVertexData();
 918      mesh->mVertexData.setReady(true);
 919
 920#ifdef TORQUE_DEBUG
 921      AssertFatal(mesh->mNumVerts == mesh->mVerts.size(), "vert mismatch");
 922      for (U32 i = 0; i < mesh->mNumVerts; i++)
 923      {
 924         Point3F v1 = mesh->mVerts[i];
 925         Point3F v2 = mesh->mVertexData.getBase(i).vert();
 926         AssertFatal(mesh->mVerts[i] == mesh->mVertexData.getBase(i).vert(), "vert data mismatch");
 927      }
 928
 929      if (mesh->getMeshType() == TSMesh::SkinMeshType)
 930      {
 931         AssertFatal(mesh->getMaxBonesPerVert() != 0, "Skin mesh has no bones used, very strange!");
 932      }
 933#endif
 934
 935      // Advance
 936      vertexDataPtr += mesh->mVertSize * mesh->mNumVerts;
 937
 938      AssertFatal(vertexDataPtr - vertexData <= destVertex, "Vertex data overflow");
 939   }
 940
 941   mShapeVertexData.vertexDataReady = true;
 942
 943   initVertexBuffers();
 944}
 945
 946void TSShape::setupBillboardDetails( const String &cachePath )
 947{
 948   // set up billboard details -- only do this once, meaning that
 949   // if we add a sequence to the shape we don't redo the billboard
 950   // details...
 951   if ( !billboardDetails.empty() )
 952      return;
 953
 954   for ( U32 i=0; i < details.size(); i++ )
 955   {
 956      const Detail &det = details[i];
 957
 958      if ( det.subShapeNum >= 0 )
 959         continue; // not a billboard detail
 960
 961      while (billboardDetails.size() <= i )
 962         billboardDetails.push_back(NULL);
 963
 964      billboardDetails[i] = new TSLastDetail(   this,
 965                                                cachePath,
 966                                                det.bbEquatorSteps,
 967                                                det.bbPolarSteps,
 968                                                det.bbPolarAngle,
 969                                                det.bbIncludePoles,
 970                                                det.bbDetailLevel,
 971                                                det.bbDimension );
 972
 973      billboardDetails[i]->update();
 974   }
 975}
 976
 977void TSShape::initMaterialList()
 978{
 979   S32 numSubShapes = subShapeFirstObject.size();
 980   #if defined(TORQUE_MAX_LIB)
 981   subShapeFirstTranslucentObject.setSize(numSubShapes);
 982   #endif
 983
 984   S32 i,j,k;
 985   // for each subshape, find the first translucent object
 986   // also, while we're at it, set mHasTranslucency
 987   for (S32 ss = 0; ss<numSubShapes; ss++)
 988   {
 989      S32 start = subShapeFirstObject[ss];
 990      S32 end = subShapeNumObjects[ss];
 991      subShapeFirstTranslucentObject[ss] = end;
 992      for (i=start; i<end; i++)
 993      {
 994         // check to see if this object has translucency
 995         Object & obj = objects[i];
 996         for (j=0; j<obj.numMeshes; j++)
 997         {
 998            TSMesh * mesh = meshes[obj.startMeshIndex+j];
 999            if (!mesh)
1000               continue;
1001
1002            for (k=0; k<mesh->mPrimitives.size(); k++)
1003            {
1004               if (mesh->mPrimitives[k].matIndex & TSDrawPrimitive::NoMaterial)
1005                  continue;
1006               S32 flags = materialList->getFlags(mesh->mPrimitives[k].matIndex & TSDrawPrimitive::MaterialMask);
1007               if (flags & TSMaterialList::AuxiliaryMap)
1008                  continue;
1009               if (flags & TSMaterialList::Translucent)
1010               {
1011                  mFlags |= HasTranslucency;
1012                  subShapeFirstTranslucentObject[ss] = i;
1013                  break;
1014               }
1015            }
1016            if (k!=mesh->mPrimitives.size())
1017               break;
1018         }
1019         if (j!=obj.numMeshes)
1020            break;
1021      }
1022      if (i!=<a href="/coding/namespace/namespaceprimbuild/#namespaceprimbuild_1acf3edd784de91bee46e8a68ea08c9532">end</a>)
1023         break;
1024   }
1025
1026}
1027
1028bool TSShape::preloadMaterialList(const Torque::Path &path)
1029{
1030   if (materialList)
1031      materialList->setTextureLookupPath(path.getPath());
1032   return true;
1033}
1034
1035bool TSShape::buildConvexHull(S32 dl) const
1036{
1037   AssertFatal(dl>=0 && dl<details.size(),"TSShape::buildConvexHull: detail out of range");
1038
1039   bool ok = true;
1040
1041   const Detail & detail = details[dl];
1042   S32 ss = detail.subShapeNum;
1043   S32 od = detail.objectDetailNum;
1044
1045   S32 start = subShapeFirstObject[ss];
1046   S32 end   = subShapeNumObjects[ss];
1047   for (S32 i=start; i<end; i++)
1048   {
1049      TSMesh * mesh = meshes[objects[i].startMeshIndex+od];
1050      if (!mesh)
1051         continue;
1052      ok &= mesh->buildConvexHull();
1053   }
1054   return ok;
1055}
1056
1057Vector<MatrixF> gTempNodeTransforms(__FILE__, __LINE__);
1058
1059void TSShape::computeBounds(S32 dl, Box3F & bounds) const
1060{
1061   // if dl==-1, nothing to do
1062   if (dl==-1)
1063      return;
1064
1065   AssertFatal(dl>=0 && dl<details.size(),"TSShapeInstance::computeBounds");
1066
1067   // get subshape and object detail
1068   const TSDetail * detail = &details[dl];
1069   S32 ss = detail->subShapeNum;
1070   S32 od = detail->objectDetailNum;
1071
1072   // If we have no subshapes then there is
1073   // no valid bounds for this detail level.
1074   if ( ss < 0 )
1075      return;
1076
1077   // set up temporary storage for non-local transforms...
1078   S32 i;
1079   S32 start = subShapeFirstNode[ss];
1080   S32 end   = subShapeNumNodes[ss] + start;
1081   gTempNodeTransforms.setSize(end-start);
1082   for (i=start; i<end; i++)
1083   {
1084      MatrixF mat;
1085      QuatF q;
1086      TSTransform::setMatrix(defaultRotations[i].getQuatF(&q),defaultTranslations[i],&mat);
1087      if (nodes[i].parentIndex>=0)
1088         gTempNodeTransforms[i-start].mul(gTempNodeTransforms[nodes[i].parentIndex-start],mat);
1089      else
1090         gTempNodeTransforms[i-start] = mat;
1091   }
1092
1093   // run through objects and updating bounds as we go
1094   bounds.minExtents.set( 10E30f, 10E30f, 10E30f);
1095   bounds.maxExtents.set(-10E30f,-10E30f,-10E30f);
1096   Box3F box;
1097   start = subShapeFirstObject[ss];
1098   end   = subShapeNumObjects[ss] + start;
1099   for (i=start; i<end; i++)
1100   {
1101      const Object * object = &objects[i];
1102      TSMesh * mesh = od<object->numMeshes ? meshes[object->startMeshIndex+od] : NULL;
1103      if (mesh)
1104      {
1105         static MatrixF idMat(true);
1106         if (object->nodeIndex<0)
1107            mesh->computeBounds(idMat,box);
1108         else
1109            mesh->computeBounds(gTempNodeTransforms[object->nodeIndex-start],box);
1110         bounds.minExtents.setMin(box.minExtents);
1111         bounds.maxExtents.setMax(box.maxExtents);
1112      }
1113   }
1114}
1115
1116TSShapeAlloc TSShape::smTSAlloc;
1117
1118#define tsalloc TSShape::smTSAlloc
1119
1120
1121// messy stuff: check to see if we should "skip" meshNum
1122// this assumes that meshes for a given object are in a row
1123// skipDL is the lowest detail number we keep (i.e., the # of details we skip)
1124bool TSShape::checkSkip(S32 meshNum, S32 & curObject, S32 skipDL)
1125{
1126   if (skipDL==0)
1127      // easy out...
1128      return false;
1129
1130   // skip detail level exists on this subShape
1131   S32 skipSS = details[skipDL].subShapeNum;
1132
1133   if (curObject<objects.size())
1134   {
1135      S32 start = objects[curObject].startMeshIndex;
1136      if (meshNum>=start)
1137      {
1138         // we are either from this object, the next object, or a decal
1139         if (meshNum < start + objects[curObject].numMeshes)
1140         {
1141            // this object...
1142            if (subShapeFirstObject[skipSS]>curObject)
1143               // haven't reached this subshape yet
1144               return true;
1145            if (skipSS+1==<a href="/coding/class/classtsshape/#classtsshape_1ab304b57ca121344cb6b5fb44612ebe94">subShapeFirstObject</a>.size() || curObject<subShapeFirstObject[skipSS+1])
1146               // curObject is on subshape of skip detail...make sure it's after skipDL
1147               return (meshNum-start<details[skipDL].objectDetailNum);
1148            // if we get here, then curObject occurs on subShape after skip detail (so keep it)
1149            return false;
1150         }
1151         else
1152            // advance object, try again
1153            return checkSkip(meshNum,++curObject,skipDL);
1154      }
1155   }
1156
1157   AssertFatal(0,"TSShape::checkSkip: assertion failed");
1158   return false;
1159}
1160
1161void TSShape::assembleShape()
1162{
1163   S32 i,j;
1164
1165   // get counts...
1166   S32 numNodes = tsalloc.get32();
1167   S32 numObjects = tsalloc.get32();
1168   S32 numDecals = tsalloc.get32();
1169   S32 numSubShapes = tsalloc.get32();
1170   S32 numIflMaterials = tsalloc.get32();
1171   S32 numNodeRots;
1172   S32 numNodeTrans;
1173   S32 numNodeUniformScales;
1174   S32 numNodeAlignedScales;
1175   S32 numNodeArbitraryScales;
1176   if (smReadVersion<22)
1177   {
1178      numNodeRots = numNodeTrans = tsalloc.get32() - numNodes;
1179      numNodeUniformScales = numNodeAlignedScales = numNodeArbitraryScales = 0;
1180   }
1181   else
1182   {
1183      numNodeRots = tsalloc.get32();
1184      numNodeTrans = tsalloc.get32();
1185      numNodeUniformScales = tsalloc.get32();
1186      numNodeAlignedScales = tsalloc.get32();
1187      numNodeArbitraryScales = tsalloc.get32();
1188   }
1189   S32 numGroundFrames = 0;
1190   if (smReadVersion>23)
1191      numGroundFrames = tsalloc.get32();
1192   S32 numObjectStates = tsalloc.get32();
1193   S32 numDecalStates = tsalloc.get32();
1194   S32 numTriggers = tsalloc.get32();
1195   S32 numDetails = tsalloc.get32();
1196   S32 numMeshes = tsalloc.get32();
1197   S32 numSkins = 0;
1198   if (smReadVersion<23)
1199      // in later versions, skins are kept with other meshes
1200      numSkins = tsalloc.get32();
1201   S32 numNames = tsalloc.get32();
1202
1203   // Note that we are recalculating these values later on for safety.
1204   mSmallestVisibleSize = (F32)tsalloc.get32();
1205   mSmallestVisibleDL   = tsalloc.get32();
1206   
1207   tsalloc.checkGuard();
1208
1209   // get bounds...
1210   tsalloc.get32((S32*)&mRadius,1);
1211   tsalloc.get32((S32*)&tubeRadius,1);
1212   tsalloc.get32((S32*)&center,3);
1213   tsalloc.get32((S32*)&mBounds,6);
1214
1215   tsalloc.checkGuard();
1216
1217   // copy various vectors...
1218   S32 * ptr32 = tsalloc.copyToShape32(numNodes*5);
1219   nodes.set(ptr32,numNodes);
1220
1221   tsalloc.checkGuard();
1222
1223   ptr32 = tsalloc.copyToShape32(numObjects*6,true);
1224   if (!ptr32)
1225      ptr32 = tsalloc.allocShape32(numSkins*6); // pre v23 shapes store skins and meshes separately...no longer
1226   else
1227      tsalloc.allocShape32(numSkins*6);
1228   objects.set(ptr32,numObjects);
1229
1230   tsalloc.checkGuard();
1231
1232   // DEPRECATED decals
1233   ptr32 = tsalloc.getPointer32(numDecals*5);
1234
1235   tsalloc.checkGuard();
1236
1237   // DEPRECATED ifl materials
1238   ptr32 = tsalloc.copyToShape32(numIflMaterials*5);
1239
1240   tsalloc.checkGuard();
1241
1242   ptr32 = tsalloc.copyToShape32(numSubShapes,true);
1243   subShapeFirstNode.set(ptr32,numSubShapes);
1244   ptr32 = tsalloc.copyToShape32(numSubShapes,true);
1245   subShapeFirstObject.set(ptr32,numSubShapes);
1246   // DEPRECATED subShapeFirstDecal
1247   ptr32 = tsalloc.getPointer32(numSubShapes);
1248
1249   tsalloc.checkGuard();
1250
1251   ptr32 = tsalloc.copyToShape32(numSubShapes);
1252   subShapeNumNodes.set(ptr32,numSubShapes);
1253   ptr32 = tsalloc.copyToShape32(numSubShapes);
1254   subShapeNumObjects.set(ptr32,numSubShapes);
1255   // DEPRECATED subShapeNumDecals
1256   ptr32 = tsalloc.getPointer32(numSubShapes);
1257
1258   tsalloc.checkGuard();
1259
1260   ptr32 = tsalloc.allocShape32(numSubShapes);
1261   subShapeFirstTranslucentObject.set(ptr32,numSubShapes);
1262
1263   // get default translation and rotation
1264   S16 * ptr16 = tsalloc.allocShape16(0);
1265   for (i=0;i<numNodes;i++)
1266      tsalloc.copyToShape16(4);
1267   defaultRotations.set(ptr16,numNodes);
1268   tsalloc.align32();
1269   ptr32 = tsalloc.allocShape32(0);
1270   for (i=0;i<numNodes;i++)
1271   {
1272      tsalloc.copyToShape32(3);
1273      tsalloc.copyToShape32(sizeof(Point3F)-12); // handle alignment issues w/ point3f
1274   }
1275   defaultTranslations.set(ptr32,numNodes);
1276
1277   // get any node sequence data stored in shape
1278   nodeTranslations.setSize(numNodeTrans);
1279   for (i=0;i<numNodeTrans;i++)
1280      tsalloc.get32((S32*)&nodeTranslations[i],3);
1281   nodeRotations.setSize(numNodeRots);
1282   for (i=0;i<numNodeRots;i++)
1283      tsalloc.get16((S16*)&nodeRotations[i],4);
1284   tsalloc.align32();
1285
1286   tsalloc.checkGuard();
1287
1288   if (smReadVersion>21)
1289   {
1290      // more node sequence data...scale
1291      nodeUniformScales.setSize(numNodeUniformScales);
1292      for (i=0;i<numNodeUniformScales;i++)
1293         tsalloc.get32((S32*)&nodeUniformScales[i],1);
1294      nodeAlignedScales.setSize(numNodeAlignedScales);
1295      for (i=0;i<numNodeAlignedScales;i++)
1296         tsalloc.get32((S32*)&nodeAlignedScales[i],3);
1297      nodeArbitraryScaleFactors.setSize(numNodeArbitraryScales);
1298      for (i=0;i<numNodeArbitraryScales;i++)
1299         tsalloc.get32((S32*)&nodeArbitraryScaleFactors[i],3);
1300      nodeArbitraryScaleRots.setSize(numNodeArbitraryScales);
1301      for (i=0;i<numNodeArbitraryScales;i++)
1302         tsalloc.get16((S16*)&nodeArbitraryScaleRots[i],4);
1303      tsalloc.align32();
1304
1305      tsalloc.checkGuard();
1306   }
1307
1308   // old shapes need ground transforms moved to ground arrays...but only do it once
1309   if (smReadVersion<22 && tsalloc.allocShape32(0))
1310   {
1311      for (i=0; i<sequences.size(); i++)
1312      {
1313         // move ground transform data to ground vectors
1314         Sequence & seq = sequences[i];
1315         S32 oldSz = groundTranslations.size();
1316         groundTranslations.setSize(oldSz+seq.numGroundFrames);
1317         groundRotations.setSize(oldSz+seq.numGroundFrames);
1318         for (S32 groundFrm =0; groundFrm<seq.numGroundFrames; groundFrm++)
1319         {
1320            groundTranslations[groundFrm +oldSz] = nodeTranslations[seq.firstGroundFrame+ groundFrm -numNodes];
1321            groundRotations[groundFrm +oldSz] = nodeRotations[seq.firstGroundFrame+ groundFrm -numNodes];
1322         }
1323         seq.firstGroundFrame = oldSz;
1324         seq.baseTranslation -= numNodes;
1325         seq.baseRotation -= numNodes;
1326         seq.baseScale = 0; // not used on older shapes...but keep it clean
1327      }
1328   }
1329
1330   // version 22 & 23 shapes accidentally had no ground transforms, and ground for
1331   // earlier shapes is handled just above, so...
1332   if (smReadVersion>23)
1333   {
1334      groundTranslations.setSize(numGroundFrames);
1335      for (i=0;i<numGroundFrames;i++)
1336         tsalloc.get32((S32*)&groundTranslations[i],3);
1337      groundRotations.setSize(numGroundFrames);
1338      for (i=0;i<numGroundFrames;i++)
1339         tsalloc.get16((S16*)&groundRotations[i],4);
1340      tsalloc.align32();
1341
1342      tsalloc.checkGuard();
1343   }
1344
1345   // object states
1346   ptr32 = tsalloc.copyToShape32(numObjectStates*3);
1347   objectStates.set(ptr32,numObjectStates);
1348   tsalloc.allocShape32(numSkins*3); // provide buffer after objectStates for older shapes
1349
1350   tsalloc.checkGuard();
1351
1352   // DEPRECATED decal states
1353   ptr32 = tsalloc.getPointer32(numDecalStates);
1354
1355   tsalloc.checkGuard();
1356
1357   // frame triggers
1358   ptr32 = tsalloc.getPointer32(numTriggers*2);
1359   triggers.setSize(numTriggers);
1360   dMemcpy(triggers.address(),ptr32,sizeof(S32)*numTriggers*2);
1361
1362   tsalloc.checkGuard();
1363
1364   // details
1365   if ( smReadVersion >= 26 )
1366   {
1367      U32 alignedSize32 = sizeof( Detail ) / 4;
1368      ptr32 = tsalloc.copyToShape32( numDetails * alignedSize32, true );
1369      details.set( ptr32, numDetails );
1370   }
1371   else
1372   {
1373      // Previous to version 26 the Detail structure
1374      // only contained the first 7 values...
1375      //
1376      //    struct Detail
1377      //    {
1378      //       S32 nameIndex;
1379      //       S32 subShapeNum;
1380      //       S32 objectDetailNum;
1381      //       F32 size;
1382      //       F32 averageError;
1383      //       F32 maxError;
1384      //       S32 polyCount;
1385      //    };
1386      //
1387      // In the code below we're reading just these 7 values and
1388      // copying them to the new larger structure.
1389
1390      ptr32 = tsalloc.copyToShape32( numDetails * 7, true );
1391
1392      details.setSize( numDetails );
1393      for ( U32 detID = 0; detID < details.size(); detID++, ptr32 += 7 )
1394      {
1395         Detail *det = &(details[detID]);
1396
1397         // Clear the struct... we don't want to leave 
1398         // garbage in the parts that are unfilled.
1399         U32 alignedSize32 = sizeof( Detail );
1400         dMemset( det, 0, alignedSize32 );
1401
1402         // Copy the old struct values over.
1403         dMemcpy( det, ptr32, 7 * 4 );
1404
1405         // If this is an autobillboard then we need to
1406         // fill in the new part of the struct.
1407         if ( det->subShapeNum >= 0 )
1408            continue; 
1409
1410         S32 lastDetailOpts = det->objectDetailNum;
1411         det->bbEquatorSteps = lastDetailOpts & 0x7F; // bits 0..6
1412         det->bbPolarSteps = (lastDetailOpts >> 7) & 0x3F; // bits 7..12
1413         det->bbPolarAngle = 0.5f * M_PI_F * (1.0f/64.0f) * (F32) (( lastDetailOpts >>13 ) & 0x3F); // bits 13..18
1414         det->bbDetailLevel = (lastDetailOpts >> 19) & 0x0F;  // 19..22
1415         det->bbDimension = (lastDetailOpts >> 23) & 0xFF; // 23..30
1416         det->bbIncludePoles = (lastDetailOpts & 0x80000000)!=0; // bit 31
1417      }
1418   }
1419
1420   // Some DTS exporters (MAX - I'm looking at you!) write garbage into the
1421   // averageError and maxError values which stops LOD from working correctly.
1422   // Try to detect and fix it
1423   for ( U32 erID = 0; erID < details.size(); erID++ )
1424   {
1425      if ( ( details[erID].averageError == 0 ) || ( details[erID].averageError > 10000 ) ||
1426           ( details[erID].maxError == 0 ) || ( details[erID].maxError > 10000 ) )
1427      {
1428         details[erID].averageError = details[erID].maxError = -1.0f;
1429      }
1430   }
1431
1432   // We don't trust the value of mSmallestVisibleDL loaded from the dts
1433   // since some legacy meshes seem to have the wrong value. Recalculate it
1434   // now that we have the details loaded.
1435   updateSmallestVisibleDL();
1436
1437   S32 skipDL = getMin(mSmallestVisibleDL,smNumSkipLoadDetails);
1438   if (skipDL < 0)
1439      skipDL = 0;
1440
1441
1442   tsalloc.checkGuard();
1443
1444   if (TSShape::smReadVersion >= 27)
1445   {
1446      // Vertex format is set here
1447      S8 *vboData = NULL;
1448      S32 vboSize = 0;
1449
1450      mBasicVertexFormat.readAlloc(&tsalloc);
1451      mVertexFormat.clear();
1452      mBasicVertexFormat.getFormat(mVertexFormat);
1453      mVertexSize = mVertexFormat.getSizeInBytes();
1454
1455      AssertFatal(mVertexSize == mBasicVertexFormat.vertexSize, "vertex size mismatch");
1456
1457      vboSize = tsalloc.get32();
1458      vboData = tsalloc.getPointer8(vboSize);
1459
1460      if (tsalloc.getBuffer() && vboSize > 0)
1461      {
1462         U8 *vertexData = (U8*)dMalloc_aligned(vboSize, 16);
1463         dMemcpy(vertexData, vboData, vboSize);
1464         mShapeVertexData.set(vertexData, vboSize);
1465         mShapeVertexData.vertexDataReady = true;
1466      }
1467      else
1468      {
1469         mShapeVertexData.set(NULL, 0);
1470      }
1471   }
1472   else
1473   {
1474      mShapeVertexData.set(NULL, 0);
1475   }
1476
1477   // about to read in the meshes...first must allocate some scratch space
1478   S32 scratchSize = getMax(numSkins,numMeshes);
1479   TSMesh::smVertsList.setSize(scratchSize);
1480   TSMesh::smTVertsList.setSize(scratchSize);
1481
1482   if ( smReadVersion >= 26 )
1483   {
1484      TSMesh::smTVerts2List.setSize(scratchSize);
1485      TSMesh::smColorsList.setSize(scratchSize);
1486   }
1487
1488   TSMesh::smNormsList.setSize(scratchSize);
1489   TSMesh::smEncodedNormsList.setSize(scratchSize);
1490   TSMesh::smDataCopied.setSize(scratchSize);
1491   TSSkinMesh::smInitTransformList.setSize(scratchSize);
1492   TSSkinMesh::smVertexIndexList.setSize(scratchSize);
1493   TSSkinMesh::smBoneIndexList.setSize(scratchSize);
1494   TSSkinMesh::smWeightList.setSize(scratchSize);
1495   TSSkinMesh::smNodeIndexList.setSize(scratchSize);
1496   for (i=0; i<numMeshes; i++)
1497   {
1498      TSMesh::smVertsList[i]=<a href="/coding/file/types_8lint_8h/#types_8lint_8h_1a070d2ce7b6bb7e5c05602aa8c308d0c4">NULL</a>;
1499      TSMesh::smTVertsList[i]=<a href="/coding/file/types_8lint_8h/#types_8lint_8h_1a070d2ce7b6bb7e5c05602aa8c308d0c4">NULL</a>;
1500      
1501      if ( smReadVersion >= 26 )
1502      {
1503         TSMesh::smTVerts2List[i] = NULL;
1504         TSMesh::smColorsList[i] = NULL;
1505      }
1506      
1507      TSMesh::smNormsList[i]=<a href="/coding/file/types_8lint_8h/#types_8lint_8h_1a070d2ce7b6bb7e5c05602aa8c308d0c4">NULL</a>;
1508      TSMesh::smEncodedNormsList[i]=<a href="/coding/file/types_8lint_8h/#types_8lint_8h_1a070d2ce7b6bb7e5c05602aa8c308d0c4">NULL</a>;
1509      TSMesh::smDataCopied[i]=false;
1510      TSSkinMesh::smInitTransformList[i] = NULL;
1511      TSSkinMesh::smVertexIndexList[i] = NULL;
1512      TSSkinMesh::smBoneIndexList[i] = NULL;
1513      TSSkinMesh::smWeightList[i] = NULL;
1514      TSSkinMesh::smNodeIndexList[i] = NULL;
1515   }
1516
1517   // read in the meshes (sans skins)...straightforward read one at a time
1518   TSMesh **ptrmesh = (TSMesh**)tsalloc.allocShape32((numMeshes + numSkins*numDetails) * (sizeof(TSMesh*) / 4));
1519   S32 curObject = 0; // for tracking skipped meshes
1520   for (i=0; i<numMeshes; i++)
1521   {
1522      bool skip = checkSkip(i,curObject,skipDL); // skip this mesh?
1523      S32 meshType = tsalloc.get32();
1524      if (meshType == TSMesh::DecalMeshType)
1525         // decal mesh deprecated
1526         skip = true;
1527      TSMesh * mesh = TSMesh::assembleMesh(meshType,skip);
1528      if (ptrmesh)
1529      {
1530         ptrmesh[i] = skip ?  0 : mesh;
1531      }
1532
1533      // fill in location of verts, tverts, and normals for detail levels
1534      if (mesh && meshType!=<a href="/coding/class/classtsmesh/#classtsmesh_1abecf9557a0f4b4a582f2cae15eff21bfa20c73022bcf281f02273d7d6d770e482">TSMesh::DecalMeshType</a>)
1535      {
1536         TSMesh::smVertsList[i]  = mesh->mVerts.address();
1537         TSMesh::smTVertsList[i] = mesh->mTverts.address();
1538         if (smReadVersion >= 26)
1539         {
1540            TSMesh::smTVerts2List[i] = mesh->mTverts2.address();
1541            TSMesh::smColorsList[i] = mesh->mColors.address();
1542         }
1543         TSMesh::smNormsList[i]  = mesh->mNorms.address();
1544         TSMesh::smEncodedNormsList[i] = mesh->mEncodedNorms.address();
1545         TSMesh::smDataCopied[i] = !skip; // as long as we didn't skip this mesh, the data should be in shape now
1546         if (meshType==<a href="/coding/class/classtsmesh/#classtsmesh_1abecf9557a0f4b4a582f2cae15eff21bfa4e7301620099d7cf3438bea494da25e4">TSMesh::SkinMeshType</a>)
1547         {
1548            TSSkinMesh * skin = (TSSkinMesh*)mesh;
1549            TSMesh::smVertsList[i]  = skin->batchData.initialVerts.address();
1550            TSMesh::smNormsList[i]  = skin->batchData.initialNorms.address();
1551            TSSkinMesh::smInitTransformList[i] = skin->batchData.initialTransforms.address();
1552            TSSkinMesh::smVertexIndexList[i] = skin->vertexIndex.address();
1553            TSSkinMesh::smBoneIndexList[i] = skin->boneIndex.address();
1554            TSSkinMesh::smWeightList[i] = skin->weight.address();
1555            TSSkinMesh::smNodeIndexList[i] = skin->batchData.nodeIndex.address();
1556         }
1557      }
1558   }
1559   meshes.set(ptrmesh, numMeshes);
1560
1561   tsalloc.checkGuard();
1562
1563   // names
1564   char * nameBufferStart = (char*)tsalloc.getPointer8(0);
1565   char * name = nameBufferStart;
1566   S32 nameBufferSize = 0;
1567   names.setSize(numNames);
1568   for (i=0; i<numNames; i++)
1569   {
1570      for (j=0; name[j]; j++)
1571         ;
1572
1573      names[i] = name;
1574      nameBufferSize += j + 1;
1575      name += j + 1;
1576   }
1577
1578   tsalloc.getPointer8(nameBufferSize);
1579   tsalloc.align32();
1580
1581   tsalloc.checkGuard();
1582
1583   if (smReadVersion<23)
1584   {
1585      // get detail information about skins...
1586      S32 * detFirstSkin = tsalloc.getPointer32(numDetails);
1587      S32 * detailNumSkins = tsalloc.getPointer32(numDetails);
1588
1589      tsalloc.checkGuard();
1590
1591      // about to read in skins...clear out scratch space...
1592      if (numSkins)
1593      {
1594         TSSkinMesh::smInitTransformList.setSize(numSkins);
1595         TSSkinMesh::smVertexIndexList.setSize(numSkins);
1596         TSSkinMesh::smBoneIndexList.setSize(numSkins);
1597         TSSkinMesh::smWeightList.setSize(numSkins);
1598         TSSkinMesh::smNodeIndexList.setSize(numSkins);
1599      }
1600      for (i=0; i<numSkins; i++)
1601      {
1602         TSMesh::smVertsList[i]=<a href="/coding/file/types_8lint_8h/#types_8lint_8h_1a070d2ce7b6bb7e5c05602aa8c308d0c4">NULL</a>;
1603         TSMesh::smTVertsList[i]=<a href="/coding/file/types_8lint_8h/#types_8lint_8h_1a070d2ce7b6bb7e5c05602aa8c308d0c4">NULL</a>;
1604         TSMesh::smNormsList[i]=<a href="/coding/file/types_8lint_8h/#types_8lint_8h_1a070d2ce7b6bb7e5c05602aa8c308d0c4">NULL</a>;
1605         TSMesh::smEncodedNormsList[i]=<a href="/coding/file/types_8lint_8h/#types_8lint_8h_1a070d2ce7b6bb7e5c05602aa8c308d0c4">NULL</a>;
1606         TSMesh::smDataCopied[i]=false;
1607         TSSkinMesh::smInitTransformList[i] = NULL;
1608         TSSkinMesh::smVertexIndexList[i] = NULL;
1609         TSSkinMesh::smBoneIndexList[i] = NULL;
1610         TSSkinMesh::smWeightList[i] = NULL;
1611         TSSkinMesh::smNodeIndexList[i] = NULL;
1612      }
1613
1614      // skins
1615      ptr32 = tsalloc.allocShape32(numSkins);
1616      for (i=0; i<numSkins; i++)
1617      {
1618         bool skip = i<detFirstSkin[skipDL];
1619         TSSkinMesh * skin = (TSSkinMesh*)TSMesh::assembleMesh(TSMesh::SkinMeshType,skip);
1620         if (meshes.address())
1621         {
1622            // add pointer to skin in shapes list of meshes
1623            // we reserved room for this above...
1624            meshes.set(meshes.address(),meshes.size()+1);
1625            meshes[meshes.size()-1] = skip ? NULL : skin;
1626         }
1627
1628         // fill in location of verts, tverts, and normals for shared detail levels
1629         if (skin)
1630         {
1631            TSMesh::smVertsList[i]  = skin->batchData.initialVerts.address();
1632            TSMesh::smTVertsList[i] = skin->mTverts.address();
1633            TSMesh::smNormsList[i]  = skin->batchData.initialNorms.address();
1634            TSMesh::smEncodedNormsList[i]  = skin->mEncodedNorms.address();
1635            TSMesh::smDataCopied[i] = !skip; // as long as we didn't skip this mesh, the data should be in shape now
1636            TSSkinMesh::smInitTransformList[i] = skin->batchData.initialTransforms.address();
1637            TSSkinMesh::smVertexIndexList[i] = skin->vertexIndex.address();
1638            TSSkinMesh::smBoneIndexList[i] = skin->boneIndex.address();
1639            TSSkinMesh::smWeightList[i] = skin->weight.address();
1640            TSSkinMesh::smNodeIndexList[i] = skin->batchData.nodeIndex.address();
1641         }
1642      }
1643
1644      tsalloc.checkGuard();
1645
1646      // we now have skins in mesh list...add skin objects to object list and patch things up
1647      fixupOldSkins(numMeshes,numSkins,numDetails,detFirstSkin,detailNumSkins);
1648   }
1649
1650   // allocate storage space for some arrays (filled in during Shape::init)...
1651   ptr32 = tsalloc.allocShape32(numDetails);
1652   alphaIn.set(ptr32,numDetails);
1653   ptr32 = tsalloc.allocShape32(numDetails);
1654   alphaOut.set(ptr32,numDetails);
1655}
1656
1657void TSShape::disassembleShape()
1658{
1659   S32 i;
1660
1661   // set counts...
1662   S32 numNodes = tsalloc.set32(nodes.size());
1663   S32 numObjects = tsalloc.set32(objects.size());
1664   tsalloc.set32(0); // DEPRECATED decals
1665   S32 numSubShapes = tsalloc.set32(subShapeFirstNode.size());
1666   tsalloc.set32(0); // DEPRECATED ifl materials
1667   S32 numNodeRotations = tsalloc.set32(nodeRotations.size());
1668   S32 numNodeTranslations = tsalloc.set32(nodeTranslations.size());
1669   S32 numNodeUniformScales = tsalloc.set32(nodeUniformScales.size());
1670   S32 numNodeAlignedScales = tsalloc.set32(nodeAlignedScales.size());
1671   S32 numNodeArbitraryScales = tsalloc.set32(nodeArbitraryScaleFactors.size());
1672   S32 numGroundFrames = tsalloc.set32(groundTranslations.size());
1673   S32 numObjectStates = tsalloc.set32(objectStates.size());
1674   tsalloc.set32(0); // DEPRECATED decals
1675   S32 numTriggers = tsalloc.set32(triggers.size());
1676   S32 numDetails = tsalloc.set32(details.size());
1677   S32 numMeshes = tsalloc.set32(meshes.size());
1678   S32 numNames = tsalloc.set32(names.size());
1679   tsalloc.set32((S32)mSmallestVisibleSize);
1680   tsalloc.set32(mSmallestVisibleDL);
1681
1682   tsalloc.setGuard();
1683
1684   // get bounds...
1685   tsalloc.copyToBuffer32((S32*)&mRadius,1);
1686   tsalloc.copyToBuffer32((S32*)&tubeRadius,1);
1687   tsalloc.copyToBuffer32((S32*)&center,3);
1688   tsalloc.copyToBuffer32((S32*)&mBounds,6);
1689
1690   tsalloc.setGuard();
1691
1692   // copy various vectors...
1693   tsalloc.copyToBuffer32((S32*)nodes.address(),numNodes*5);
1694   tsalloc.setGuard();
1695   tsalloc.copyToBuffer32((S32*)objects.address(),numObjects*6);
1696   tsalloc.setGuard();
1697   // DEPRECATED: no copy decals
1698   tsalloc.setGuard();
1699   tsalloc.copyToBuffer32(0,0); // DEPRECATED: ifl materials!
1700   tsalloc.setGuard();
1701   tsalloc.copyToBuffer32((S32*)subShapeFirstNode.address(),numSubShapes);
1702   tsalloc.copyToBuffer32((S32*)subShapeFirstObject.address(),numSubShapes);
1703   tsalloc.copyToBuffer32(0, numSubShapes); // DEPRECATED: no copy subShapeFirstDecal
1704   tsalloc.setGuard();
1705   tsalloc.copyToBuffer32((S32*)subShapeNumNodes.address(),numSubShapes);
1706   tsalloc.copyToBuffer32((S32*)subShapeNumObjects.address(),numSubShapes);
1707   tsalloc.copyToBuffer32(0, numSubShapes); // DEPRECATED: no copy subShapeNumDecals
1708   tsalloc.setGuard();
1709
1710   // default transforms...
1711   tsalloc.copyToBuffer16((S16*)defaultRotations.address(),numNodes*4);
1712   tsalloc.copyToBuffer32((S32*)defaultTranslations.address(),numNodes*3);
1713
1714   // animated transforms...
1715   tsalloc.copyToBuffer16((S16*)nodeRotations.address(),numNodeRotations*4);
1716   tsalloc.copyToBuffer32((S32*)nodeTranslations.address(),numNodeTranslations*3);
1717
1718   tsalloc.setGuard();
1719
1720   // ...with scale
1721   tsalloc.copyToBuffer32((S32*)nodeUniformScales.address(),numNodeUniformScales);
1722   tsalloc.copyToBuffer32((S32*)nodeAlignedScales.address(),numNodeAlignedScales*3);
1723   tsalloc.copyToBuffer32((S32*)nodeArbitraryScaleFactors.address(),numNodeArbitraryScales*3);
1724   tsalloc.copyToBuffer16((S16*)nodeArbitraryScaleRots.address(),numNodeArbitraryScales*4);
1725
1726   tsalloc.setGuard();
1727
1728   tsalloc.copyToBuffer32((S32*)groundTranslations.address(),3*numGroundFrames);
1729   tsalloc.copyToBuffer16((S16*)groundRotations.address(),4*numGroundFrames);
1730
1731   tsalloc.setGuard();
1732
1733   // object states..
1734   tsalloc.copyToBuffer32((S32*)objectStates.address(),numObjectStates*3);
1735   tsalloc.setGuard();
1736
1737   // decal states...
1738   // DEPRECATED (numDecalStates = 0)
1739   tsalloc.setGuard();
1740
1741   // frame triggers
1742   tsalloc.copyToBuffer32((S32*)triggers.address(),numTriggers*2);
1743   tsalloc.setGuard();
1744
1745   // details
1746   if (TSShape::smVersion > 25)
1747   {
1748      U32 alignedSize32 = sizeof( Detail ) / 4;
1749      tsalloc.copyToBuffer32((S32*)details.address(),numDetails * alignedSize32 );
1750   }
1751   else
1752   {
1753      // Legacy details => no explicit autobillboard parameters
1754      U32 legacyDetailSize32 = 7;   // only store the first 7 4-byte values of each detail
1755      for ( S32 bbID = 0; bbID < details.size(); bbID++ )
1756         tsalloc.copyToBuffer32( (S32*)&details[bbID], legacyDetailSize32 );
1757   }
1758   tsalloc.setGuard();
1759
1760   if (TSShape::smVersion >= 27)
1761   {
1762      // Vertex format now included with mesh data. Note this doesn't include index data which
1763      // is constructed directly in the buffer from the meshes
1764
1765      mBasicVertexFormat.writeAlloc(&tsalloc);
1766
1767      tsalloc.set32(mShapeVertexData.size);
1768      tsalloc.copyToBuffer8((S8*)mShapeVertexData.base, mShapeVertexData.size);
1769   }
1770
1771   // read in the meshes (sans skins)...
1772   bool * isMesh = new bool[numMeshes]; // funny business because decals are pretend meshes (legacy issue)
1773   for (i=0;i<numMeshes;i++)
1774      isMesh[i]=false;
1775   for (i=0; i<objects.size(); i++)
1776   {
1777      for (S32 j=0; j<objects[i].numMeshes; j++)
1778         // even if an empty mesh, it's a mesh...
1779         isMesh[objects[i].startMeshIndex+j]=true;
1780   }
1781   for (i=0; i<numMeshes; i++)
1782   {
1783      TSMesh * mesh = NULL;
1784      // decal mesh deprecated
1785      if (isMesh[i])
1786         mesh = meshes[i];
1787      tsalloc.set32( (mesh && mesh->getMeshType() != TSMesh::DecalMeshType) ? mesh->getMeshType() : TSMesh::NullMeshType);
1788      if (mesh)
1789         mesh->disassemble();
1790   }
1791   delete [] isMesh;
1792   tsalloc.setGuard();
1793
1794   // names
1795   for (i=0; i<numNames; i++)
1796      tsalloc.copyToBuffer8((S8 *)(names[i].c_str()),names[i].length()+1);
1797
1798   tsalloc.setGuard();
1799}
1800
1801//-------------------------------------------------
1802// write whole shape
1803//-------------------------------------------------
1804/** Determine whether we can write this shape in TSTPRO compatible format */
1805bool TSShape::canWriteOldFormat() const
1806{
1807   // Cannot use old format if using autobillboard details
1808   for (S32 i = 0; i < details.size(); i++)
1809   {
1810      if (details[i].subShapeNum < 0)
1811         return false;
1812   }
1813
1814   for (S32 i = 0; i < meshes.size(); i++)
1815   {
1816      if (!meshes[i])
1817         continue;
1818
1819      // Cannot use old format if using the new functionality (COLORs, 2nd UV set)
1820      if (meshes[i]->mTverts2.size() || meshes[i]->mColors.size())
1821         return false;
1822
1823      // Cannot use old format if any primitive has too many triangles
1824      // (ie. cannot fit in a S16)
1825      for (S32 j = 0; j < meshes[i]->mPrimitives.size(); j++)
1826      {
1827         if ((meshes[i]->mPrimitives[j].start +
1828               meshes[i]->mPrimitives[j].numElements) >= (1 << 15))
1829         {
1830            return false;
1831         }
1832      }
1833   }
1834
1835   return true;
1836}
1837
1838void TSShape::write(Stream * s, bool saveOldFormat)
1839{
1840   S32 currentVersion = smVersion;
1841   if (saveOldFormat)
1842      smVersion = 24;
1843
1844   // write version
1845   s->write(smVersion | (mExporterVersion<<16));
1846
1847   tsalloc.setWrite();
1848   disassembleShape();
1849
1850   S32     * buffer32 = tsalloc.getBuffer32();
1851   S16     * buffer16 = tsalloc.getBuffer16();
1852   S8      * buffer8  = tsalloc.getBuffer8();
1853
1854   S32 size32 = tsalloc.getBufferSize32();
1855   S32 size16 = tsalloc.getBufferSize16();
1856   S32 size8  = tsalloc.getBufferSize8();
1857
1858   // convert sizes to dwords...
1859   if (size16 & 1)
1860      size16 += 2;
1861   size16 >>= 1;
1862   if (size8 & 3)
1863      size8 += 4;
1864   size8 >>= 2;
1865
1866   S32 sizeMemBuffer, start16, start8;
1867   sizeMemBuffer = size32 + size16 + size8;
1868   start16 = size32;
1869   start8 = start16+size16;
1870
1871   // in dwords -- write will properly endian-flip.
1872   s->write(sizeMemBuffer);
1873   s->write(start16);
1874   s->write(start8);
1875
1876   // endian-flip the entire write buffers.
1877   fixEndian(buffer32,buffer16,buffer8,size32,size16,size8);
1878
1879   // now write buffers
1880   s->write(size32*4,buffer32);
1881   s->write(size16*4,buffer16);
1882   s->write(size8 *4,buffer8);
1883
1884   // write sequences - write will properly endian-flip.
1885   s->write(sequences.size());
1886   for (S32 i=0; i<sequences.size(); i++)
1887      sequences[i].write(s);
1888
1889   // write material list - write will properly endian-flip.
1890   materialList->write(*s);
1891
1892   delete [] buffer32;
1893   delete [] buffer16;
1894   delete [] buffer8;
1895
1896   smVersion = currentVersion;
1897}
1898
1899//-------------------------------------------------
1900// read whole shape
1901//-------------------------------------------------
1902
1903bool TSShape::read(Stream * s)
1904{
1905   // read version - read handles endian-flip
1906   s->read(&smReadVersion);
1907   mExporterVersion = smReadVersion >> 16;
1908   smReadVersion &= 0xFF;
1909   if (smReadVersion>smVersion)
1910   {
1911      // error -- don't support future versions yet :>
1912      Con::errorf(ConsoleLogEntry::General,
1913                  "Error: attempt to load a version %i dts-shape, can currently only load version %i and before.",
1914                   smReadVersion,smVersion);
1915      return false;
1916   }
1917   mReadVersion = smReadVersion;
1918
1919   S32 * memBuffer32;
1920   S16 * memBuffer16;
1921   S8 * memBuffer8;
1922   S32 count32, count16, count8;
1923   if (mReadVersion<19)
1924   {
1925      Con::errorf("... Shape with old version.");
1926      return false;
1927   }
1928   else
1929   {
1930      S32 i;
1931      U32 sizeMemBuffer, startU16, startU8;
1932
1933      // in dwords. - read handles endian-flip
1934      s->read(&sizeMemBuffer);
1935      s->read(&startU16);
1936      s->read(&startU8);
1937
1938      if (s->getStatus()!=<a href="/coding/class/classstream/#classstream_1ab4b87f75f121255ce4e2647941503dd3a7e7659659592dca84c4ff72af19dae16">Stream::Ok</a>)
1939      {
1940         Con::errorf(ConsoleLogEntry::General, "Error: bad shape file.");
1941         return false;
1942      }
1943
1944      S32 * tmp = new S32[sizeMemBuffer];
1945      s->read(sizeof(S32)*sizeMemBuffer,(U8*)tmp);
1946      memBuffer32 = tmp;
1947      memBuffer16 = (S16*)(tmp+startU16);
1948      memBuffer8  = (S8*)(tmp+startU8);
1949
1950      count32 = startU16;
1951      count16 = startU8-startU16;
1952      count8  = sizeMemBuffer-startU8;
1953
1954      // read sequences
1955      S32 numSequences;
1956      s->read(&numSequences);
1957      sequences.setSize(numSequences);
1958      for (i=0; i<numSequences; i++)
1959      {
1960         sequences[i].read(s);
1961
1962         // Store initial (empty) source data
1963         sequences[i].sourceData.total = sequences[i].numKeyframes;
1964         sequences[i].sourceData.end = sequences[i].sourceData.total - 1;
1965      }
1966
1967      // read material list
1968      delete materialList; // just in case...
1969      materialList = new TSMaterialList;
1970      materialList->read(*s);
1971   }
1972
1973   // since we read in the buffers, we need to endian-flip their entire contents...
1974   fixEndian(memBuffer32,memBuffer16,memBuffer8,count32,count16,count8);
1975
1976   tsalloc.setRead(memBuffer32,memBuffer16,memBuffer8,true);
1977   assembleShape(); // determine size of buffer needed
1978   mShapeDataSize = tsalloc.getSize();
1979   tsalloc.doAlloc();
1980   mShapeData = tsalloc.getBuffer();
1981   tsalloc.setRead(memBuffer32,memBuffer16,memBuffer8,false);
1982   assembleShape(); // copy to buffer
1983   AssertFatal(tsalloc.getSize()==<a href="/coding/class/classtsshape/#classtsshape_1aceeb988a62e9fcfbc4bde6bda7e2597e">mShapeDataSize</a>,"TSShape::read: shape data buffer size mis-calculated");
1984
1985   delete [] memBuffer32;
1986
1987   if (smInitOnRead)
1988   {
1989      init();
1990   }
1991
1992   return true;
1993}
1994
1995void TSShape::createEmptyShape()
1996{
1997   nodes.set(dMalloc(1 * sizeof(Node)), 1);
1998      nodes[0].nameIndex = 1;
1999      nodes[0].parentIndex = -1;
2000      nodes[0].firstObject = 0;
2001      nodes[0].firstChild = -1;
2002      nodes[0].nextSibling = -1;
2003
2004   objects.set(dMalloc(1 * sizeof(Object)), 1);
2005      objects[0].nameIndex = 2;
2006      objects[0].numMeshes = 1;
2007      objects[0].startMeshIndex = 0;
2008      objects[0].nodeIndex = 0;
2009      objects[0].nextSibling = -1;
2010      objects[0].firstDecal = -1;
2011
2012   objectStates.set(dMalloc(1 * sizeof(ObjectState)), 1);
2013      objectStates[0].vis = 1;
2014      objectStates[0].frameIndex = 0;
2015      objectStates[0].matFrameIndex = 0;
2016
2017   subShapeFirstNode.set(dMalloc(1 * sizeof(S32)), 1);
2018      subShapeFirstNode[0] = 0;
2019
2020   subShapeFirstObject.set(dMalloc(1 * sizeof(S32)), 1);
2021      subShapeFirstObject[0] = 0;
2022
2023   detailFirstSkin.set(NULL, 0);
2024
2025   subShapeNumNodes.set(dMalloc(1 * sizeof(S32)), 1);
2026      subShapeNumNodes[0] = 1;
2027
2028   subShapeNumObjects.set(dMalloc(1 * sizeof(S32)), 1);
2029      subShapeNumObjects[0] = 1;
2030
2031   details.set(dMalloc(1 * sizeof(Detail)), 1);
2032      details[0].nameIndex = 0;
2033      details[0].subShapeNum = 0;
2034      details[0].objectDetailNum = 0;
2035      details[0].size = 2.0f;
2036      details[0].averageError = -1.0f;
2037      details[0].maxError = -1.0f;
2038      details[0].polyCount = 0;
2039
2040   defaultRotations.set(dMalloc(1 * sizeof(Quat16)), 1);
2041      defaultRotations[0].x = 0.0f;
2042      defaultRotations[0].y = 0.0f;
2043      defaultRotations[0].z = 0.0f;
2044      defaultRotations[0].w = 0.0f;
2045
2046   defaultTranslations.set(dMalloc(1 * sizeof(Point3F)), 1);
2047      defaultTranslations[0].set(0.0f, 0.0f, 0.0f);
2048
2049   subShapeFirstTranslucentObject.set(dMalloc(1 * sizeof(S32)), 1);
2050      subShapeFirstTranslucentObject[0] = 1;
2051
2052   alphaIn.set(dMalloc(1 * sizeof(F32)), 1);
2053      alphaIn[0] = 0;
2054
2055   alphaOut.set(dMalloc(1 * sizeof(F32)), 1);
2056      alphaOut[0] = -1;
2057
2058   sequences.set(NULL, 0);
2059   nodeRotations.set(NULL, 0);
2060   nodeTranslations.set(NULL, 0);
2061   nodeUniformScales.set(NULL, 0);
2062   nodeAlignedScales.set(NULL, 0);
2063   nodeArbitraryScaleRots.set(NULL, 0);
2064   nodeArbitraryScaleFactors.set(NULL, 0);
2065   groundRotations.set(NULL, 0);
2066   groundTranslations.set(NULL, 0);
2067   triggers.set(NULL, 0);
2068   billboardDetails.set(NULL, 0);
2069
2070   names.setSize(3);
2071      names[0] = StringTable->insert("Detail2");
2072      names[1] = StringTable->insert("Mesh2");
2073      names[2] = StringTable->insert("Mesh");
2074
2075   mRadius = 0.866025f;
2076   tubeRadius = 0.707107f;
2077   center.set(0.0f, 0.5f, 0.0f);
2078   mBounds.minExtents.set(-0.5f, 0.0f, -0.5f);
2079   mBounds.maxExtents.set(0.5f, 1.0f, 0.5f);
2080
2081   mExporterVersion = 124;
2082   mSmallestVisibleSize = 2;
2083   mSmallestVisibleDL = 0;
2084   mReadVersion = 24;
2085   mFlags = 0;
2086   mSequencesConstructed = 0;
2087
2088   mUseDetailFromScreenError = false;
2089
2090   mDetailLevelLookup.setSize( 1 );
2091   mDetailLevelLookup[0].set( -1, 0 );
2092
2093   // Init the collision accelerator array.  Note that we don't compute the
2094   //  accelerators until the app requests them
2095   detailCollisionAccelerators.setSize(details.size());
2096   for (U32 i = 0; i < detailCollisionAccelerators.size(); i++)
2097      detailCollisionAccelerators[i] = NULL;
2098}
2099
2100void TSShape::fixEndian(S32 * buff32, S16 * buff16, S8 *, S32 count32, S32 count16, S32)
2101{
2102   // if endian-ness isn't the same, need to flip the buffer contents.
2103   if (0x12345678!=convertLEndianToHost(0x12345678))
2104   {
2105      for (S32 i=0; i<count32; i++)
2106         buff32[i]=convertLEndianToHost(buff32[i]);
2107      for (S32 i=0; i<count16*2; i++)
2108         buff16[i]=convertLEndianToHost(buff16[i]);
2109   }
2110}
2111
2112template<> void *Resource<TSShape>::create(const Torque::Path &path)
2113{
2114   // Execute the shape script if it exists
2115   Torque::Path scriptPath(path);
2116   scriptPath.setExtension(TORQUE_SCRIPT_EXTENSION);
2117
2118   // Don't execute the script if we're already doing so!
2119   StringTableEntry currentScript = Platform::stripBasePath(CodeBlock::getCurrentCodeBlockFullPath());
2120   if (!scriptPath.getFullPath().equal(currentScript))
2121   {
2122      Torque::Path scriptPathDSO(scriptPath);
2123      scriptPathDSO.setExtension(TORQUE_SCRIPT_EXTENSION ".dso");
2124
2125      if (Torque::FS::IsFile(scriptPathDSO) || Torque::FS::IsFile(scriptPath))
2126      {
2127         String evalCmd = "exec(\"" + scriptPath + "\");";
2128
2129         String instantGroup = Con::getVariable("InstantGroup");
2130         Con::setIntVariable("InstantGroup", RootGroupId);
2131         Con::evaluate((const char*)evalCmd.c_str(), false, scriptPath.getFullPath());
2132         Con::setVariable("InstantGroup", instantGroup.c_str());
2133      }
2134   }
2135
2136   // Attempt to load the shape
2137   TSShape * ret = 0;
2138   bool readSuccess = false;
2139   const String extension = path.getExtension();
2140
2141   if ( extension.equal( "dts", String::NoCase ) )
2142   {
2143      FileStream stream;
2144      stream.open( path.getFullPath(), Torque::FS::File::Read );
2145      if ( stream.getStatus() != Stream::Ok )
2146      {
2147         Con::errorf( "Resource<TSShape>::create - Could not open '%s'", path.getFullPath().c_str() );
2148         return NULL;
2149      }
2150
2151      ret = new TSShape;
2152      readSuccess = ret->read(&stream);
2153   }
2154   else if ( extension.equal( "dae", String::NoCase ) || extension.equal( "kmz", String::NoCase ) )
2155   {
2156#ifdef TORQUE_COLLADA
2157      // Attempt to load the DAE file
2158      ret = loadColladaShape(path);
2159      readSuccess = (ret != NULL);
2160#else
2161      // No COLLADA support => attempt to load the cached DTS file instead
2162      Torque::Path cachedPath = path;
2163      cachedPath.setExtension("cached.dts");
2164       
2165      FileStream stream;
2166      stream.open( cachedPath.getFullPath(), Torque::FS::File::Read );
2167      if ( stream.getStatus() != Stream::Ok )
2168      {
2169         Con::errorf( "Resource<TSShape>::create - Could not open '%s'", cachedPath.getFullPath().c_str() );
2170         return NULL;
2171      }
2172      ret = new TSShape;
2173      readSuccess = ret->read(&stream);
2174#endif
2175   }
2176   else
2177   {
2178      //Con::errorf( "Resource<TSShape>::create - '%s' has an unknown file format", path.getFullPath().c_str() );
2179      //delete ret;
2180      //return NULL;
2181
2182      // andrewmac: Open Asset Import Library
2183#ifdef TORQUE_ASSIMP
2184      ret = assimpLoadShape(path);
2185      readSuccess = (ret != NULL);
2186#endif
2187
2188      // andrewmac : I could have used another conditional macro but I think this is suffice:
2189      if (!readSuccess)
2190      {
2191         Con::errorf("Resource<TSShape>::create - '%s' has an unknown file format", path.getFullPath().c_str());
2192         delete ret;
2193         return NULL;
2194      }
2195   }
2196
2197   if( !readSuccess )
2198   {
2199      Con::errorf( "Resource<TSShape>::create - Error reading '%s'", path.getFullPath().c_str() );
2200      delete ret;
2201      ret = NULL;
2202   }
2203
2204   return ret;
2205}
2206
2207template<> ResourceBase::Signature  Resource<TSShape>::signature()
2208{
2209   return MakeFourCC('t','s','s','h');
2210}
2211
2212TSShape::ConvexHullAccelerator* TSShape::getAccelerator(S32 dl)
2213{
2214   AssertFatal(dl < details.size(), "Error, bad detail level!");
2215   if (dl == -1)
2216      return NULL;
2217
2218   AssertFatal( detailCollisionAccelerators.size() == details.size(), 
2219      "TSShape::getAccelerator() - mismatched array sizes!" );
2220
2221   if (detailCollisionAccelerators[dl] == NULL)
2222      computeAccelerator(dl);
2223
2224   AssertFatal(detailCollisionAccelerators[dl] != NULL, "This should be non-null after computing it!");
2225   return detailCollisionAccelerators[dl];
2226}
2227
2228
2229void TSShape::computeAccelerator(S32 dl)
2230{
2231   AssertFatal(dl < details.size(), "Error, bad detail level!");
2232
2233   // Have we already computed this?
2234   if (detailCollisionAccelerators[dl] != NULL)
2235      return;
2236
2237   // Create a bogus features list...
2238   ConvexFeature cf;
2239   MatrixF mat(true);
2240   Point3F n(0, 0, 1);
2241
2242   const TSDetail* detail = &details[dl];
2243   S32 ss = detail->subShapeNum;
2244   S32 od = detail->objectDetailNum;
2245
2246   S32 start = subShapeFirstObject[ss];
2247   S32 end   = subShapeNumObjects[ss] + start;
2248   if (start < end)
2249   {
2250      // run through objects and collide
2251      // DMMNOTE: This assumes that the transform of the collision hulls is
2252      //  identity...
2253      U32 surfaceKey = 0;
2254      for (S32 i = start; i < end; i++)
2255      {
2256         const TSObject* obj = &objects[i];
2257
2258         if (obj->numMeshes && od < obj->numMeshes) {
2259            TSMesh* mesh = meshes[obj->startMeshIndex + od];
2260            if (mesh)
2261               mesh->getFeatures(0, mat, n, &cf, surfaceKey);
2262         }
2263      }
2264   }
2265
2266   Vector<Point3F> fixedVerts;
2267   VECTOR_SET_ASSOCIATION(fixedVerts);
2268   S32 i;
2269   for (i = 0; i < cf.mVertexList.size(); i++) {
2270      S32 j;
2271      bool found = false;
2272      for (j = 0; j < cf.mFaceList.size(); j++) {
2273         if (cf.mFaceList[j].vertex[0] == i ||
2274             cf.mFaceList[j].vertex[1] == i ||
2275             cf.mFaceList[j].vertex[2] == i) {
2276            found = true;
2277            break;
2278         }
2279      }
2280      if (!found)
2281         continue;
2282
2283      found = false;
2284      for (j = 0; j < fixedVerts.size(); j++) {
2285         if (fixedVerts[j] == cf.mVertexList[i]) {
2286            found = true;
2287            break;
2288         }
2289      }
2290      if (found == true) {
2291         // Ok, need to replace any references to vertex i in the facelists with
2292         //  a reference to vertex j in the fixed list
2293         for (S32 k = 0; k < cf.mFaceList.size(); k++) {
2294            for (S32 l = 0; l < 3; l++) {
2295               if (cf.mFaceList[k].vertex[l] == i)
2296                  cf.mFaceList[k].vertex[l] = j;
2297            }
2298         }
2299      } else {
2300         for (S32 k = 0; k < cf.mFaceList.size(); k++) {
2301            for (S32 l = 0; l < 3; l++) {
2302               if (cf.mFaceList[k].vertex[l] == i)
2303                  cf.mFaceList[k].vertex[l] = fixedVerts.size();
2304            }
2305         }
2306         fixedVerts.push_back(cf.mVertexList[i]);
2307      }
2308   }
2309   cf.mVertexList.setSize(0);
2310   cf.mVertexList = fixedVerts;
2311
2312   // Ok, so now we have a vertex list.  Lets copy that out...
2313   ConvexHullAccelerator* accel = new ConvexHullAccelerator;
2314   detailCollisionAccelerators[dl] = accel;
2315   accel->numVerts    = cf.mVertexList.size();
2316   accel->vertexList  = new Point3F[accel->numVerts];
2317   dMemcpy(accel->vertexList, cf.mVertexList.address(), sizeof(Point3F) * accel->numVerts);
2318
2319   accel->normalList = new Point3F[cf.mFaceList.size()];
2320   for (i = 0; i < cf.mFaceList.size(); i++)
2321      accel->normalList[i] = cf.mFaceList[i].normal;
2322
2323   accel->emitStrings = new U8*[accel->numVerts];
2324   dMemset(accel->emitStrings, 0, sizeof(U8*) * accel->numVerts);
2325
2326   for (i = 0; i < accel->numVerts; i++) {
2327      S32 j;
2328
2329      Vector<U32> faces;
2330      VECTOR_SET_ASSOCIATION(faces);
2331      for (j = 0; j < cf.mFaceList.size(); j++) {
2332         if (cf.mFaceList[j].vertex[0] == i ||
2333             cf.mFaceList[j].vertex[1] == i ||
2334             cf.mFaceList[j].vertex[2] == i) {
2335            faces.push_back(j);
2336         }
2337      }
2338      AssertFatal(faces.size() != 0, "Huh?  Vertex unreferenced by any faces");
2339
2340      // Insert all faces that didn't make the first cut, but share a plane with
2341      //  a face that's on the short list.
2342      for (j = 0; j < cf.mFaceList.size(); j++) {
2343         bool found = false;
2344         S32 k;
2345         for (k = 0; k < faces.size(); k++) {
2346            if (faces[k] == j)
2347               found = true;
2348         }
2349         if (found)
2350            continue;
2351
2352         found = false;
2353         for (k = 0; k < faces.size(); k++) {
2354            if (mDot(accel->normalList[faces[k]], accel->normalList[j]) > 0.999) {
2355               found = true;
2356               break;
2357            }
2358         }
2359         if (found)
2360            faces.push_back(j);
2361      }
2362
2363      Vector<U32> vertRemaps;
2364      VECTOR_SET_ASSOCIATION(vertRemaps);
2365      for (j = 0; j < faces.size(); j++) {
2366         for (U32 k = 0; k < 3; k++) {
2367            U32 insert = cf.mFaceList[faces[j]].vertex[k];
2368            bool found = false;
2369            for (S32 l = 0; l < vertRemaps.size(); l++) {
2370               if (insert == vertRemaps[l]) {
2371                  found = true;
2372                  break;
2373               }
2374            }
2375            if (!found)
2376               vertRemaps.push_back(insert);
2377         }
2378      }
2379
2380      Vector<Point2I> edges;
2381      VECTOR_SET_ASSOCIATION(edges);
2382      for (j = 0; j < faces.size(); j++) {
2383         for (U32 k = 0; k < 3; k++) {
2384            U32 edgeStart = cf.mFaceList[faces[j]].vertex[(k + 0) % 3];
2385            U32 edgeEnd   = cf.mFaceList[faces[j]].vertex[(k + 1) % 3];
2386
2387            U32 e0 = getMin(edgeStart, edgeEnd);
2388            U32 e1 = getMax(edgeStart, edgeEnd);
2389
2390            bool found = false;
2391            for (S32 l = 0; l < edges.size(); l++) {
2392               if (edges[l].x == e0 && edges[l].y == e1) {
2393                  found = true;
2394                  break;
2395               }
2396            }
2397            if (!found)
2398               edges.push_back(Point2I(e0, e1));
2399         }
2400      }
2401
2402      //AssertFatal(vertRemaps.size() < 256 && faces.size() < 256 && edges.size() < 256,
2403      //            "Error, ran over the shapebase assumptions about convex hulls.");
2404
2405      U32 emitStringLen = 1 + vertRemaps.size()  +
2406                          1 + (edges.size() * 2) +
2407                          1 + (faces.size() * 4);
2408      accel->emitStrings[i] = new U8[emitStringLen];
2409
2410      U32 currPos = 0;
2411
2412      accel->emitStrings[i][currPos++] = vertRemaps.size();
2413      for (j = 0; j < vertRemaps.size(); j++)
2414         accel->emitStrings[i][currPos++] = vertRemaps[j];
2415
2416      accel->emitStrings[i][currPos++] = edges.size();
2417      for (j = 0; j < edges.size(); j++) {
2418         S32 l;
2419         U32 old = edges[j].x;
2420         bool found = false;
2421         for (l = 0; l < vertRemaps.size(); l++) {
2422            if (vertRemaps[l] == old) {
2423               found = true;
2424               accel->emitStrings[i][currPos++] = l;
2425               break;
2426            }
2427         }
2428         AssertFatal(found, "Error, couldn't find the remap!");
2429
2430         old = edges[j].y;
2431         found = false;
2432         for (l = 0; l < vertRemaps.size(); l++) {
2433            if (vertRemaps[l] == old) {
2434               found = true;
2435               accel->emitStrings[i][currPos++] = l;
2436               break;
2437            }
2438         }
2439         AssertFatal(found, "Error, couldn't find the remap!");
2440      }
2441
2442      accel->emitStrings[i][currPos++] = faces.size();
2443      for (j = 0; j < faces.size(); j++) {
2444         accel->emitStrings[i][currPos++] = faces[j];
2445         for (U32 k = 0; k < 3; k++) {
2446            U32 old = cf.mFaceList[faces[j]].vertex[k];
2447            bool found = false;
2448            for (S32 l = 0; l < vertRemaps.size(); l++) {
2449               if (vertRemaps[l] == old) {
2450                  found = true;
2451                  accel->emitStrings[i][currPos++] = l;
2452                  break;
2453               }
2454            }
2455            AssertFatal(found, "Error, couldn't find the remap!");
2456         }
2457      }
2458      AssertFatal(currPos == emitStringLen, "Error, over/underflowed the emission string!");
2459   }
2460}
2461
2462void TSShape::finalizeEditable()
2463{
2464   for (U32 i = 0; i < meshes.size(); i++)
2465   {
2466      if (meshes[i])
2467      {
2468         meshes[i]->clearEditable();
2469      }
2470   }
2471}
2472