2
0
Эх сурвалжийг харах

Merge pull request #1756 from jamesu/hw_skinning_fix

Fix load with DTS shapes introduced with HW skinning changes
Anis 9 жил өмнө
parent
commit
2add87a702

+ 15 - 3
Engine/source/ts/tsMesh.cpp

@@ -2606,7 +2606,7 @@ void TSMesh::disassemble()
    tsalloc.copyToBuffer32( (S32*)&mCenter, 3 );
    tsalloc.set32( (S32)mRadius );
 
-   bool shouldMakeEditable = TSShape::smVersion < 27;
+   bool shouldMakeEditable = TSShape::smVersion < 27 || mVertSize == 0;
 
    // Re-create the vectors
    if (shouldMakeEditable)
@@ -2799,6 +2799,18 @@ void TSSkinMesh::assemble( bool skip )
          batchData.initialNorms.set((Point3F*)ptr32, numVerts);
          encodedNorms.set(NULL, 0);
       }
+
+      // Sometimes we'll have a mesh with 0 verts but initialVerts is set,
+      // so set these accordingly
+      if (verts.size() == 0)
+      {
+         verts = batchData.initialVerts;
+      }
+
+      if (norms.size() == 0)
+      {
+         norms = batchData.initialNorms;
+      }
    }
    else
    {
@@ -3429,8 +3441,8 @@ void TSBasicVertexFormat::addMeshRequirements(TSMesh *mesh)
    bool hasTexcoord2 = false;
    bool hasSkin = false;
 
-   hasColors = mesh->getHasColor() || (texCoordOffset != -1);
-   hasTexcoord2 = mesh->getHasTVert2() || (colorOffset != -1);
+   hasColors = mesh->getHasColor() || (colorOffset != -1);
+   hasTexcoord2 = mesh->getHasTVert2() || (texCoordOffset != -1);
    hasSkin = (mesh->getMeshType() == TSMesh::SkinMeshType) || (boneOffset != -1);
 
    S32 offset = sizeof(TSMesh::__TSMeshVertexBase);

+ 67 - 48
Engine/source/ts/tsShape.cpp

@@ -71,6 +71,7 @@ TSShape::TSShape()
    mShapeDataSize = 0;
 
    mUseDetailFromScreenError = false;
+   mNeedReinit = false;
 
    mDetailLevelLookup.setSize( 1 );
    mDetailLevelLookup[0].set( -1, 0 );
@@ -412,44 +413,52 @@ void TSShape::getObjectDetails(S32 objIndex, Vector<S32>& objDetails)
 }
 
 void TSShape::init()
