Torque3D Documentation / _generateds / tsShapeEdit.cpp

tsShapeEdit.cpp

Engine/source/ts/tsShapeEdit.cpp

More...

Public Functions

_PadMoveAndTrim(Vector< T * > & vec, S32 offset, S32 count, S32 padLength, S32 oldIndex, S32 newIndex)
adjustForNameRemoval(Vector< T > & group, S32 nameIndex)

Adjust the nameIndex for elements in the group.

bool
doRename(TSShape * shape, Vector< T > & group, const String & oldName, const String & newName)
eraseStates(Vector< T > & vec, const TSIntegerSet & matters, S32 base, S32 numKeyframes, S32 index)

Erase animation keyframes (translation, rotation etc)

findByName(Vector< T > & group, S32 nameIndex)

Get the index of the element in the group with a given name.

Detailed Description

Public Functions

_PadMoveAndTrim(Vector< T * > & vec, S32 offset, S32 count, S32 padLength, S32 oldIndex, S32 newIndex)

adjustForNameRemoval(Vector< T > & group, S32 nameIndex)

Adjust the nameIndex for elements in the group.

doRename(TSShape * shape, Vector< T > & group, const String & oldName, const String & newName)

eraseStates(Vector< T > & vec, const TSIntegerSet & matters, S32 base, S32 numKeyframes, S32 index)

Erase animation keyframes (translation, rotation etc)

findByName(Vector< T > & group, S32 nameIndex)