+{
+   initObjects();
+   initVertexFeatures();
+   initMaterialList();
+   mNeedReinit = false;
+}
+
+void TSShape::initObjects()
 {
    S32 numSubShapes = subShapeFirstNode.size();
-   AssertFatal(numSubShapes==subShapeFirstObject.size(),"TSShape::init");
+   AssertFatal(numSubShapes == subShapeFirstObject.size(), "TSShape::initObjects");
 
-   S32 i,j;
+   S32 i, j;
 
    // set up parent/child relationships on nodes and objects
-   for (i=0; i<nodes.size(); i++)
+   for (i = 0; i<nodes.size(); i++)
       nodes[i].firstObject = nodes[i].firstChild = nodes[i].nextSibling = -1;
-   for (i=0; i<nodes.size(); i++)
+   for (i = 0; i<nodes.size(); i++)
    {
       S32 parentIndex = nodes[i].parentIndex;
-      if (parentIndex>=0)
+      if (parentIndex >= 0)
       {
          if (nodes[parentIndex].firstChild<0)
-            nodes[parentIndex].firstChild=i;
+            nodes[parentIndex].firstChild = i;
          else
          {
             S32 child = nodes[parentIndex].firstChild;
-            while (nodes[child].nextSibling>=0)
+            while (nodes[child].nextSibling >= 0)
                child = nodes[child].nextSibling;
             nodes[child].nextSibling = i;
          }
       }
    }
-   for (i=0; i<objects.size(); i++)
+   for (i = 0; i<objects.size(); i++)
    {
       objects[i].nextSibling = -1;
 
       S32 nodeIndex = objects[i].nodeIndex;
-      if (nodeIndex>=0)
+      if (nodeIndex >= 0)
       {
          if (nodes[nodeIndex].firstObject<0)
             nodes[nodeIndex].firstObject = i;
          else
          {
             S32 objectIndex = nodes[nodeIndex].firstObject;
-            while (objects[objectIndex].nextSibling>=0)
+            while (objects[objectIndex].nextSibling >= 0)
                objectIndex = objects[objectIndex].nextSibling;
             objects[objectIndex].nextSibling = i;
          }
@@ -457,7 +466,7 @@ void TSShape::init()
    }
 
    mFlags = 0;
-   for (i=0; i<sequences.size(); i++)
+   for (i = 0; i<sequences.size(); i++)
    {
       if (!sequences[i].animatesScale())
          continue;
@@ -465,32 +474,32 @@ void TSShape::init()
       U32 curVal = mFlags & AnyScale;
       U32 newVal = sequences[i].flags & AnyScale;
       mFlags &= ~(AnyScale);
-      mFlags |= getMax(curVal,newVal); // take the larger value (can only convert upwards)
+      mFlags |= getMax(curVal, newVal); // take the larger value (can only convert upwards)
    }
 
    // set up alphaIn and alphaOut vectors...
    alphaIn.setSize(details.size());
    alphaOut.setSize(details.size());
 
-   for (i=0; i<details.size(); i++)
+   for (i = 0; i<details.size(); i++)
    {
       if (details[i].size<0)
       {
          // we don't care...
-         alphaIn[i]  = 0.0f;
+         alphaIn[i] = 0.0f;
          alphaOut[i] = 0.0f;
       }
-      else if (i+1==details.size() || details[i+1].size<0)
+      else if (i + 1 == details.size() || details[i + 1].size<0)
       {
-         alphaIn[i]  = 0.0f;
+         alphaIn[i] = 0.0f;
          alphaOut[i] = smAlphaOutLastDetail;
       }
       else
       {
-         if (details[i+1].subShapeNum<0)
+         if (details[i + 1].subShapeNum<0)
          {
             // following detail is a billboard detail...treat special...
-            alphaIn[i]  = smAlphaInBillboard;
+            alphaIn[i] = smAlphaInBillboard;
             alphaOut[i] = smAlphaOutBillboard;
          }
          else
@@ -502,7 +511,7 @@ void TSShape::init()
       }
    }
 
-   for (i=mSmallestVisibleDL-1; i>=0; i--)
+   for (i = mSmallestVisibleDL - 1; i >= 0; i--)
    {
       if (i<smNumSkipLoadDetails)
       {
@@ -510,19 +519,19 @@ void TSShape::init()
          // is larger than our cap...zap all the meshes and decals
          // associated with it and use the next detail level
          // instead...
-         S32 ss    = details[i].subShapeNum;
-         S32 od    = details[i].objectDetailNum;
+         S32 ss = details[i].subShapeNum;
+         S32 od = details[i].objectDetailNum;
 
-         if (ss==details[i+1].subShapeNum && od==details[i+1].objectDetailNum)
+         if (ss == details[i + 1].subShapeNum && od == details[i + 1].objectDetailNum)
             // doh! already done this one (init can be called multiple times on same shape due
             // to sequence importing).
             continue;
-         details[i].subShapeNum = details[i+1].subShapeNum;
-         details[i].objectDetailNum = details[i+1].objectDetailNum;
+         details[i].subShapeNum = details[i + 1].subShapeNum;
+         details[i].objectDetailNum = details[i + 1].objectDetailNum;
       }
    }
 
-   for (i=0; i<details.size(); i++)
+   for (i = 0; i<details.size(); i++)
    {
       S32 count = 0;
       S32 ss = details[i].subShapeNum;
@@ -534,13 +543,13 @@ void TSShape::init()
          continue;
       }
       S32 start = subShapeFirstObject[ss];
-      S32 end   = start + subShapeNumObjects[ss];
-      for (j=start; j<end; j++)
+      S32 end = start + subShapeNumObjects[ss];
+      for (j = start; j<end; j++)
       {
          Object & obj = objects[j];
          if (od<obj.numMeshes)
          {
-            TSMesh * mesh = meshes[obj.startMeshIndex+od];
+            TSMesh * mesh = meshes[obj.startMeshIndex + od];
             count += mesh ? mesh->getNumPolys() : 0;
          }
       }
@@ -555,11 +564,11 @@ void TSShape::init()
       {
          ConvexHullAccelerator* accel = detailCollisionAccelerators[dca];
          if (accel != NULL) {
-            delete [] accel->vertexList;
-            delete [] accel->normalList;
+            delete[] accel->vertexList;
+            delete[] accel->normalList;
             for (S32 j = 0; j < accel->numVerts; j++)
-               delete [] accel->emitStrings[j];
-            delete [] accel->emitStrings;
+               delete[] accel->emitStrings[j];
+            delete[] accel->emitStrings;
             delete accel;
          }
       }
@@ -576,7 +585,12 @@ void TSShape::init()
       if (!mesh)
          continue;
 
-      if (mesh->parentMesh >= 0)
+      if (mesh->parentMesh >= meshes.size())
+      {
+         Con::warnf("Mesh %i has a bad parentMeshObject (%i)", iter - meshes.begin(), mesh->parentMesh);
+      }
+
+      if (mesh->parentMesh >= 0 && mesh->parentMesh < meshes.size())
       {
          mesh->parentMeshObject = meshes[mesh->parentMesh];
       }
@@ -587,9 +601,6 @@ void TSShape::init()
 
       mesh->mVertexFormat = &mVertexFormat;
    }
-
-   initVertexFeatures();
-   initMaterialList();
 }
 
 void TSShape::initVertexBuffers()
@@ -736,7 +747,7 @@ void TSShape::initVertexBufferPointers()
          if (mesh->mVertSize > 0 && !mesh->mVertexData.isReady())
          {
             U32 boneOffset = 0;
-            U32 colorOffset = 0;
+            U32 texCoordOffset = 0;
             AssertFatal(mesh->mVertSize == mVertexFormat.getSizeInBytes(), "mismatch in format size");
 
             if (mBasicVertexFormat.boneOffset >= 0)
@@ -744,13 +755,13 @@ void TSShape::initVertexBufferPointers()
                boneOffset = mBasicVertexFormat.boneOffset;
             }
 
-            if (mBasicVertexFormat.colorOffset >= 0)
+            if (mBasicVertexFormat.texCoordOffset >= 0)
             {
-               colorOffset = mBasicVertexFormat.colorOffset;
+               texCoordOffset = mBasicVertexFormat.texCoordOffset;
             }
 
             // Initialize the vertex data
-            mesh->mVertexData.set(mShapeVertexData.base + mesh->mVertOffset, mesh->mVertSize, mesh->mNumVerts, colorOffset, boneOffset, false);
+            mesh->mVertexData.set(mShapeVertexData.base + mesh->mVertOffset, mesh->mVertSize, mesh->mNumVerts, texCoordOffset, boneOffset, false);
             mesh->mVertexData.setReady(true);
          }
       }
@@ -856,6 +867,7 @@ void TSShape::initVertexFeatures()
    }
 
    // Now we can create the VBO
+   mShapeVertexData.set(NULL, 0);
    U8 *vertexData = (U8*)dMalloc_aligned(destVertex, 16);
    U8 *vertexDataPtr = vertexData;
    mShapeVertexData.set(vertexData, destVertex);
@@ -872,7 +884,7 @@ void TSShape::initVertexFeatures()
          continue;
 
       U32 boneOffset = 0;
-      U32 colorOffset = 0;
+      U32 texCoordOffset = 0;
       AssertFatal(mesh->mVertSize == mVertexFormat.getSizeInBytes(), "mismatch in format size");
 
       if (mBasicVertexFormat.boneOffset >= 0)
@@ -880,9 +892,9 @@ void TSShape::initVertexFeatures()
          boneOffset = mBasicVertexFormat.boneOffset;
       }
 
-      if (mBasicVertexFormat.colorOffset >= 0)
+      if (mBasicVertexFormat.texCoordOffset >= 0)
       {
-         colorOffset = mBasicVertexFormat.colorOffset;
+         texCoordOffset = mBasicVertexFormat.texCoordOffset;
       }
 
       // Dump everything
@@ -891,7 +903,14 @@ void TSShape::initVertexFeatures()
       AssertFatal(mesh->mVertOffset == vertexDataPtr - vertexData, "vertex offset mismatch");
       mesh->mNumVerts = mesh->getNumVerts();
 