Get the index of the element in the group with a given name.

   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 "console/consoleTypes.h"
  27#include "core/resourceManager.h"
  28#include "ts/tsShape.h"
  29#include "ts/tsShapeInstance.h"
  30#include "ts/tsLastDetail.h"
  31#include "ts/tsMaterialList.h"
  32#include "core/stream/fileStream.h"
  33#include "core/volume.h"
  34
  35
  36//-----------------------------------------------------------------------------
  37
  38S32 TSShape::addName(const String& name)
  39{
  40   // Check for empty names
  41   if (name.isEmpty())
  42      return -1;
  43
  44   // Return the index of the new name (add if it is unique)
  45   S32 index = findName(name);
  46   if (index >= 0)
  47      return index;
  48
  49   names.push_back(StringTable->insert(name));
  50   return names.size()-1;
  51}
  52
  53void TSShape::updateSmallestVisibleDL()
  54{
  55   // Update smallest visible detail
  56   mSmallestVisibleDL = -1;
  57   mSmallestVisibleSize = F32_MAX;
  58   F32 maxSize = 0.0f;
  59   for (S32 i = 0; i < details.size(); i++)
  60   {
  61      maxSize = getMax( maxSize, details[i].size );
  62
  63      if ((details[i].size >= 0) && (details[i].size < mSmallestVisibleSize))
  64      {
  65         mSmallestVisibleDL = i;
  66         mSmallestVisibleSize = details[i].size;
  67      }
  68   }
  69
  70   // Initialize the detail level lod lookup table.
  71   mDetailLevelLookup.setSize( (U32)( maxSize * 2.0f ) + 2 );
  72   for ( U32 l=0; l < mDetailLevelLookup.size(); l++ )
  73   {
  74      F32 pixelSize = (F32)l;
  75      S32 dl = -1;
  76
  77      for ( U32 d=0; d < details.size(); d++ )
  78      {
  79         // Break when we get to hidden detail 
  80         // levels like collision shapes.
  81         if ( details[d].size < 0 )
  82            break;
  83
  84         if ( pixelSize > details[d].size )
  85         {
  86            dl = d;
  87            break;
  88         }
  89
  90         if ( d + 1 >= details.size() || details[d+1].size < 0 )
  91         {
  92            // We've run out of details and haven't found anything?
  93            // Let's just grab this one.
  94            dl = d;
  95            break;
  96         }
  97      }
  98
  99      // Calculate the intra detail level.
 100      F32 intraDL = 0;
 101      if ( dl > -1 )
 102      {
 103         F32 curSize = details[dl].size;
 104         F32 nextSize = dl == 0 ? 2.0f * curSize : details[dl - 1].size;
 105         intraDL = mClampF( nextSize - curSize > 0.01f ? (pixelSize - curSize) / (nextSize - curSize) : 1.0f, 0, 1 );
 106      }
 107
 108      mDetailLevelLookup[l].set( dl, intraDL );
 109   }
 110
 111   // Test for using the legacy screen error
 112   // lod method here instead of runtime.
 113   //
 114   // See setDetailFromDistance().
 115   //
 116   mUseDetailFromScreenError =   mSmallestVisibleDL >= 0 && 
 117                                 details.first().maxError >= 0;
 118}
 119
 120S32 TSShape::addDetail(const String& dname, S32 size, S32 subShapeNum)
 121{
 122   S32 nameIndex = addName(avar("%s%d", dname.c_str(), size));
 123
 124   // Check if this detail size has already been added
 125   S32 index;
 126   for (index = 0; index < details.size(); index++)
 127   {
 128      if ((details[index].size == size) &&
 129         (details[index].subShapeNum == subShapeNum) &&
 130         (details[index].nameIndex == nameIndex))
 131         return index;
 132      if (details[index].size < size)
 133         break;
 134   }
 135
 136   // Create a new detail level at the right index, so array
 137   // remains sorted by detail size (from largest to smallest)
 138   details.insert(index);
 139   TSShape::Detail &detail = details[index];
 140
 141   // Clear the detail to ensure no garbage values
 142   // are left in any vars we don't set.
 143   dMemset( &detail, 0, sizeof( Detail ) );
 144
 145   // Setup the detail.
 146   detail.nameIndex = nameIndex;
 147   detail.size = size;
 148   detail.subShapeNum = subShapeNum;
 149   detail.objectDetailNum = 0;
 150   detail.averageError = -1;
 151   detail.maxError = -1;
 152   detail.polyCount = 0;
 153
 154   // Resize alpha vectors
 155   alphaIn.increment();
 156   alphaOut.increment();
 157
 158   // Fixup objectDetailNum in other detail levels
 159   for (S32 i = index+1; i < details.size(); i++)
 160   {
 161      if ((details[i].subShapeNum >= 0) &&
 162         ((subShapeNum == -1) || (details[i].subShapeNum == subShapeNum)))
 163         details[i].objectDetailNum++;
 164   }
 165
 166   // Update smallest visible detail
 167   updateSmallestVisibleDL();
 168
 169   return index;
 170}
 171
 172S32 TSShape::addImposter(const String& cachePath, S32 size, S32 numEquatorSteps,
 173                        S32 numPolarSteps, S32 dl, S32 dim, bool includePoles, F32 polarAngle)
 174{
 175   // Check if the desired size is already in use
 176   bool isNewDetail = false;
 177   S32 detIndex = findDetailBySize( size );
 178
 179   if ( detIndex >= 0 )
 180   {
 181      // Size is in use. If the detail is already an imposter, we can just change
 182      // the settings, otherwise quit
 183      if ( details[detIndex].subShapeNum >= 0 )
 184      {
 185         Con::errorf( "TSShape::addImposter: A non-billboard detail already "
 186            "exists at size %d", size );
 187         return -1;
 188      }
 189   }
 190   else
 191   {
 192      // Size is not in use. If an imposter already exists, change its size, otherwise
 193      // create a new detail
 194      for ( detIndex = 0; detIndex < details.size(); ++detIndex )
 195      {
 196         if ( details[detIndex].subShapeNum < 0 )
 197         {
 198            // Change the imposter detail size
 199            setDetailSize( details[detIndex].size, size );
 200            break;
 201         }
 202      }
 203      if ( detIndex == details.size() )
 204      {
 205         isNewDetail = true;
 206         detIndex = addDetail( "bbDetail", size, -1 );
 207      }
 208   }
 209
 210   // Now set the billboard properties.
 211   Detail &detail = details[detIndex];
 212
 213   // In prior to DTS version 26 we would pack the autobillboard
 214   // into this single 32bit value.  This was prone to overflows
 215   // of parameters caused random bugs.
 216   //
 217   // Set the old autobillboard properties var to zero.
 218   detail.objectDetailNum = 0;
 219   
 220   // We now use the new vars.
 221   detail.bbEquatorSteps = numEquatorSteps;
 222   detail.bbPolarSteps = numPolarSteps;
 223   detail.bbPolarAngle = polarAngle;
 224   detail.bbDetailLevel = dl;
 225   detail.bbDimension = dim;
 226   detail.bbIncludePoles = includePoles;
 227
 228   // Rebuild billboard details or force an update of the modified detail
 229   if ( isNewDetail )
 230   {
 231      // Add NULL meshes for this detail
 232      for ( S32 iObj = 0; iObj < objects.size(); ++iObj )
 233      {
 234         if ( detIndex < objects[iObj].numMeshes )
 235         {
 236            objects[iObj].numMeshes++;
 237            meshes.insert( objects[iObj].startMeshIndex + detIndex, NULL );
 238            for (S32 j = iObj + 1; j < objects.size(); ++j )
 239               objects[j].startMeshIndex++;
 240         }
 241      }
 242
 243      // Could be dedicated server.
 244      if ( GFXDevice::devicePresent() )
 245         setupBillboardDetails( cachePath );
 246
 247      while ( detailCollisionAccelerators.size() < details.size() )
 248         detailCollisionAccelerators.push_back( NULL );
 249   }
 250   else
 251   {
 252      if ( billboardDetails.size() && GFXDevice::devicePresent() )
 253      {
 254         delete billboardDetails[detIndex];
 255         billboardDetails[detIndex] = new TSLastDetail(
 256                                          this,
 257                                          cachePath,
 258                                          detail.bbEquatorSteps,
 259                                          detail.bbPolarSteps,
 260                                          detail.bbPolarAngle,
 261                                          detail.bbIncludePoles,
 262                                          detail.bbDetailLevel,
 263                                          detail.bbDimension );
 264
 265         billboardDetails[detIndex]->update( true );
 266      }
 267   }
 268
 269   return detIndex;
 270}
 271
 272bool TSShape::removeImposter()
 273{
 274   // Find the imposter detail level
 275   S32 detIndex;
 276   for ( detIndex = 0; detIndex < details.size(); ++detIndex )
 277   {
 278      if ( details[detIndex].subShapeNum < 0 )
 279         break;
 280   }
 281
 282   if ( detIndex == details.size() )
 283   {
 284      Con::errorf( "TSShape::removeImposter: No imposter detail level found in shape" );
 285      return false;
 286   }
 287
 288   // Remove the detail level
 289   details.erase( detIndex );
 290
 291   if ( detIndex < billboardDetails.size() )
 292   {
 293      // Delete old textures
 294      TSLastDetail* bb = billboardDetails[detIndex];
 295      bb->deleteImposterCacheTextures();
 296      delete billboardDetails[detIndex];
 297   }
 298   billboardDetails.clear();
 299
 300   detailCollisionAccelerators.erase( detIndex );
 301
 302   // Remove the (NULL) meshes from each object
 303   for ( S32 iObj = 0; iObj < objects.size(); ++iObj )
 304   {
 305      if ( detIndex < objects[iObj].numMeshes )
 306      {
 307         objects[iObj].numMeshes--;
 308         meshes.erase( objects[iObj].startMeshIndex + detIndex );
 309         for (S32 j = iObj + 1; j < objects.size(); ++j )
 310            objects[j].startMeshIndex--;
 311      }
 312   }
 313
 314   // Update smallest visible size
 315   updateSmallestVisibleDL();
 316
 317   return true;
 318}
 319
 320//-----------------------------------------------------------------------------
 321
 322/// Get the index of the element in the group with a given name
 323template<class T> S32 findByName(Vector<T>& group, S32 nameIndex)
 324{
 325   for (S32 i = 0; i < group.size(); i++)
 326      if (group[i].nameIndex == nameIndex)
 327         return i;
 328   return -1;
 329}
 330
 331/// Adjust the nameIndex for elements in the group
 332template<class T> void adjustForNameRemoval(Vector<T>& group, S32 nameIndex)
 333{
 334   for (S32 i = 0; i < group.size(); i++)
 335      if (group[i].nameIndex > nameIndex)
 336         group[i].nameIndex--;
 337}
 338
 339bool TSShape::removeName(const String& name)
 340{
 341   // Check if the name is still in use
 342   S32 nameIndex = findName(name);
 343   if ((findByName(nodes, nameIndex) >= 0)      ||
 344       (findByName(objects, nameIndex) >= 0)    ||
 345       (findByName(sequences, nameIndex) >= 0)  ||
 346       (findByName(details, nameIndex) >= 0))
 347       return false;
 348
 349   // Remove the name, then update nameIndex for affected elements
 350   names.erase(nameIndex);
 351
 352   adjustForNameRemoval(nodes, nameIndex);
 353   adjustForNameRemoval(objects, nameIndex);
 354   adjustForNameRemoval(sequences, nameIndex);
 355   adjustForNameRemoval(details, nameIndex);
 356
 357   return true;
 358}
 359
 360//-----------------------------------------------------------------------------
 361
 362template<class T> bool doRename(TSShape* shape, Vector<T>& group, const String& oldName, const String& newName)
 363{
 364   // Find the element in the group with the oldName
 365   S32 index = findByName(group, shape->findName(oldName));
 366   if (index < 0)
 367   {
 368      Con::errorf("TSShape::rename: Could not find '%s'", oldName.c_str());
 369      return false;
 370   }
 371
 372   // Ignore trivial renames
 373   if (oldName.equal(newName, String::NoCase))
 374      return true;
 375
 376   // Check that this name is not already in use
 377   if (findByName(group, shape->findName(newName)) >= 0)
 378   {
 379      Con::errorf("TSShape::rename: '%s' is already in use", newName.c_str());
 380      return false;
 381   }
 382
 383   // Do the rename (the old name will be removed if it is no longer in use)
 384   group[index].nameIndex = shape->addName(newName);
 385   shape->removeName(oldName);
 386   return true;
 387}
 388
 389bool TSShape::renameNode(const String& oldName, const String& newName)
 390{
 391   return doRename(this, nodes, oldName, newName);
 392}
 393
 394bool TSShape::renameObject(const String& oldName, const String& newName)
 395{
 396   return doRename(this, objects, oldName, newName);
 397}
 398
 399bool TSShape::renameDetail(const String& oldName, const String& newName)
 400{
 401   return doRename(this, details, oldName, newName);
 402}
 403
 404bool TSShape::renameSequence(const String& oldName, const String& newName)
 405{
 406   return doRename(this, sequences, oldName, newName);
 407}
 408
 409//-----------------------------------------------------------------------------
 410
 411bool TSShape::addNode(const String& name, const String& parentName, const Point3F& pos, const QuatF& rot)
 412{
 413   // Check that adding this node would not exceed the maximum count
 414   if (nodes.size() >= MAX_TS_SET_SIZE)
 415   {
 416      Con::errorf("TSShape::addNode: Cannot add node, shape already has maximum (%d) nodes", MAX_TS_SET_SIZE);
 417      return false;
 418   }
 419
 420   // Check that there is not already a node with this name
 421   if (findNode(name) >= 0)
 422   {
 423      Con::errorf("TSShape::addNode: %s already exists!", name.c_str());
 424      return false;
 425   }
 426
 427   // Find the parent node (OK for name to be empty => node is at root level)
 428   S32 parentIndex = -1;
 429   if (String::compare(parentName, ""))
 430   {
 431      parentIndex = findNode(parentName);
 432      if (parentIndex < 0)
 433      {
 434         Con::errorf("TSShape::addNode: Could not find parent node '%s'", parentName.c_str());
 435         return false;
 436      }
 437   }
 438
 439   // Need to make everything editable since node indexes etc will change
 440   makeEditable();
 441
 442   // Insert node at the end of the subshape
 443   S32 subShapeIndex = (parentIndex >= 0) ? getSubShapeForNode(parentIndex) : 0;
 444   S32 nodeIndex = subShapeNumNodes[subShapeIndex];
 445
 446   // Adjust subshape node indices
 447   subShapeNumNodes[subShapeIndex]++;
 448   for (S32 i = subShapeIndex + 1; i < subShapeFirstNode.size(); i++)
 449      subShapeFirstNode[i]++;
 450
 451   // Update animation sequences
 452   for (S32 iSeq = 0; iSeq < sequences.size(); iSeq++)
 453   {
 454      // Update animation matters arrays (new node is not animated)
 455      TSShape::Sequence& seq = sequences[iSeq];
 456      seq.translationMatters.insert(nodeIndex, false);
 457      seq.rotationMatters.insert(nodeIndex, false);
 458      seq.scaleMatters.insert(nodeIndex, false);
 459   }
 460
 461   // Insert the new node
 462   TSShape::Node node;
 463   node.nameIndex = addName(name);
 464   node.parentIndex = parentIndex;
 465   node.firstChild = -1;
 466   node.firstObject = -1;
 467   node.nextSibling = -1;
 468   nodes.insert(nodeIndex, node);
 469
 470   // Insert node default translation and rotation
 471   Quat16 rot16;
 472   rot16.set(rot);
 473   defaultTranslations.insert(nodeIndex, pos);
 474   defaultRotations.insert(nodeIndex, rot16);
 475
 476   // Fixup node indices
 477   for (S32 i = 0; i < nodes.size(); i++)
 478   {
 479      if (nodes[i].parentIndex >= nodeIndex)
 480         nodes[i].parentIndex++;
 481   }
 482   for (S32 i = 0; i < objects.size(); i++)
 483   {
 484      if (objects[i].nodeIndex >= nodeIndex)
 485         objects[i].nodeIndex++;
 486   }
 487   for (S32 i = 0; i < meshes.size(); i++)
 488   {
 489      if (meshes[i] && (meshes[i]->getMeshType() == TSMesh::SkinMeshType))
 490      {
 491         TSSkinMesh* skin = dynamic_cast<TSSkinMesh*>(meshes[i]);
 492         for (S32 j = 0; j < skin->batchData.nodeIndex.size(); j++)
 493         {
 494            if (skin->batchData.nodeIndex[j] >= nodeIndex)
 495               skin->batchData.nodeIndex[j]++;
 496         }
 497      }
 498   }
 499
 500   initObjects();
 501
 502   return true;
 503}
 504
 505/// Erase animation keyframes (translation, rotation etc)
 506template<class T> S32 eraseStates(Vector<T>& vec, const TSIntegerSet& matters, S32 base, S32 numKeyframes, S32 index=-1)
 507{
 508   S32 dest, count;
 509   if (index == -1)
 510   {
 511      // Erase for all nodes/objects
 512      dest = base;
 513      count = numKeyframes * matters.count();
 514   }
 515   else
 516   {
 517      // Erase for the indexed node/object only
 518      dest = base + matters.count(index)*numKeyframes;
 519      count = numKeyframes;
 520   }
 521
 522   // Erase the values
 523   if (count)
 524   {
 525      if ((dest + count) < vec.size())
 526         dCopyArray(&vec[dest], &vec[dest + count], vec.size() - (dest + count));
 527      vec.decrement(count);
 528   }
 529   return count;
 530}
 531
 532bool TSShape::removeNode(const String& name)
 533{
 534   // Find the node to be removed
 535   S32 nodeIndex = findNode(name);
 536   if (nodeIndex < 0)
 537   {
 538      Con::errorf("TSShape::removeNode: Could not find node '%s'", name.c_str());
 539      return false;
 540   }
 541
 542   S32 nodeParentIndex = nodes[nodeIndex].parentIndex;
 543
 544   // Warn if there are objects attached to this node
 545   Vector<S32> nodeObjects;
 546   getNodeObjects(nodeIndex, nodeObjects);
 547   if (nodeObjects.size())
 548   {
 549      Con::warnf("TSShape::removeNode: Node '%s' has %d objects attached, these "
 550         "will be reassigned to the node's parent ('%s')", name.c_str(), nodeObjects.size(),
 551         ((nodeParentIndex >= 0) ? getName(nodes[nodeParentIndex].nameIndex).c_str() : "null"));
 552   }
 553
 554   // Need to make everything editable since node indexes etc will change
 555   makeEditable();
 556
 557   // Update animation sequences
 558   for (S32 iSeq = 0; iSeq < sequences.size(); iSeq++)
 559   {
 560      TSShape::Sequence& seq = sequences[iSeq];
 561
 562      // Remove animated node transforms
 563      if (seq.translationMatters.test(nodeIndex))
 564         eraseStates(nodeTranslations, seq.translationMatters, seq.baseTranslation, seq.numKeyframes, nodeIndex);
 565      if (seq.rotationMatters.test(nodeIndex))
 566         eraseStates(nodeRotations, seq.rotationMatters, seq.baseRotation, seq.numKeyframes, nodeIndex);
 567      if (seq.scaleMatters.test(nodeIndex))
 568      {
 569         if (seq.flags & TSShape::ArbitraryScale)
 570         {
 571            eraseStates(nodeArbitraryScaleRots, seq.scaleMatters, seq.baseScale, seq.numKeyframes, nodeIndex);
 572            eraseStates(nodeArbitraryScaleFactors, seq.scaleMatters, seq.baseScale, seq.numKeyframes, nodeIndex);
 573         }
 574         else if (seq.flags & TSShape::AlignedScale)
 575            eraseStates(nodeAlignedScales, seq.scaleMatters, seq.baseScale, seq.numKeyframes, nodeIndex);
 576         else
 577            eraseStates(nodeUniformScales, seq.scaleMatters, seq.baseScale, seq.numKeyframes, nodeIndex);
 578      }
 579
 580      seq.translationMatters.erase(nodeIndex);
 581      seq.rotationMatters.erase(nodeIndex);
 582      seq.scaleMatters.erase(nodeIndex);
 583   }
 584
 585   // Remove the node
 586   nodes.erase(nodeIndex);
 587   defaultTranslations.erase(nodeIndex);
 588   defaultRotations.erase(nodeIndex);
 589
 590   // Adjust subshape node indices
 591   S32 subShapeIndex = getSubShapeForNode(nodeIndex);
 592   subShapeNumNodes[subShapeIndex]--;
 593   for (S32 i = subShapeIndex + 1; i < subShapeFirstNode.size(); i++)
 594      subShapeFirstNode[i]--;
 595
 596   // Fixup node parent indices
 597   for (S32 i = 0; i < nodes.size(); i++)
 598   {
 599      if (nodes[i].parentIndex == nodeIndex)
 600         nodes[i].parentIndex = -1;
 601      else if (nodes[i].parentIndex > nodeIndex)
 602         nodes[i].parentIndex--;
 603   }
 604   if (nodeParentIndex > nodeIndex)
 605      nodeParentIndex--;
 606
 607   // Fixup object node indices, and re-assign attached objects to node's parent
 608   for (S32 i = 0; i < objects.size(); i++)
 609   {
 610      if (objects[i].nodeIndex == nodeIndex)
 611         objects[i].nodeIndex = nodeParentIndex;
 612      if (objects[i].nodeIndex > nodeIndex)
 613         objects[i].nodeIndex--;
 614   }
 615
 616   // Fixup skin weight node indices, and re-assign weights for deleted node to its parent
 617   for (S32 i = 0; i < meshes.size(); i++)
 618   {
 619      if (meshes[i] && (meshes[i]->getMeshType() == TSMesh::SkinMeshType))
 620      {
 621         TSSkinMesh* skin = dynamic_cast<TSSkinMesh*>(meshes[i]);
 622         for (S32 j = 0; j < skin->batchData.nodeIndex.size(); j++)
 623         {
 624            if (skin->batchData.nodeIndex[j] == nodeIndex)
 625               skin->batchData.nodeIndex[j] = nodeParentIndex;
 626            if (skin->batchData.nodeIndex[j] > nodeIndex)
 627               skin->batchData.nodeIndex[j]--;
 628         }
 629      }
 630   }
 631
 632   // Remove the sequence name if it is no longer in use
 633   removeName(name);
 634
 635   initObjects();
 636
 637   return true;
 638}
 639
 640//-----------------------------------------------------------------------------
 641
 642bool TSShape::setNodeTransform(const String& name, const Point3F& pos, const QuatF& rot)
 643{
 644   // Find the node to be transformed
 645   S32 nodeIndex = findNode(name);
 646   if (nodeIndex < 0)
 647   {
 648      Con::errorf("TSShape::setNodeTransform: Could not find node '%s'", name.c_str());
 649      return false;
 650   }
 651
 652   // Update initial node position and rotation
 653   defaultTranslations[nodeIndex] = pos;
 654   defaultRotations[nodeIndex].set(rot);
 655
 656   return true;
 657}
 658
 659//-----------------------------------------------------------------------------
 660
 661S32 TSShape::addObject(const String& objName, S32 subShapeIndex)
 662{
 663   S32 objIndex = subShapeNumObjects[subShapeIndex];
 664
 665   // Add object to subshape
 666   subShapeNumObjects[subShapeIndex]++;
 667   for (S32 i = subShapeIndex + 1; i < subShapeFirstObject.size(); i++)
 668      subShapeFirstObject[i]++;
 669
 670   TSShape::Object obj;
 671   obj.nameIndex = addName(objName);
 672   obj.nodeIndex = 0;
 673   obj.numMeshes = 0;
 674   obj.startMeshIndex = (objIndex == 0) ? 0 : objects[objIndex-1].startMeshIndex + objects[objIndex-1].numMeshes;
 675   obj.firstDecal = 0;
 676   obj.nextSibling = 0;
 677   objects.insert(objIndex, obj);
 678
 679   // Add default object state
 680   TSShape::ObjectState state;
 681   state.frameIndex = 0;
 682   state.matFrameIndex = 0;
 683   state.vis = 1.0f;
 684   objectStates.insert(objIndex, state);
 685
 686   // Fixup sequences
 687   for (S32 i = 0; i < sequences.size(); i++)
 688      sequences[i].baseObjectState++;
 689
 690   return objIndex;
 691}
 692
 693void TSShape::addMeshToObject(S32 objIndex, S32 meshIndex, TSMesh* mesh)
 694{
 695   TSShape::Object& obj = objects[objIndex];
 696
 697   // Pad with NULLs if required
 698   S32 oldNumMeshes = obj.numMeshes;
 699   if (mesh)
 700   {
 701      for (S32 i = obj.numMeshes; i < meshIndex; i++)
 702      {
 703         meshes.insert(obj.startMeshIndex + i, NULL);
 704         obj.numMeshes++;
 705      }
 706   }
 707
 708   // Insert the new mesh
 709   meshes.insert(obj.startMeshIndex + meshIndex, mesh);
 710   obj.numMeshes++;
 711
 712   // Skinned meshes are not attached to any node
 713   if (mesh && (mesh->getMeshType() == TSMesh::SkinMeshType))
 714      obj.nodeIndex = -1;
 715
 716   // Fixup mesh indices for other objects
 717   for (S32 i = 0; i < objects.size(); i++)
 718   {
 719      if ((i != objIndex) && (objects[i].startMeshIndex >= obj.startMeshIndex))
 720         objects[i].startMeshIndex += (obj.numMeshes - oldNumMeshes);
 721   }
 722}
 723
 724void TSShape::removeMeshFromObject(S32 objIndex, S32 meshIndex)
 725{
 726   TSShape::Object& obj = objects[objIndex];
 727
 728   // Remove the mesh, but do not destroy it (this must be done by the caller)
 729   meshes[obj.startMeshIndex + meshIndex] = NULL;
 730
 731   // Check if there are any objects remaining that have a valid mesh at this
 732   // detail size
 733   bool removeDetail = true;
 734   for (S32 i = 0; i < objects.size(); i++)
 735   {
 736      if ((meshIndex < objects[i].numMeshes) && meshes[objects[i].startMeshIndex + meshIndex])
 737      {
 738         removeDetail = false;
 739         break;
 740      }
 741   }
 742
 743   // Remove detail level if possible
 744   if (removeDetail)
 745   {
 746      for (S32 i = 0; i < objects.size(); i++)
 747      {
 748         if (meshIndex < objects[i].numMeshes)
 749         {
 750            U32 idxToRemove = objects[i].startMeshIndex + meshIndex;
 751            meshes.erase(idxToRemove);
 752            objects[i].numMeshes--;
 753
 754            // Clear invalid parent
 755            for (U32 k = 0; k < meshes.size(); k++)
 756            {
 757               if (meshes[k] == NULL)
 758                  continue;
 759
 760               if (meshes[k]->mParentMesh == idxToRemove)
 761               {
 762                  meshes[k]->mParentMesh = -1;
 763               }
 764               else if (meshes[k]->mParentMesh > idxToRemove)
 765               {
 766                  meshes[k]->mParentMesh--;
 767               }
 768            }
 769
 770            for (S32 j = 0; j < objects.size(); j++)
 771            {
 772               if (objects[j].startMeshIndex > objects[i].startMeshIndex)
 773                  objects[j].startMeshIndex--;
 774            }
 775         }
 776      }
 777
 778      Vector<S32> validDetails;
 779      getSubShapeDetails(getSubShapeForObject(objIndex), validDetails);
 780
 781      for (S32 i = 0; i < validDetails.size(); i++)
 782      {
 783         TSShape::Detail& detail = details[validDetails[i]];
 784         if (detail.objectDetailNum > meshIndex)
 785            detail.objectDetailNum--;
 786      }
 787
 788      details.erase(validDetails[meshIndex]);
 789   }
 790
 791   // Remove trailing NULL meshes from the object
 792   S32 oldNumMeshes = obj.numMeshes;
 793   while (obj.numMeshes && !meshes[obj.startMeshIndex + obj.numMeshes - 1])
 794   {
 795      U32 idxToRemove = obj.startMeshIndex + obj.numMeshes - 1;
 796      meshes.erase(idxToRemove);
 797
 798      // Clear invalid parent
 799      for (U32 k = 0; k < meshes.size(); k++)
 800      {
 801         if (meshes[k] == NULL)
 802            continue;
 803
 804         if (meshes[k]->mParentMesh == idxToRemove)
 805         {
 806            meshes[k]->mParentMesh = -1;
 807         }
 808         else if (meshes[k]->mParentMesh > idxToRemove)
 809         {
 810            meshes[k]->mParentMesh--;
 811         }
 812      }
 813
 814      obj.numMeshes--;
 815   }
 816
 817   // Fixup mesh indices for other objects
 818   for (S32 i = 0; i < objects.size(); i++)
 819   {
 820      if (objects[i].startMeshIndex > obj.startMeshIndex)
 821         objects[i].startMeshIndex -= (oldNumMeshes - obj.numMeshes);
 822   }
 823}
 824
 825bool TSShape::setObjectNode(const String& objName, const String& nodeName)
 826{
 827   // Find the object and node
 828   S32 objIndex = findObject(objName);
 829   if (objIndex < 0)
 830   {
 831      Con::errorf("TSShape::setObjectNode: Could not find object '%s'", objName.c_str());
 832      return false;
 833   }
 834
 835   S32 nodeIndex;
 836   if (nodeName.isEmpty())
 837      nodeIndex = -1;
 838   else
 839   {
 840      nodeIndex = findNode(nodeName);
 841      if (nodeIndex < 0)
 842      {
 843         Con::errorf("TSShape::setObjectNode: Could not find node '%s'", nodeName.c_str());
 844         return false;
 845      }
 846   }
 847
 848   objects[objIndex].nodeIndex = nodeIndex;
 849
 850   return true;
 851}
 852
 853bool TSShape::removeObject(const String& name)
 854{
 855   // Find the object
 856   S32 objIndex = findObject(name);
 857   if (objIndex < 0)
 858   {
 859      Con::errorf("TSShape::removeObject: Could not find object '%s'", name.c_str());
 860      return false;
 861   }
 862
 863   // Need to make everything editable since node indexes etc will change
 864   makeEditable();
 865
 866   // Destroy all meshes in the object
 867   TSShape::Object& obj = objects[objIndex];
 868   while ( obj.numMeshes )
 869   {
 870      destructInPlace(meshes[obj.startMeshIndex + obj.numMeshes - 1]);
 871      removeMeshFromObject(objIndex, obj.numMeshes - 1);
 872   }
 873
 874   // Remove the object from the shape
 875   objects.erase(objIndex);
 876   S32 subShapeIndex = getSubShapeForObject(objIndex);
 877   subShapeNumObjects[subShapeIndex]--;
 878   for (S32 i = subShapeIndex + 1; i < subShapeFirstObject.size(); i++)
 879      subShapeFirstObject[i]--;
 880
 881   // Remove the object from all sequences
 882   for (S32 i = 0; i < sequences.size(); i++)
 883   {
 884      TSShape::Sequence& seq = sequences[i];
 885
 886      TSIntegerSet objMatters(seq.frameMatters);
 887      objMatters.overlap(seq.matFrameMatters);
 888      objMatters.overlap(seq.visMatters);
 889
 890      if (objMatters.test(objIndex))
 891         eraseStates(objectStates, objMatters, seq.baseObjectState, seq.numKeyframes, objIndex);
 892
 893      seq.frameMatters.erase(objIndex);
 894      seq.matFrameMatters.erase(objIndex);
 895      seq.visMatters.erase(objIndex);
 896   }
 897
 898   // Remove the object name if it is no longer in use
 899   removeName(name);
 900
 901   // Update smallest visible detail
 902   updateSmallestVisibleDL();
 903
 904   initObjects();
 905
 906   return true;
 907}
 908
 909//-----------------------------------------------------------------------------
 910
 911// Helper to copy a TSMesh ready for adding to this TSShape (with the right vertex format etc)
 912TSMesh* TSShape::copyMesh( const TSMesh* srcMesh ) const
 913{
 914   TSMesh * mesh = 0;
 915   if ( srcMesh && ( srcMesh->getMeshType() == TSMesh::SkinMeshType ) )
 916   {
 917      TSSkinMesh* skin = new TSSkinMesh;
 918
 919      // Copy skin elements
 920      const TSSkinMesh *srcSkin = dynamic_cast<const TSSkinMesh*>(srcMesh);
 921      skin->weight = srcSkin->weight;
 922      skin->vertexIndex = srcSkin->vertexIndex;
 923      skin->boneIndex = srcSkin->boneIndex;
 924
 925      skin->batchData.nodeIndex = srcSkin->batchData.nodeIndex;
 926      skin->batchData.initialTransforms = srcSkin->batchData.initialTransforms;
 927      skin->batchData.initialVerts = srcSkin->batchData.initialVerts;
 928      skin->batchData.initialNorms = srcSkin->batchData.initialNorms;
 929
 930      mesh = static_cast<TSMesh*>(skin);
 931   }
 932   else
 933   {
 934      mesh = new TSMesh;
 935   }
 936
 937   if ( !srcMesh )
 938      return mesh;      // return an empty mesh
 939
 940   // Copy mesh elements
 941   mesh->mIndices = srcMesh->mIndices;
 942   mesh->mPrimitives = srcMesh->mPrimitives;
 943   mesh->numFrames = srcMesh->numFrames;
 944   mesh->numMatFrames = srcMesh->numMatFrames;
 945   mesh->vertsPerFrame = srcMesh->vertsPerFrame;
 946   mesh->setFlags(srcMesh->getFlags());
 947   mesh->mNumVerts = srcMesh->mNumVerts;
 948
 949   // Copy vertex data in an *unpacked* form
 950   mesh->copySourceVertexDataFrom(srcMesh);
 951
 952   mesh->createTangents(mesh->mVerts, mesh->mNorms);
 953   mesh->mEncodedNorms.set(NULL, 0);
 954
 955   mesh->computeBounds();
 956
 957   return mesh;
 958}
 959
 960bool TSShape::addMesh(TSMesh* mesh, const String& meshName)
 961{ 
 962   // Ensure mesh is in editable state
 963   mesh->makeEditable();
 964
 965   // Need to make everything editable since node indexes etc will change
 966   makeEditable();
 967
 968   // Determine the object name and detail size from the mesh name
 969   S32 detailSize = 999;
 970   String objName(String::GetTrailingNumber(meshName, detailSize));
 971
 972   // Find the destination object (create one if it does not exist)
 973   S32 objIndex = findObject(objName);
 974   if (objIndex < 0)
 975      objIndex = addObject(objName, 0);
 976   AssertFatal(objIndex >= 0 && objIndex < objects.size(), "Invalid object index!");
 977
 978   // Determine the subshape this object belongs to
 979   S32 subShapeIndex = getSubShapeForObject(objIndex);
 980   AssertFatal(subShapeIndex < subShapeFirstObject.size(), "Could not find subshape for object!");
 981
 982   // Get the existing detail levels for the subshape
 983   Vector<S32> validDetails;
 984   getSubShapeDetails(subShapeIndex, validDetails);
 985
 986   // Determine where to add the new mesh, and whether this is a new detail
 987   S32 detIndex;
 988   bool newDetail = true;
 989   for (detIndex = 0; detIndex < validDetails.size(); detIndex++)
 990   {
 991      const TSShape::Detail& det = details[validDetails[detIndex]];
 992      if (detailSize >= det.size)
 993      {
 994         newDetail = (det.size != detailSize);
 995         break;
 996      }
 997   }
 998
 999   // Insert the new detail level if required
1000   if (newDetail)
1001   {
1002      // Determine a name for the detail level
1003      const char* detailName;
1004      if (dStrStartsWith(objName, "Col"))
1005         detailName = "collision";
1006      else if (dStrStartsWith(objName, "loscol"))
1007         detailName = "los";
1008      else
1009         detailName = "detail";
1010
1011      S32 index = addDetail(detailName, detailSize, subShapeIndex);
1012      details[index].objectDetailNum = detIndex;
1013   }
1014
1015   // Adding a new mesh or detail level is a bit tricky, since each
1016   // object potentially stores a different number of meshes, including
1017   // NULL meshes for higher detail levels where required.
1018   // For example, the following table shows 3 objects. Note how NULLs
1019   // must be inserted for detail levels higher than the first valid
1020   // mesh, but details after the the last valid mesh are left empty.
1021   //
1022   // Detail   |  Object1  |  Object2  |  Object3
1023   // ---------+-----------+-----------+---------
1024   // 128      |  128      |  NULL     |  NULL
1025   // 64       |           |  NULL     |  64
1026   // 32       |           |  32       |  NULL
1027   // 2        |           |           |  2
1028
1029   // Add meshes as required for each object
1030   for (S32 i = 0; i < subShapeNumObjects[subShapeIndex]; i++)
1031   {
1032      S32 index = subShapeFirstObject[subShapeIndex] + i;
1033      const TSShape::Object& obj = objects[index];
1034
1035      if (index == objIndex)
1036      {
1037         // The target object: replace the existing mesh (if any) or add a new one
1038         // if required.
1039         if (!newDetail && (detIndex < obj.numMeshes))
1040         {
1041            if ( meshes[obj.startMeshIndex + detIndex] )
1042               destructInPlace(meshes[obj.startMeshIndex + detIndex]);
1043            meshes[obj.startMeshIndex + detIndex] = mesh;
1044         }
1045         else
1046            addMeshToObject(index, detIndex, mesh);
1047      }
1048      else
1049      {
1050         // Other objects: add a NULL mesh only if inserting before a valid mesh
1051         if (newDetail && (detIndex < obj.numMeshes))
1052            addMeshToObject(index, detIndex, NULL);
1053      }
1054   }
1055
1056   initObjects();
1057
1058   return true;
1059}
1060
1061bool TSShape::addMesh(TSShape* srcShape, const String& srcMeshName, const String& meshName)
1062{
1063   // Find the mesh in the source shape
1064   TSMesh* srcMesh = srcShape->findMesh(srcMeshName);
1065   if (!srcMesh)
1066   {
1067      Con::errorf("TSShape::addMesh: Could not find mesh '%s' in shape", srcMeshName.c_str());
1068      return false;
1069   }
1070
1071   // Copy the source mesh
1072   TSMesh *mesh = copyMesh( srcMesh );
1073   if (srcMesh->getMeshType() == TSMesh::SkinMeshType)
1074   {
1075      TSSkinMesh *srcSkin = dynamic_cast<TSSkinMesh*>(srcMesh);
1076
1077      // Check that the source skin is compatible with our skeleton
1078      Vector<S32> nodeMap(srcShape->nodes.size());
1079      for (S32 i = 0; i < srcShape->nodes.size(); i++)
1080         nodeMap.push_back( findNode( srcShape->getName(srcShape->nodes[i].nameIndex) ) );
1081
1082      for (S32 i = 0; i < srcSkin->boneIndex.size(); i++)
1083      {
1084         S32 srcNode = srcSkin->boneIndex[i];
1085         if (nodeMap[srcNode] == -1)
1086         {
1087            const char* name = srcShape->getName(srcShape->nodes[srcNode].nameIndex).c_str();
1088            Con::errorf("TSShape::addMesh: Skin is weighted to node (%s) that "
1089               "does not exist in this shape", name);
1090            return false;
1091         }
1092      }
1093
1094      TSSkinMesh *skin = dynamic_cast<TSSkinMesh*>(mesh);
1095
1096      // Remap node indices
1097      skin->batchData.nodeIndex = srcSkin->batchData.nodeIndex;
1098      for (S32 i = 0; i < skin->batchData.nodeIndex.size(); i++)
1099         skin->batchData.nodeIndex[i] = nodeMap[skin->batchData.nodeIndex[i]];
1100   }
1101
1102   // Add the copied mesh to the shape
1103   if (!addMesh(mesh, meshName))
1104   {
1105      delete mesh;
1106      return false;
1107   }
1108
1109   // Copy materials used by the source mesh (only if from a different shape)
1110   if (srcShape != this)
1111   {
1112      for (S32 i = 0; i < mesh->mPrimitives.size(); i++)
1113      {
1114         if (!(mesh->mPrimitives[i].matIndex & TSDrawPrimitive::NoMaterial))
1115         {
1116            S32 drawType = (mesh->mPrimitives[i].matIndex & (~<a href="/coding/class/structtsdrawprimitive/">TSDrawPrimitive</a>::MaterialMask));
1117            S32 srcMatIndex = mesh->mPrimitives[i].matIndex & TSDrawPrimitive::MaterialMask;
1118            const String& matName = srcShape->materialList->getMaterialName(srcMatIndex);
1119
1120            // Add the material if it does not already exist
1121            S32 destMatIndex = materialList->getMaterialNameList().find_next(matName);
1122            if (destMatIndex < 0)
1123            {
1124               destMatIndex = materialList->size();
1125               materialList->push_back(matName, srcShape->materialList->getFlags(srcMatIndex));
1126            }
1127
1128            mesh->mPrimitives[i].matIndex = drawType | destMatIndex;
1129         }
1130      }
1131   }
1132
1133   return true;
1134}
1135
1136bool TSShape::setMeshSize(const String& meshName, S32 size)
1137{
1138   S32 objIndex, meshIndex;
1139   if (!findMeshIndex(meshName, objIndex, meshIndex) ||
1140      !meshes[objects[objIndex].startMeshIndex + meshIndex])
1141   {
1142      Con::errorf("TSShape::setMeshSize: Could not find mesh '%s'", meshName.c_str());
1143      return false;
1144   }
1145
1146   // Need to make everything editable since node indexes etc will change
1147   makeEditable();
1148
1149   // Remove the mesh from the object, but don't destroy it
1150   TSShape::Object& obj = objects[objIndex];
1151   TSMesh* mesh = meshes[obj.startMeshIndex + meshIndex];
1152   removeMeshFromObject(objIndex, meshIndex);
1153
1154   // Add the mesh back at the new position
1155   addMesh(mesh, avar("%s %d", getName(obj.nameIndex).c_str(), size));
1156
1157   // Update smallest visible detail
1158   updateSmallestVisibleDL();
1159
1160   initObjects();
1161
1162   return true;
1163}
1164
1165bool TSShape::removeMesh(const String& meshName)
1166{
1167   S32 objIndex, meshIndex;
1168   if (!findMeshIndex(meshName, objIndex, meshIndex) ||
1169      !meshes[objects[objIndex].startMeshIndex + meshIndex])
1170   {
1171      Con::errorf("TSShape::removeMesh: Could not find mesh '%s'", meshName.c_str());
1172      return false;
1173   }
1174
1175   // Need to make everything editable since node indexes etc will change
1176   makeEditable();
1177
1178   // Destroy and remove the mesh
1179   TSShape::Object& obj = objects[objIndex];
1180   destructInPlace(meshes[obj.startMeshIndex + meshIndex]);
1181   removeMeshFromObject(objIndex, meshIndex);
1182
1183   // Remove the object if there are no meshes left
1184   if (!obj.numMeshes)
1185      removeObject(getName(obj.nameIndex));
1186
1187   // Update smallest visible detail
1188   updateSmallestVisibleDL();
1189
1190   initObjects();
1191
1192   return true;
1193}
1194
1195//-----------------------------------------------------------------------------
1196
1197// Helper function for dealing with some of the Vectors used in a TSShape. 'meshes'
1198// for example contains a TSMesh* per-object, per-detail-level, with NULLs for
1199// undefined details for each object. Trailing NULLs are not added to the Vector,
1200// so you end up with a different number of pointers for each object, depending
1201// on which detail levels it defines. This makes it tricky to move meshes around
1202// since you have to know how many elements belong to each object.
1203// To simplify things, this function pads the Vector up to a certain length (so
1204// all objects can appear to have the same number of meshes), the moves a single
1205// element to a new index, then trims trailing NULLs again.
1206template<class T>
1207static void _PadMoveAndTrim(Vector<T*>& vec, S32 offset, S32 count,
1208                              S32 padLength, S32 oldIndex, S32 newIndex)
1209{
1210   // Pad the array with NULLs
1211   for ( S32 i = count; i < padLength; ++i )
1212      vec.insert( offset + count, NULL );
1213
1214   // Move the element from the old to the new index
1215   T* tmp = vec[offset + oldIndex];
1216   vec.erase( offset + oldIndex );
1217   vec.insert( offset + newIndex, tmp );
1218
1219   // Trim trailing NULLs from the vector
1220   for ( S32 i = padLength - 1; i >= 0; --i )
1221   {
1222      if ( vec[offset + i] )
1223         break;
1224      else
1225         vec.erase( offset + i );
1226   }
1227}
1228
1229S32 TSShape::setDetailSize(S32 oldSize, S32 newSize)
1230{
1231   S32 oldIndex = findDetailBySize( oldSize );
1232   if ( oldIndex < 0 )
1233   {
1234      Con::errorf( "TSShape::setDetailSize: Cannot find detail with size %d", oldSize );
1235      return -1;
1236   }
1237
1238   // Remove this detail from the list
1239   TSShape::Detail tmpDetail = details[oldIndex];
1240   tmpDetail.size = newSize;
1241   details.erase(oldIndex);
1242
1243   // Determine the new position for the detail (details are sorted by size)
1244   S32 newIndex = 0;
1245   for ( newIndex = 0; newIndex < details.size(); ++newIndex )
1246   {
1247      if ( newSize > details[newIndex].size )
1248         break;
1249   }
1250
1251   // Add the detail at its new position
1252   details.insert( newIndex, tmpDetail );
1253
1254   // Rename the detail so its trailing size value is correct
1255   {
1256      S32 tmp;
1257      String oldName( getName( tmpDetail.nameIndex ) );
1258      String newName( String::GetTrailingNumber( oldName, tmp ) );
1259      newName += String::ToString( "%d", newSize );
1260      renameDetail(oldName, newName);
1261   }
1262
1263   if ( newIndex != oldIndex )
1264   {
1265      // Fixup details
1266      for ( S32 iDet = 0; iDet < details.size(); iDet++ )
1267      {
1268         if ( details[iDet].subShapeNum < 0 )
1269         {
1270            if ( details[iDet].bbDetailLevel == oldIndex )
1271               details[iDet].bbDetailLevel = newIndex;
1272         }
1273         else
1274         {
1275            details[iDet].objectDetailNum = iDet;
1276         }
1277      }
1278
1279      // Fixup Billboard details
1280      _PadMoveAndTrim( billboardDetails, 0, billboardDetails.size(),
1281                        details.size(), oldIndex, newIndex );
1282
1283      // Now move the mesh for each object in the subshape (adding and removing
1284      // NULLs as appropriate)
1285      for ( S32 iObj = 0; iObj < objects.size(); ++iObj )
1286      {
1287         TSShape::Object& obj = objects[iObj];
1288         S32 oldMeshCount = meshes.size();
1289
1290         _PadMoveAndTrim( meshes, obj.startMeshIndex, obj.numMeshes,
1291                           details.size(), oldIndex, newIndex );
1292
1293         obj.numMeshes += ( meshes.size() - oldMeshCount );
1294
1295         // Fixup startMeshIndex for remaining objects
1296         for ( S32 j = iObj + 1; j < objects.size(); ++j )
1297            objects[j].startMeshIndex += ( meshes.size() - oldMeshCount );
1298      }
1299   }
1300
1301   // Update smallest visible detail
1302   updateSmallestVisibleDL();
1303
1304   // Nothing major, just reint object lists
1305   initObjects();
1306
1307   return newIndex;
1308}
1309
1310bool TSShape::removeDetail( S32 size )
1311{
1312   S32 dl = findDetailBySize( size );
1313
1314   if ( ( dl < 0 ) || ( dl >= details.size() ) )
1315   {
1316      Con::errorf( "TSShape::removeDetail: Invalid detail index (%d)", dl );
1317      return false;
1318   }
1319
1320   // Need to make everything editable since node indexes etc will change
1321   makeEditable();
1322
1323   // Destroy and remove each mesh in the detail level
1324   for ( S32 objIndex = objects.size()-1; objIndex >= 0; objIndex-- )
1325   {
1326      TSShape::Object& obj = objects[objIndex];
1327      if ( dl < obj.numMeshes )
1328      {
1329         if ( meshes[obj.startMeshIndex + dl] )
1330            destructInPlace( meshes[obj.startMeshIndex + dl] );
1331         removeMeshFromObject(objIndex, dl);
1332
1333         // Remove the object if there are no meshes left
1334         if (!obj.numMeshes)
1335            removeObject( getName( obj.nameIndex ) );
1336      }
1337   }
1338
1339   // Destroy billboard detail level
1340   if ( dl < billboardDetails.size() )
1341   {
1342      if ( billboardDetails[dl] )
1343      {
1344         // Delete old textures
1345         billboardDetails[dl]->deleteImposterCacheTextures();
1346         delete billboardDetails[dl];
1347     }
1348     
1349      billboardDetails.erase( dl );
1350   }
1351
1352   // Update smallest visible detail
1353   updateSmallestVisibleDL();
1354
1355   initObjects();
1356
1357   return true;
1358}
1359
1360//-----------------------------------------------------------------------------
1361bool TSShape::addSequence(const Torque::Path& path, const String& fromSeq,
1362                          const String& name, S32 startFrame, S32 endFrame,
1363                          bool padRotKeys, bool padTransKeys)
1364{
1365   String oldName(fromSeq);
1366
1367   if (path.getExtension().equal("dsq", String::NoCase))
1368   {
1369      S32 oldSeqCount = sequences.size();
1370
1371      // DSQ source file
1372      char filenameBuf[1024];
1373      Con::expandScriptFilename(filenameBuf, sizeof(filenameBuf), path.getFullPath().c_str());
1374
1375      FileStream *f;
1376      if((f = FileStream::createAndOpen( filenameBuf, Torque::FS::File::Read )) == NULL)
1377      {
1378         Con::errorf("TSShape::addSequence: Could not load DSQ file '%s'", filenameBuf);
1379         return false;
1380      }
1381      if (!importSequences(f, filenameBuf) || (f->getStatus() != Stream::Ok))
1382      {
1383         delete f;
1384         Con::errorf("TSShape::addSequence: Load sequence file '%s' failed", filenameBuf);
1385         return false;
1386      }
1387      delete f;
1388
1389      // Rename the new sequence if required (avoid rename if name is not
1390      // unique (this will be fixed up later, and we don't need 2 errors about it!)
1391      if (oldName.isEmpty())
1392         oldName = getName(sequences.last().nameIndex);
1393      if (!oldName.equal(name))
1394      {
1395         if (findSequence(name) == -1)
1396         {
1397            // Use a dummy intermediate name since we might be renaming from an
1398            // existing name (and we want to rename the right sequence!)
1399            sequences.last().nameIndex = addName("__dummy__");
1400            renameSequence("__dummy__", name);
1401         }
1402      }
1403
1404      // Check that sequences have unique names
1405      bool lastSequenceRejected = false;
1406      for (S32 i = sequences.size()-1; i >= oldSeqCount; i--)
1407      {
1408         S32 nameIndex = (i == sequences.size()-1) ? findName(name) : sequences[i].nameIndex;
1409         S32 seqIndex = findSequence(nameIndex);
1410         if ((seqIndex != -1) && (seqIndex != i))
1411         {
1412            Con::errorf("TSShape::addSequence: Failed to add sequence '%s' "
1413               "(name already exists)", getName(nameIndex).c_str());
1414            sequences[i].nameIndex = addName("__dummy__");
1415            removeSequence("__dummy__");
1416            if (i == sequences.size())
1417               lastSequenceRejected = true;
1418         }
1419      }
1420
1421      // @todo:Need to remove keyframes if start!=0 and end!=-1
1422      TSShape::Sequence& seq = sequences.last();
1423
1424      // Store information about how this sequence was created
1425      seq.sourceData.from = String::ToString("%s\t%s", filenameBuf, name.c_str());
1426      seq.sourceData.total = seq.numKeyframes;
1427      seq.sourceData.start = ((startFrame < 0) || (startFrame >= seq.numKeyframes)) ? 0 : startFrame;
1428      seq.sourceData.end = ((endFrame < 0) || (endFrame >= seq.numKeyframes)) ? seq.numKeyframes-1 : endFrame;
1429
1430      return (sequences.size() != oldSeqCount);
1431   }
1432
1433   /* Check that sequence to be added does not already exist */
1434   if (findSequence(name) != -1)
1435   {
1436      Con::errorf("TSShape::addSequence: Cannot add sequence '%s' (name already exists)", name.c_str());
1437      return false;
1438   }
1439
1440   Resource<TSShape> hSrcShape;
1441   TSShape* srcShape = this;        // Assume we are copying an existing sequence
1442
1443   if (path.getExtension().equal("dts", String::NoCase) ||
1444       path.getExtension().equal("dae", String::NoCase))
1445   {
1446      // DTS or DAE source file
1447      char filenameBuf[1024];
1448      Con::expandScriptFilename(filenameBuf, sizeof(filenameBuf), path.getFullPath().c_str());
1449
1450      hSrcShape = ResourceManager::get().load(filenameBuf);
1451      if (!bool(hSrcShape))
1452      {
1453         Con::errorf("TSShape::addSequence: Could not load source shape '%s'", path.getFullPath().c_str());
1454         return false;
1455      }
1456      srcShape = const_cast<TSShape*>((const TSShape*)hSrcShape);
1457      if (!srcShape->sequences.size())
1458      {
1459         Con::errorf("TSShape::addSequence: Source shape '%s' does not contain any sequences", path.getFullPath().c_str());
1460         return false;
1461      }
1462
1463      // If no sequence name is specified, just use the first one
1464      if (oldName.isEmpty())
1465         oldName = srcShape->getName(srcShape->sequences[0].nameIndex);
1466   }
1467   else
1468   {
1469      // Source is an existing sequence
1470      oldName = path.getFullPath();
1471   }
1472
1473   // Find the sequence
1474   S32 seqIndex = srcShape->findSequence(oldName);
1475   if (seqIndex < 0)
1476   {
1477      Con::errorf("TSShape::addSequence (%s): Could not find sequence named '%s'", path.getFullPath().c_str(), oldName.c_str());
1478      return false;
1479   }
1480
1481   // Check keyframe range
1482   const TSShape::Sequence* srcSeq = &srcShape->sequences[seqIndex];
1483   if ((startFrame < 0) || (startFrame >= srcSeq->numKeyframes))
1484   {
1485      Con::warnf("TSShape::addSequence (%s): Start keyframe (%d) out of range (0-%d) for sequence '%s'",
1486         path.getFullPath().c_str(), startFrame, srcSeq->numKeyframes-1, oldName.c_str());
1487      startFrame = 0;
1488   }
1489   if (endFrame < 0)
1490      endFrame = srcSeq->numKeyframes - 1;
1491   else if (endFrame >= srcSeq->numKeyframes)
1492   {
1493      Con::warnf("TSShape::addSequence (%s): End keyframe (%d) out of range (0-%d) for sequence '%s'",
1494         path.getFullPath().c_str(), endFrame, srcSeq->numKeyframes-1, oldName.c_str());
1495      endFrame = srcSeq->numKeyframes - 1;
1496   }
1497
1498   // Create array to map source nodes to our nodes
1499   Vector<S32> nodeMap(srcShape->nodes.size());
1500   for (S32 i = 0; i < srcShape->nodes.size(); i++)
1501      nodeMap.push_back(findNode(srcShape->getName(srcShape->nodes[i].nameIndex)));
1502
1503   // Create array to map source objects to our objects
1504   Vector<S32> objectMap(srcShape->objects.size());
1505   for (S32 i = 0; i < srcShape->objects.size(); i++)
1506      objectMap.push_back(findObject(srcShape->getName(srcShape->objects[i].nameIndex)));
1507
1508   // Copy the source sequence (need to do it this ugly way instead of just
1509   // using push_back since srcSeq pointer may change if copying a sequence
1510   // from inside the shape itself
1511   sequences.increment();
1512   TSShape::Sequence& seq = sequences.last();
1513   srcSeq = &srcShape->sequences[seqIndex]; // update pointer as it may have changed!
1514   seq = *srcSeq;
1515
1516   seq.nameIndex = addName(name);
1517   seq.numKeyframes = endFrame - startFrame + 1;
1518   if (seq.duration > 0)
1519      seq.duration *= ((F32)seq.numKeyframes / srcSeq->numKeyframes);
1520
1521   // Add object states
1522   // Note: only visibility animation is supported
1523   seq.frameMatters.clearAll();
1524   seq.matFrameMatters.clearAll();
1525   seq.visMatters.clearAll();
1526   for (S32 i = 0; i < objectMap.size(); i++)
1527   {
1528      if (objectMap[i] < 0)
1529         continue;
1530
1531      if (srcSeq->visMatters.test(i))
1532      {
1533         // Check if visibility is animated within the frames to be copied
1534         const F32 defaultVis = srcShape->objectStates[i].vis;
1535         S32 objNum = srcSeq->visMatters.count(i);
1536         for (S32 iFrame = startFrame; iFrame <= endFrame; iFrame++)
1537         {
1538            if (srcShape->getObjectState(*srcSeq, iFrame, objNum).vis != defaultVis)
1539            {
1540               seq.visMatters.set(objectMap[i]);
1541               break;
1542            }
1543         }
1544      }
1545   }
1546
1547   TSIntegerSet srcObjectStateSet(srcSeq->frameMatters);
1548   srcObjectStateSet.overlap(srcSeq->matFrameMatters);
1549   srcObjectStateSet.overlap(srcSeq->visMatters);
1550
1551   TSIntegerSet objectStateSet(seq.frameMatters);
1552   objectStateSet.overlap(seq.matFrameMatters);
1553   objectStateSet.overlap(seq.visMatters);
1554
1555   seq.baseObjectState = objectStates.size();
1556   objectStates.increment(objectStateSet.count()*seq.numKeyframes);
1557   for (S32 i = 0; i < objectMap.size(); i++)
1558   {
1559      if (objectMap[i] < 0)
1560         continue;
1561
1562      // Note: only visibility animation is supported
1563      if (objectStateSet.test(objectMap[i]))
1564      {
1565         S32 src = srcSeq->baseObjectState + srcSeq->numKeyframes * srcObjectStateSet.count(i) + startFrame;
1566         S32 dest = seq.baseObjectState + seq.numKeyframes * objectStateSet.count(objectMap[i]);
1567         dCopyArray(&objectStates[dest], &srcShape->objectStates[src], seq.numKeyframes);
1568      }
1569   }
1570
1571   // Add ground frames
1572   F32 ratio = (F32)seq.numKeyframes / srcSeq->numKeyframes;
1573   S32 groundBase = srcSeq->firstGroundFrame + startFrame*ratio;
1574
1575   seq.numGroundFrames *= ratio;
1576   seq.firstGroundFrame = groundTranslations.size();
1577   groundTranslations.reserve(mMin(groundTranslations.size() + seq.numGroundFrames, srcShape->groundTranslations.size()));
1578   groundRotations.reserve(mMin(groundRotations.size() + seq.numGroundFrames, srcShape->groundRotations.size()));
1579   for (S32 i = 0; i < seq.numGroundFrames; i++)
1580   {
1581      S32 offset = groundBase + i;
1582      if (offset >= srcShape->groundTranslations.size())
1583      {
1584         Con::errorf("%s  groundTranslations out of bounds! [%i/%i] ", path.getFullPath().c_str(), groundBase + i, srcShape->groundTranslations.size());
1585         offset = srcShape->groundTranslations.size() - 1;
1586      }
1587
1588      groundTranslations.push_back(srcShape->groundTranslations[offset]);
1589
1590      S32 offset2 = groundBase + i;
1591      if (offset2 >= srcShape->groundRotations.size())
1592      {
1593         Con::errorf("%s  groundRotations out of bounds! [%i/%i] ", path.getFullPath().c_str(), groundBase + i, srcShape->groundRotations.size());
1594         offset2 = srcShape->groundRotations.size() - 1;
1595      }
1596
1597      groundRotations.push_back(srcShape->groundRotations[offset2]);
1598   }
1599
1600   // Add triggers
1601   seq.numTriggers = 0;
1602   seq.firstTrigger = triggers.size();
1603   F32 seqStartPos = (F32)startFrame / seq.numKeyframes;
1604   F32 seqEndPos = (F32)endFrame / seq.numKeyframes;
1605   for (S32 i = 0; i < srcSeq->numTriggers; i++)
1606   {
1607      const TSShape::Trigger& srcTrig = srcShape->triggers[srcSeq->firstTrigger + i];
1608      if ((srcTrig.pos >= seqStartPos) && (srcTrig.pos <= seqEndPos))
1609      {
1610         triggers.push_back(srcTrig);
1611         triggers.last().pos -= seqStartPos;
1612         seq.numTriggers++;
1613      }
1614   }
1615
1616   // Fixup node matters arrays
1617   seq.translationMatters.clearAll();
1618   seq.rotationMatters.clearAll();
1619   seq.scaleMatters.clearAll();
1620   for (S32 i = 0; i < nodeMap.size(); i++)
1621   {
1622      if (nodeMap[i] < 0)
1623         continue;
1624
1625      if (srcSeq->translationMatters.test(i))
1626      {
1627         // Check if node position is animated within the frames to be copied
1628         const Point3F& defaultTrans = srcShape->defaultTranslations[i];
1629         S32 tranNum = srcSeq->translationMatters.count(i);
1630         for (S32 iFrame = startFrame; iFrame <= endFrame; iFrame++)
1631         {
1632            if (srcShape->getTranslation(*srcSeq, iFrame, tranNum) != defaultTrans)
1633            {
1634               seq.translationMatters.set(nodeMap[i]);
1635               break;
1636            }
1637         }
1638      }
1639
1640      if (srcSeq->rotationMatters.test(i))
1641      {
1642         // Check if node rotation is animated within the frames to be copied
1643         const QuatF defaultRot = srcShape->defaultRotations[i].getQuatF();
1644         S32 rotNum = srcSeq->rotationMatters.count(i);
1645         for (S32 iFrame = startFrame; iFrame <= endFrame; iFrame++)
1646         {
1647            QuatF temp;
1648            if (srcShape->getRotation(*srcSeq, iFrame, rotNum, &temp) != defaultRot)
1649            {
1650               seq.rotationMatters.set(nodeMap[i]);
1651               break;
1652            }
1653         }
1654      }
1655      if (srcSeq->scaleMatters.test(i))
1656      {
1657         S32 scaleNum = srcSeq->scaleMatters.count(i);
1658
1659         // Check if node scale is animated within the frames to be copied
1660         if (srcSeq->animatesArbitraryScale())
1661         {
1662            TSScale defaultScale;
1663            defaultScale.identity();
1664            for (S32 iFrame = startFrame; iFrame <= endFrame; iFrame++)
1665            {
1666               TSScale temp;
1667               if (!(srcShape->getArbitraryScale(*srcSeq, iFrame, scaleNum, &temp) == defaultScale))
1668               {
1669                  seq.scaleMatters.set(nodeMap[i]);
1670                  break;
1671               }
1672            }
1673         }
1674         else if (srcSeq->animatesAlignedScale())
1675         {
1676            const Point3F defaultScale(Point3F::One);
1677            for (S32 iFrame = startFrame; iFrame <= endFrame; iFrame++)
1678            {
1679               if (srcShape->getAlignedScale(*srcSeq, iFrame, scaleNum) != defaultScale)
1680               {
1681                  seq.scaleMatters.set(nodeMap[i]);
1682                  break;
1683               }
1684            }
1685         }
1686         else if (srcSeq->animatesUniformScale())
1687         {
1688            const F32 defaultScale = 1.0f;
1689            for (S32 iFrame = startFrame; iFrame <= endFrame; iFrame++)
1690            {
1691               if (srcShape->getUniformScale(*srcSeq, iFrame, scaleNum) != defaultScale)
1692               {
1693                  seq.scaleMatters.set(nodeMap[i]);
1694                  break;
1695               }
1696            }
1697         }
1698      }
1699   }
1700
1701   // Resize the node transform arrays
1702   seq.baseTranslation = nodeTranslations.size();
1703   nodeTranslations.increment(seq.translationMatters.count()*seq.numKeyframes);
1704   seq.baseRotation = nodeRotations.size();
1705   nodeRotations.increment(seq.rotationMatters.count()*seq.numKeyframes);
1706   if (seq.flags & TSShape::ArbitraryScale)
1707   {
1708      S32 scaleCount = seq.scaleMatters.count();
1709      seq.baseScale = nodeArbitraryScaleRots.size();
1710      nodeArbitraryScaleRots.increment(scaleCount*seq.numKeyframes);
1711      nodeArbitraryScaleFactors.increment(scaleCount*seq.numKeyframes);
1712   }
1713   else if (seq.flags & TSShape::AlignedScale)
1714   {
1715      seq.baseScale = nodeAlignedScales.size();
1716      nodeAlignedScales.increment(seq.scaleMatters.count()*seq.numKeyframes);
1717   }
1718   else
1719   {
1720      seq.baseScale = nodeUniformScales.size();
1721      nodeUniformScales.increment(seq.scaleMatters.count()*seq.numKeyframes);
1722   }
1723
1724   // Add node transforms (remap from source node indices to our node indices). As
1725   // well as copying animated node translations and rotations, also handle when the
1726   // default translation and rotation are different between the source and
1727   // destination shapes.
1728   for (S32 i = 0; i < nodeMap.size(); i++)
1729   {
1730      if (nodeMap[i] < 0)
1731         continue;
1732
1733      if (seq.translationMatters.test(nodeMap[i]))
1734      {
1735         S32 src = srcSeq->baseTranslation + srcSeq->numKeyframes * srcSeq->translationMatters.count(i) + startFrame;
1736         S32 dest = seq.baseTranslation + seq.numKeyframes * seq.translationMatters.count(nodeMap[i]);
1737         dCopyArray(&nodeTranslations[dest], &srcShape->nodeTranslations[src], seq.numKeyframes);
1738      }
1739      else if (padTransKeys && (defaultTranslations[nodeMap[i]] != srcShape->defaultTranslations[i]))
1740      {
1741         seq.translationMatters.set(nodeMap[i]);
1742         S32 dest = seq.baseTranslation + seq.numKeyframes * seq.translationMatters.count(nodeMap[i]);
1743         for (S32 j = 0; j < seq.numKeyframes; j++)
1744            nodeTranslations.insert(dest, srcShape->defaultTranslations[i]);
1745      }
1746
1747      if (seq.rotationMatters.test(nodeMap[i]))
1748      {
1749         S32 src = srcSeq->baseRotation + srcSeq->numKeyframes * srcSeq->rotationMatters.count(i) + startFrame;
1750         S32 dest = seq.baseRotation + seq.numKeyframes * seq.rotationMatters.count(nodeMap[i]);
1751         dCopyArray(&nodeRotations[dest], &srcShape->nodeRotations[src], seq.numKeyframes);
1752      }
1753      else if (padRotKeys && (defaultRotations[nodeMap[i]] != srcShape->defaultRotations[i]))
1754      {
1755         seq.rotationMatters.set(nodeMap[i]);
1756         S32 dest = seq.baseRotation + seq.numKeyframes * seq.rotationMatters.count(nodeMap[i]);
1757         for (S32 j = 0; j < seq.numKeyframes; j++)
1758            nodeRotations.insert(dest, srcShape->defaultRotations[i]);
1759      }
1760
1761      if (seq.scaleMatters.test(nodeMap[i]))
1762      {
1763         S32 src = srcSeq->baseScale + srcSeq->numKeyframes * srcSeq->scaleMatters.count(i)+ startFrame;
1764         S32 dest = seq.baseScale + seq.numKeyframes * seq.scaleMatters.count(nodeMap[i]);
1765         if (seq.flags & TSShape::ArbitraryScale)
1766         {
1767            dCopyArray(&nodeArbitraryScaleRots[dest], &srcShape->nodeArbitraryScaleRots[src], seq.numKeyframes);
1768            dCopyArray(&nodeArbitraryScaleFactors[dest], &srcShape->nodeArbitraryScaleFactors[src], seq.numKeyframes);
1769         }
1770         else if (seq.flags & TSShape::AlignedScale)
1771            dCopyArray(&nodeAlignedScales[dest], &srcShape->nodeAlignedScales[src], seq.numKeyframes);
1772         else
1773            dCopyArray(&nodeUniformScales[dest], &srcShape->nodeUniformScales[src], seq.numKeyframes);
1774      }
1775   }
1776
1777   // Set shape flags (only the most significant scale type)
1778   U32 curVal = mFlags & AnyScale;
1779   mFlags &= ~(AnyScale);
1780   mFlags |= getMax(curVal, seq.flags & AnyScale);    // take the larger value (can only convert upwards)
1781
1782   // Set sequence flags
1783   seq.dirtyFlags = 0;
1784   if (seq.rotationMatters.testAll() || seq.translationMatters.testAll() || seq.scaleMatters.testAll())
1785      seq.dirtyFlags |= TSShapeInstance::TransformDirty;
1786   if (seq.visMatters.testAll())
1787      seq.dirtyFlags |= TSShapeInstance::VisDirty;
1788   if (seq.frameMatters.testAll())
1789      seq.dirtyFlags |= TSShapeInstance::FrameDirty;
1790   if (seq.matFrameMatters.testAll())
1791      seq.dirtyFlags |= TSShapeInstance::MatFrameDirty;
1792
1793   // Store information about how this sequence was created
1794   seq.sourceData.from = String::ToString("%s\t%s", path.getFullPath().c_str(), oldName.c_str());
1795   seq.sourceData.total = srcSeq->numKeyframes;
1796   seq.sourceData.start = startFrame;
1797   seq.sourceData.end = endFrame;
1798
1799   return true;
1800}
1801
1802bool TSShape::removeSequence(const String& name)
1803{
1804   // Find the sequence to be removed
1805   S32 seqIndex = findSequence(name);
1806   if (seqIndex < 0)
1807   {
1808      Con::errorf("TSShape::removeSequence: Could not find sequence '%s'", name.c_str());
1809      return false;
1810   }
1811
1812   TSShape::Sequence& seq = sequences[seqIndex];
1813
1814   // Remove the node transforms for this sequence
1815   S32 transCount = eraseStates(nodeTranslations, seq.translationMatters, seq.baseTranslation, seq.numKeyframes);
1816   S32 rotCount = eraseStates(nodeRotations, seq.rotationMatters, seq.baseRotation, seq.numKeyframes);
1817   S32 scaleCount = 0;
1818   if (seq.flags & TSShape::ArbitraryScale)
1819   {
1820      scaleCount = eraseStates(nodeArbitraryScaleRots, seq.scaleMatters, seq.baseScale, seq.numKeyframes);
1821      eraseStates(nodeArbitraryScaleFactors, seq.scaleMatters, seq.baseScale, seq.numKeyframes);
1822   }
1823   else if (seq.flags & TSShape::AlignedScale)
1824      scaleCount = eraseStates(nodeAlignedScales, seq.scaleMatters, seq.baseScale, seq.numKeyframes);
1825   else
1826      scaleCount = eraseStates(nodeUniformScales, seq.scaleMatters, seq.baseScale, seq.numKeyframes);
1827
1828   // Remove the object states for this sequence
1829   TSIntegerSet objMatters(seq.frameMatters);
1830   objMatters.overlap(seq.matFrameMatters);
1831   objMatters.overlap(seq.visMatters);
1832   S32 objCount = eraseStates(objectStates, objMatters, seq.baseObjectState, seq.numKeyframes);
1833
1834   // Remove groundframes and triggers
1835   TSIntegerSet dummy;
1836   eraseStates(groundTranslations, dummy, seq.firstGroundFrame, seq.numGroundFrames, 0);
1837   eraseStates(groundRotations, dummy, seq.firstGroundFrame, seq.numGroundFrames, 0);
1838   eraseStates(triggers, dummy, seq.firstTrigger, seq.numTriggers, 0);
1839
1840   // Fixup the base indices of the other sequences
1841   for (S32 i = seqIndex + 1; i < sequences.size(); i++)
1842   {
1843      sequences[i].baseTranslation -= transCount;
1844      sequences[i].baseRotation -= rotCount;
1845      sequences[i].baseScale -= scaleCount;
1846      sequences[i].baseObjectState -= objCount;
1847      sequences[i].firstGroundFrame -= seq.numGroundFrames;
1848      sequences[i].firstTrigger -= seq.numTriggers;
1849   }
1850
1851   // Remove the sequence itself
1852   sequences.erase(seqIndex);
1853
1854   // Remove the sequence name if it is no longer in use
1855   removeName(name);
1856
1857   return true;
1858}
1859
1860//-----------------------------------------------------------------------------
1861
1862bool TSShape::addTrigger(const String& seqName, S32 keyframe, S32 state)
1863{
1864   // Find the sequence
1865   S32 seqIndex = findSequence(seqName);
1866   if (seqIndex < 0)
1867   {
1868      Con::errorf("TSShape::addTrigger: Could not find sequence '%s'", seqName.c_str());
1869      return false;
1870   }
1871
1872   TSShape::Sequence& seq = sequences[seqIndex];
1873   if (keyframe >= seq.numKeyframes)
1874   {
1875      Con::errorf("TSShape::addTrigger: Keyframe out of range (0-%d for sequence '%s')",
1876         seq.numKeyframes-1, seqName.c_str());
1877      return false;
1878   }
1879
1880   // Encode the trigger state
1881   if (state < 0)
1882      state = 1 << (-state-1);
1883   else if (state > 0)
1884      state = (1 << (state-1)) | TSShape::Trigger::StateOn;
1885
1886   // Fixup seq.firstTrigger if this sequence does not have any triggers yet
1887   if (seq.numTriggers == 0)
1888   {
1889      seq.firstTrigger = 0;
1890      for (S32 i = 0; i < seqIndex; i++)
1891         seq.firstTrigger += sequences[i].numTriggers;
1892   }
1893
1894   // Find where to insert the trigger (sorted by keyframe)
1895   S32 trigIndex;
1896   for (trigIndex = seq.firstTrigger; trigIndex < (seq.firstTrigger + seq.numTriggers); trigIndex++)
1897   {
1898      const TSShape::Trigger& trig = triggers[trigIndex];
1899      if ((S32)(trig.pos * seq.numKeyframes) > keyframe)
1900         break;
1901   }
1902
1903   // Create the new trigger
1904   TSShape::Trigger trig;
1905   trig.pos = (F32)keyframe / getMax(1, seq.numKeyframes-1);
1906   trig.state = state;
1907   triggers.insert(trigIndex, trig);
1908   seq.numTriggers++;
1909
1910   // set invert for other triggers if needed
1911   if ((trig.state & TSShape::Trigger::StateOn) == 0)
1912   {
1913      U32 offTrigger = (trig.state & TSShape::Trigger::StateMask);
1914      for (S32 i = 0; i < seq.numTriggers; i++)
1915      {
1916         if (triggers[seq.firstTrigger + i].state & offTrigger)
1917            triggers[seq.firstTrigger + i].state |= TSShape::Trigger::InvertOnReverse;
1918      }
1919   }
1920
1921   // fixup firstTrigger index for other sequences
1922   for (S32 i = seqIndex + 1; i < sequences.size(); i++)
1923   {
1924      if (sequences[i].numTriggers > 0)
1925         sequences[i].firstTrigger++;
1926   }
1927
1928   // set MakePath flag so triggers will be animated
1929   seq.flags |= TSShape::MakePath;
1930
1931   return true;
1932}
1933
1934bool TSShape::removeTrigger(const String& seqName, S32 keyframe, S32 state)
1935{
1936   // Find the sequence
1937   S32 seqIndex = findSequence(seqName);
1938   if (seqIndex < 0)
1939   {
1940      Con::errorf("TSShape::removeTrigger: Could not find sequence '%s'", seqName.c_str());
1941      return false;
1942   }
1943
1944   TSShape::Sequence& seq = sequences[seqIndex];
1945   if (keyframe >= seq.numKeyframes)
1946   {
1947      Con::errorf("TSShape::removeTrigger: Keyframe out of range (0-%d for sequence '%s')",
1948         seq.numKeyframes-1, seqName.c_str());
1949      return false;
1950   }
1951
1952   // Encode the trigger state
1953   if (state < 0)
1954      state = 1 << (-state-1);
1955   else if (state > 0)
1956      state = (1 << (state-1)) | TSShape::Trigger::StateOn;
1957
1958   // Find and remove the trigger
1959   for (S32 trigIndex = seq.firstTrigger; trigIndex < (seq.firstTrigger + seq.numTriggers); trigIndex++)
1960   {
1961      TSShape::Trigger& trig = triggers[trigIndex];
1962      S32 cmpFrame = (S32)(trig.pos * (seq.numKeyframes-1) + 0.5f); 
1963      S32 cmpState = trig.state & (~TSShape::Trigger::InvertOnReverse);
1964
1965      if ((cmpFrame == keyframe) && (cmpState == state))
1966      {
1967         triggers.erase(trigIndex);
1968         seq.numTriggers--;
1969
1970         // Fix up firstTrigger for other sequences
1971         for (S32 i = seqIndex + 1; i < sequences.size(); i++)
1972         {
1973            if (sequences[i].numTriggers > 0)
1974               sequences[i].firstTrigger--;
1975         }
1976
1977         // Clear MakePath flag if no more triggers
1978         if ( seq.numTriggers == 0 )
1979            seq.flags &= (~TSShape::MakePath);
1980
1981         return true;
1982      }
1983   }
1984
1985   Con::errorf("TSShape::removeTrigger: Could not find trigger (%d, %d) for sequence '%s'",
1986      keyframe, state, seqName.c_str());
1987
1988   return false;
1989}
1990
1991void TSShape::getNodeKeyframe(S32 nodeIndex, const TSShape::Sequence& seq, S32 keyframe, MatrixF* mat) const
1992{
1993   // Get the node rotation and translation
1994   QuatF rot;
1995   if (seq.rotationMatters.test(nodeIndex))
1996   {
1997      S32 index = seq.rotationMatters.count(nodeIndex) * seq.numKeyframes + keyframe;
1998      nodeRotations[seq.baseRotation + index].getQuatF(&rot);
1999   }   
2000   else
2001      defaultRotations[nodeIndex].getQuatF(&rot);
2002
2003   Point3F trans;
2004   if (seq.translationMatters.test(nodeIndex))
2005   {
2006      S32 index = seq.translationMatters.count(nodeIndex) * seq.numKeyframes + keyframe;
2007      trans = nodeTranslations[seq.baseTranslation + index];
2008   }
2009   else
2010      trans = defaultTranslations[nodeIndex];
2011
2012   // Set the keyframe matrix
2013   rot.setMatrix(mat);
2014   mat->setPosition(trans);
2015}
2016
2017bool TSShape::setSequenceBlend(const String& seqName, bool blend, const String& blendRefSeqName, S32 blendRefFrame)
2018{
2019   // Find the target sequence
2020   S32 seqIndex = findSequence(seqName);
2021   if (seqIndex < 0)
2022   {
2023      Con::errorf("TSShape::setSequenceBlend: Could not find sequence named '%s'", seqName.c_str());
2024      return false;
2025   }
2026   TSShape::Sequence& seq = sequences[seqIndex];
2027
2028   // Ignore if blend flag is already correct
2029   if (seq.isBlend() == blend)
2030      return true;
2031
2032   // Find the sequence containing the reference frame
2033   S32 blendRefSeqIndex = findSequence(blendRefSeqName);
2034   if (blendRefSeqIndex < 0)
2035   {
2036      Con::errorf("TSShape::setSequenceBlend: Could not find reference sequence named '%s'", blendRefSeqName.c_str());
2037      return false;
2038   }
2039   TSShape::Sequence& blendRefSeq = sequences[blendRefSeqIndex];
2040
2041   if ((blendRefFrame < 0) || (blendRefFrame >= blendRefSeq.numKeyframes))
2042   {
2043      Con::errorf("TSShape::setSequenceBlend: Reference frame out of range (0-%d)", blendRefSeq.numKeyframes-1);
2044      return false;
2045   }
2046
2047   // Set the new flag
2048   if (blend)
2049      seq.flags |= TSShape::Blend;
2050   else
2051      seq.flags &= (~TSShape::Blend);
2052
2053   // For each animated node in the target sequence, need to add or subtract the
2054   // reference keyframe from each frame
2055   TSIntegerSet nodeMatters(seq.rotationMatters);
2056   nodeMatters.overlap(seq.translationMatters);
2057
2058   S32 end = nodeMatters.end();
2059   for (S32 nodeIndex = nodeMatters.start(); nodeIndex < end; nodeMatters.next(nodeIndex))
2060   {
2061      MatrixF refMat;
2062      getNodeKeyframe(nodeIndex, blendRefSeq, blendRefFrame, &refMat);
2063
2064      // Add or subtract the reference?
2065      if (blend)
2066         refMat.inverse();
2067
2068      bool updateRot(false), updateTrans(false);
2069      S32 rotOffset(0), transOffset(0);
2070      if (seq.rotationMatters.test(nodeIndex))
2071      {
2072         updateRot = true;
2073         rotOffset = seq.baseRotation + seq.rotationMatters.count(nodeIndex) * seq.numKeyframes;
2074      }
2075      if (seq.translationMatters.test(nodeIndex))
2076      {
2077         updateTrans = true;
2078         transOffset = seq.baseTranslation + seq.translationMatters.count(nodeIndex) * seq.numKeyframes;
2079      }
2080
2081      for (S32 frame = 0; frame < seq.numKeyframes; frame++)
2082      {
2083         MatrixF oldMat;
2084         getNodeKeyframe(nodeIndex, seq, frame, &oldMat);
2085
2086         MatrixF newMat;
2087         newMat.mul(refMat, oldMat);
2088
2089         if (updateRot)
2090            nodeRotations[rotOffset + frame].set(QuatF(newMat));
2091         if (updateTrans)
2092            nodeTranslations[transOffset + frame] = newMat.getPosition();
2093      }
2094   }
2095
2096   // Update sequence blend information
2097   seq.sourceData.blendSeq = blendRefSeqName;
2098   seq.sourceData.blendFrame = blendRefFrame;
2099
2100   return true;
2101}
2102
2103bool TSShape::setSequenceGroundSpeed(const String& seqName, const Point3F& trans, const Point3F& rot)
2104{
2105   // Find the sequence
2106   S32 seqIndex = findSequence(seqName);
2107   if (seqIndex < 0)
2108   {
2109      Con::errorf("setSequenceGroundSpeed: Could not find sequence named '%s'", seqName.c_str());
2110      return false;
2111   }
2112   TSShape::Sequence& seq = sequences[seqIndex];
2113
2114   // Determine how many ground-frames to generate (FPS=10, at least 1 frame)
2115   const F32 groundFrameRate = 10.0f;
2116   S32 numFrames = getMax(1, (S32)(seq.duration * groundFrameRate));
2117
2118   // Allocate space for the frames (add/delete frames as required)
2119   S32 frameAdjust = numFrames - seq.numGroundFrames;
2120   for (S32 i = 0; i < mAbs(frameAdjust); i++)
2121   {
2122      if (frameAdjust > 0)
2123      {
2124         groundTranslations.insert(seq.firstGroundFrame);
2125         groundRotations.insert(seq.firstGroundFrame);
2126      }
2127      else
2128      {
2129         groundTranslations.erase(seq.firstGroundFrame);
2130         groundRotations.erase(seq.firstGroundFrame);
2131      }
2132   }
2133
2134   // Fixup ground frame indices
2135   seq.numGroundFrames += frameAdjust;
2136   for (S32 i = seqIndex + 1; i < sequences.size(); i++)
2137      sequences[i].firstGroundFrame += frameAdjust;
2138
2139   // Generate the ground-frames
2140   Point3F adjTrans = trans;
2141   Point3F adjRot = rot;
2142   if (seq.numGroundFrames > 0)
2143   {
2144      adjTrans /= seq.numGroundFrames;
2145      adjRot /= seq.numGroundFrames;
2146   }
2147   QuatF rotSpeed(adjRot);
2148   QuatF groundRot(rotSpeed);
2149   for (S32 i = 0; i < seq.numGroundFrames; i++)
2150   {
2151      groundTranslations[seq.firstGroundFrame + i] = adjTrans * (i + 1);
2152      groundRotations[seq.firstGroundFrame + i].set(groundRot);
2153      groundRot *= rotSpeed;
2154   }
2155
2156   // set MakePath flag so ground frames will be animated
2157   seq.flags |= TSShape::MakePath;
2158
2159   return true;
2160}
2161
2162void TSShape::makeEditable()
2163{
2164   mNeedReinit = true;
2165   if (mShapeVertexData.base == NULL)
2166      return;
2167
2168   for (U32 i = 0; i < meshes.size(); i++)
2169   {
2170      if (meshes[i])
2171      {
2172         meshes[i]->makeEditable();
2173      }
2174   }
2175
2176   mShapeVertexData.set(NULL, 0);
2177}
2178
2179bool TSShape::needsReinit()
2180{
2181   return mVertexSize == 0 || mShapeVertexData.base == NULL || mNeedReinit;
2182}
2183