-      mesh->mVertexData.set(mShapeVertexData.base + mesh->mVertOffset, mesh->mVertSize, mesh->mNumVerts, colorOffset, boneOffset, false);
+      // Correct bad meshes
+      if (mesh->mNumVerts != 0 && mesh->vertsPerFrame > mesh->mNumVerts)
+      {
+         Con::warnf("Shape mesh has bad vertsPerFrame (%i, should be <= %i)", mesh->vertsPerFrame, mesh->mNumVerts);
+         mesh->vertsPerFrame = mesh->mNumVerts;
+      }
+
+      mesh->mVertexData.set(mShapeVertexData.base + mesh->mVertOffset, mesh->mVertSize, mesh->mNumVerts, texCoordOffset, boneOffset, false);
       mesh->convertToVertexData();
       mesh->mVertexData.setReady(true);
 
@@ -1494,7 +1513,7 @@ void TSShape::assembleShape()
    }
 
    // read in the meshes (sans skins)...straightforward read one at a time
-   ptr32 = tsalloc.allocShape32(numMeshes + numSkins*numDetails); // leave room for skins on old shapes
+   TSMesh **ptrmesh = (TSMesh**)tsalloc.allocShape32((numMeshes + numSkins*numDetails) * (sizeof(TSMesh*) / 4));
    S32 curObject = 0; // for tracking skipped meshes
    for (i=0; i<numMeshes; i++)
    {
@@ -1504,10 +1523,9 @@ void TSShape::assembleShape()
          // decal mesh deprecated
          skip = true;
       TSMesh * mesh = TSMesh::assembleMesh(meshType,skip);
-      if (ptr32)
+      if (ptrmesh)
       {
-         ptr32[i] = skip ?  0 : (intptr_t)mesh; // @todo 64bit
-         meshes.push_back(skip ?  0 : mesh);
+         ptrmesh[i] = skip ?  0 : mesh;
       }
 
       // fill in location of verts, tverts, and normals for detail levels
@@ -1536,6 +1554,7 @@ void TSShape::assembleShape()
          }
       }
    }
+   meshes.set(ptrmesh, numMeshes);
 
    tsalloc.checkGuard();
 

+ 9 - 3
Engine/source/ts/tsShape.h

@@ -413,6 +413,7 @@ class TSShape
    GFXPrimitiveBufferHandle mShapeVertexIndices;
 
    bool mSequencesConstructed;
+   bool mNeedReinit;
 
 
    // shape class has few methods --
@@ -427,7 +428,10 @@ class TSShape
    bool preloadMaterialList(const Torque::Path &path); ///< called to preload and validate the materials in the mat list
 
    void setupBillboardDetails( const String &cachePath );
-   
+
+   /// Inits object list (no geometry buffers)
+   void initObjects();
+
    /// Initializes the main vertex buffer
    void initVertexBuffers();
 
@@ -557,8 +561,6 @@ class TSShape
 
    const GFXVertexFormat* getVertexFormat() const { return &mVertexFormat; }
 
-   bool needsBufferUpdate();
-
    /// @}
 
    /// @name Alpha Transitions
@@ -685,6 +687,10 @@ class TSShape
 
    bool setSequenceBlend(const String& seqName, bool blend, const String& blendRefSeqName, S32 blendRefFrame);
    bool setSequenceGroundSpeed(const String& seqName, const Point3F& trans, const Point3F& rot);
+
+   void makeEditable();
+   bool needsReinit();
+   bool needsBufferUpdate();
    /// @}
 };
 

+ 26 - 1
Engine/source/ts/tsShapeConstruct.cpp

@@ -86,6 +86,11 @@ void TSShapeConstructor::_onTSShapeLoaded( Resource< TSShape >& resource )
    TSShapeConstructor* ctor = findShapeConstructor( resource.getPath().getFullPath() );
    if( ctor )
       ctor->_onLoad( resource );
+
+   if (ctor && ctor->mShape && ctor->mShape->needsReinit())
+   {
+      ctor->mShape->init();
+   }
 }
 
 void TSShapeConstructor::_onTSShapeUnloaded( const Torque::Path& path, TSShape* shape )
@@ -128,7 +133,7 @@ static void SplitSequencePathAndName( String& srcPath, String& srcName )
 IMPLEMENT_CONOBJECT(TSShapeConstructor);
 
 TSShapeConstructor::TSShapeConstructor()
- : mShapePath("")
+ : mShapePath(""), mLoadingShape(false)
 {
    mShape = NULL;
 }
@@ -374,9 +379,15 @@ bool TSShapeConstructor::onAdd()
 
    // If an instance of this shape has already been loaded, call onLoad now
    Resource<TSShape> shape = ResourceManager::get().find( mShapePath );
+
    if ( shape )
       _onLoad( shape );
 
+   if (mShape && mShape->needsReinit())
+   {
+      mShape->init();
+   }
+
    return true;
 }
 
@@ -394,6 +405,7 @@ void TSShapeConstructor::_onLoad(TSShape* shape)
 
    mShape = shape;
    mChangeSet.clear();
+   mLoadingShape = true;
 
    // Add sequences defined using field syntax
    for ( S32 i = 0; i < mSequences.size(); i++ )
@@ -411,6 +423,7 @@ void TSShapeConstructor::_onLoad(TSShape* shape)
 
    // Call script function
    onLoad_callback();
+   mLoadingShape = false;
 }
 
 //-----------------------------------------------------------------------------
@@ -3279,3 +3292,15 @@ bool TSShapeConstructor::ChangeSet::addCmd_removeImposter( const TSShapeConstruc
 
    return true;
 }
+
+void TSShapeConstructor::onActionPerformed()
+{
+   // Reinit shape if we modify stuff in the shape editor, otherwise delay
+   if (!mLoadingShape)
+   {
+      if (mShape && mShape->needsReinit())
+      {
+         mShape->init();
+      }
+   }
+}

+ 12 - 1
Engine/source/ts/tsShapeConstruct.h

@@ -246,6 +246,7 @@ public:
 
    TSShape*                mShape;        // Edited shape; NULL while not loaded; not a Resource<TSShape> as we don't want it to prevent from unloading.
    ColladaUtils::ImportOptions   mOptions;
+   bool mLoadingShape;
 
 public:
 
@@ -261,6 +262,7 @@ public:
    bool onAdd();
 
    void onScriptChanged(const Torque::Path& path);
+   void onActionPerformed();
 
    bool writeField(StringTableEntry fieldname, const char *value);
    void writeChangeSet();
@@ -383,8 +385,16 @@ typedef domUpAxisType TSShapeConstructorUpAxis;
 typedef ColladaUtils::ImportOptions::eLodType TSShapeConstructorLodType;
 
 DefineEnumType( TSShapeConstructorUpAxis );
-DefineEnumType( TSShapeConstructorLodType );
+DefineEnumType(TSShapeConstructorLodType);
 
+class TSShapeConstructorMethodActionCallback
+{
+   TSShapeConstructor* mObject;
+
+public:
+   TSShapeConstructorMethodActionCallback(TSShapeConstructor *object) : mObject(object) { ; }
+   ~TSShapeConstructorMethodActionCallback() { mObject->onActionPerformed(); }
+};
 
 /* This macro simplifies the definition of a TSShapeConstructor API method. It
    wraps the actual EngineMethod definition and automatically calls the real
@@ -403,6 +413,7 @@ DefineEnumType( TSShapeConstructorLodType );
          Con::errorf( "TSShapeConstructor::" #name " - shape not loaded" );                     \
          return defRet;                                                                         \
       }                                                                                         \
+      TSShapeConstructorMethodActionCallback actionCallback(object);                            \
       return object->name rawArgs ;                                                             \
    }                                                                                            \
    /* Define the real TSShapeConstructor method */                                              \

+ 90 - 19
Engine/source/ts/tsShapeEdit.cpp

@@ -435,6 +435,9 @@ bool TSShape::addNode(const String& name, const String& parentName, const Point3
       }
    }
 
+   // Need to make everything editable since node indexes etc will change
+   makeEditable();
+
    // Insert node at the end of the subshape
    S32 subShapeIndex = (parentIndex >= 0) ? getSubShapeForNode(parentIndex) : 0;
    S32 nodeIndex = subShapeNumNodes[subShapeIndex];
@@ -493,8 +496,7 @@ bool TSShape::addNode(const String& name, const String& parentName, const Point3
       }
    }
 
-   // Re-initialise the shape
-   init();
+   initObjects();
 
    return true;
 }
@@ -548,6 +550,9 @@ bool TSShape::removeNode(const String& name)
          ((nodeParentIndex >= 0) ? getName(nodes[nodeParentIndex].nameIndex).c_str() : "null"));
    }
 
+   // Need to make everything editable since node indexes etc will change
+   makeEditable();
+
    // Update animation sequences
    for (S32 iSeq = 0; iSeq < sequences.size(); iSeq++)
    {
@@ -626,8 +631,7 @@ bool TSShape::removeNode(const String& name)
    // Remove the sequence name if it is no longer in use
    removeName(name);
 
-   // Re-initialise the shape
-   init();
+   initObjects();
 
    return true;
 }
@@ -742,9 +746,26 @@ void TSShape::removeMeshFromObject(S32 objIndex, S32 meshIndex)
       {
          if (meshIndex < objects[i].numMeshes)
          {
-            meshes.erase(objects[i].startMeshIndex + meshIndex);
+            U32 idxToRemove = objects[i].startMeshIndex + meshIndex;
+            meshes.erase(idxToRemove);
             objects[i].numMeshes--;
 
+            // Clear invalid parent
+            for (U32 k = 0; k < meshes.size(); k++)
+            {
+               if (meshes[k] == NULL)
+                  continue;
+
+               if (meshes[k]->parentMesh == idxToRemove)
+               {
+                  meshes[k]->parentMesh = -1;
+               }
+               else if (meshes[k]->parentMesh > idxToRemove)
+               {
+                  meshes[k]->parentMesh--;
+               }
+            }
+
             for (S32 j = 0; j < objects.size(); j++)
             {
                if (objects[j].startMeshIndex > objects[i].startMeshIndex)
@@ -770,7 +791,25 @@ void TSShape::removeMeshFromObject(S32 objIndex, S32 meshIndex)
    S32 oldNumMeshes = obj.numMeshes;
    while (obj.numMeshes && !meshes[obj.startMeshIndex + obj.numMeshes - 1])
    {
-      meshes.erase(obj.startMeshIndex + obj.numMeshes - 1);
+      U32 idxToRemove = obj.startMeshIndex + obj.numMeshes - 1;
+      meshes.erase(idxToRemove);
+
+      // Clear invalid parent
+      for (U32 k = 0; k < meshes.size(); k++)
+      {
+         if (meshes[k] == NULL)
+            continue;
+
+         if (meshes[k]->parentMesh == idxToRemove)
+         {
+            meshes[k]->parentMesh = -1;
+         }
+         else if (meshes[k]->parentMesh > idxToRemove)
+         {
+            meshes[k]->parentMesh--;
+         }
+      }
+
       obj.numMeshes--;
    }
 
@@ -820,6 +859,9 @@ bool TSShape::removeObject(const String& name)
       return false;
    }
 
+   // Need to make everything editable since node indexes etc will change
+   makeEditable();
+
    // Destroy all meshes in the object
    TSShape::Object& obj = objects[objIndex];
    while ( obj.numMeshes )
@@ -858,8 +900,7 @@ bool TSShape::removeObject(const String& name)
    // Update smallest visible detail
    updateSmallestVisibleDL();
 
-   // Re-initialise the shape
-   init();
+   initObjects();
 
    return true;
 }
@@ -920,6 +961,9 @@ bool TSShape::addMesh(TSMesh* mesh, const String& meshName)
    // Ensure mesh is in editable state
    mesh->makeEditable();
 
+   // Need to make everything editable since node indexes etc will change
+   makeEditable();
+
    // Determine the object name and detail size from the mesh name
    S32 detailSize = 999;
    String objName(String::GetTrailingNumber(meshName, detailSize));
@@ -1008,8 +1052,7 @@ bool TSShape::addMesh(TSMesh* mesh, const String& meshName)
       }
    }
 
-   // Re-initialise the shape
-   init();
+   initObjects();
 
    return true;
 }
@@ -1099,6 +1142,9 @@ bool TSShape::setMeshSize(const String& meshName, S32 size)
       return false;
    }
 
+   // Need to make everything editable since node indexes etc will change
+   makeEditable();
+
    // Remove the mesh from the object, but don't destroy it
    TSShape::Object& obj = objects[objIndex];
    TSMesh* mesh = meshes[obj.startMeshIndex + meshIndex];
@@ -1110,8 +1156,7 @@ bool TSShape::setMeshSize(const String& meshName, S32 size)
    // Update smallest visible detail
    updateSmallestVisibleDL();
 
-   // Re-initialise the shape
-   init();
+   initObjects();
 
    return true;
 }
@@ -1126,6 +1171,9 @@ bool TSShape::removeMesh(const String& meshName)
       return false;
    }
 
+   // Need to make everything editable since node indexes etc will change
+   makeEditable();
+
    // Destroy and remove the mesh
    TSShape::Object& obj = objects[objIndex];
    destructInPlace(meshes[obj.startMeshIndex + meshIndex]);
@@ -1138,8 +1186,7 @@ bool TSShape::removeMesh(const String& meshName)
    // Update smallest visible detail
    updateSmallestVisibleDL();
 
-   // Re-initialise the shape
-   init();
+   initObjects();
 
    return true;
 }
@@ -1253,8 +1300,8 @@ S32 TSShape::setDetailSize(S32 oldSize, S32 newSize)
    // Update smallest visible detail
    updateSmallestVisibleDL();
 
-   // Re-initialise the shape
-   init();
+   // Nothing major, just reint object lists
+   initObjects();
 
    return newIndex;
 }
@@ -1269,6 +1316,9 @@ bool TSShape::removeDetail( S32 size )
       return false;
    }
 
+   // Need to make everything editable since node indexes etc will change
+   makeEditable();
+
    // Destroy and remove each mesh in the detail level
    for ( S32 objIndex = objects.size()-1; objIndex >= 0; objIndex-- )
    {
@@ -1301,8 +1351,7 @@ bool TSShape::removeDetail( S32 size )
    // Update smallest visible detail
    updateSmallestVisibleDL();
 
-   // Re-initialise the shape
-   init();
+   initObjects();
 
    return true;
 }
@@ -2068,7 +2117,7 @@ bool TSShape::setSequenceGroundSpeed(const String& seqName, const Point3F& trans
 
    // Fixup ground frame indices
    seq.numGroundFrames += frameAdjust;
-   for (S32 i = seqIndex+1; i < sequences.size(); i++)
+   for (S32 i = seqIndex + 1; i < sequences.size(); i++)
       sequences[i].firstGroundFrame += frameAdjust;
 
    // Generate the ground-frames
@@ -2093,3 +2142,25 @@ bool TSShape::setSequenceGroundSpeed(const String& seqName, const Point3F& trans
 
    return true;
 }
+
+void TSShape::makeEditable()
+{
+   mNeedReinit = true;
+   if (mShapeVertexData.base == NULL)
+      return;
+
+   for (U32 i = 0; i < meshes.size(); i++)
+   {
+      if (meshes[i])
+      {
+         meshes[i]->makeEditable();
+      }
+   }
+
+   mShapeVertexData.set(NULL, 0);
+}
+
+bool TSShape::needsReinit()
+{
+   return mVertexSize == 0 || mShapeVertexData.base == NULL || mNeedReinit;
+}

+ 6 - 1
Engine/source/ts/tsShapeInstance.cpp

@@ -169,6 +169,7 @@ void TSShapeInstance::buildInstanceData(TSShape * _shape, bool loadMaterials)
    // material list...
    mMaterialList = NULL;
    mOwnMaterialList = false;
+   mUseOwnBuffer = false;
 
    //
    mData = 0;
@@ -532,7 +533,7 @@ void TSShapeInstance::render( const TSRenderState &rdata, S32 dl, F32 intraDL )
    S32 end = rdata.isNoRenderTranslucent() ? mShape->subShapeFirstTranslucentObject[ss] : mShape->subShapeFirstObject[ss] + mShape->subShapeNumObjects[ss];
    TSVertexBufferHandle *realBuffer;
 
-   if (TSShape::smUseHardwareSkinning)
+   if (TSShape::smUseHardwareSkinning && !mUseOwnBuffer)
    {
       // For hardware skinning, just using the buffer associated with the shape will work fine
       realBuffer = &mShape->mShapeVertexBuffer;
@@ -893,3 +894,7 @@ bool TSShapeInstance::hasAccumulation()
    return result;
 }
 
+void TSShapeInstance::setUseOwnBuffer()
+{
+   mUseOwnBuffer = true;
+}

+ 2 - 0
Engine/source/ts/tsShapeInstance.h

@@ -279,6 +279,7 @@ protected:
    TSVertexBufferHandle mSoftwareVertexBuffer;
 
    bool            mOwnMaterialList; ///< Does this own the material list pointer?
+   bool            mUseOwnBuffer; ///< Force using our own copy of the vertex buffer
 
    bool           mAlphaAlways;
    F32            mAlphaAlwaysValue;
@@ -341,6 +342,7 @@ protected:
    /// an optional feature set.
    void initMaterialList(  const FeatureSet *features = NULL );
 
+   void setUseOwnBuffer();
    bool ownMaterialList() const { return mOwnMaterialList; }
 
    /// Get the number of material targets in this shape instance