From 12019173af2aa17617c1dabffa2e51c769f80417 Mon Sep 17 00:00:00 2001 From: James Urquhart Date: Tue, 30 Aug 2016 19:07:02 +0100 Subject: [PATCH 1/4] Fix load with DTS shapes introduced with HW skinning changes --- Engine/source/ts/tsMesh.cpp | 4 +-- Engine/source/ts/tsShape.cpp | 39 ++++++++++++++------- Engine/source/ts/tsShapeEdit.cpp | 51 ++++++++++++++++++++++++++-- Engine/source/ts/tsShapeInstance.cpp | 7 +++- Engine/source/ts/tsShapeInstance.h | 2 ++ 5 files changed, 85 insertions(+), 18 deletions(-) diff --git a/Engine/source/ts/tsMesh.cpp b/Engine/source/ts/tsMesh.cpp index 058e4d826..5353a7471 100644 --- a/Engine/source/ts/tsMesh.cpp +++ b/Engine/source/ts/tsMesh.cpp @@ -3429,8 +3429,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); diff --git a/Engine/source/ts/tsShape.cpp b/Engine/source/ts/tsShape.cpp index 969cfa388..ff55766c2 100644 --- a/Engine/source/ts/tsShape.cpp +++ b/Engine/source/ts/tsShape.cpp @@ -576,7 +576,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]; } @@ -736,7 +741,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 +749,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 +861,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 +878,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 +886,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 +897,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 +1507,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; iparentMesh == 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 +787,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--; } @@ -858,6 +893,12 @@ bool TSShape::removeObject(const String& name) // Update smallest visible detail updateSmallestVisibleDL(); + // Ensure shape is dirty + if (meshes[0]) + { + meshes[0]->makeEditable(); + } + // Re-initialise the shape init(); @@ -1298,6 +1339,12 @@ bool TSShape::removeDetail( S32 size ) billboardDetails.erase( dl ); } + // Ensure shape is dirty + if (meshes[0]) + { + meshes[0]->makeEditable(); + } + // Update smallest visible detail updateSmallestVisibleDL(); diff --git a/Engine/source/ts/tsShapeInstance.cpp b/Engine/source/ts/tsShapeInstance.cpp index 6d24e49f5..d1a8da382 100644 --- a/Engine/source/ts/tsShapeInstance.cpp +++ b/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; +} diff --git a/Engine/source/ts/tsShapeInstance.h b/Engine/source/ts/tsShapeInstance.h index e133beb00..48b253358 100644 --- a/Engine/source/ts/tsShapeInstance.h +++ b/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 From 18031f09b18a7df87fdaaf3f33a346cade707980 Mon Sep 17 00:00:00 2001 From: James Urquhart Date: Thu, 1 Sep 2016 00:30:29 +0100 Subject: [PATCH 2/4] Fix edge-case with version 24 shapes --- Engine/source/ts/tsMesh.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Engine/source/ts/tsMesh.cpp b/Engine/source/ts/tsMesh.cpp index 5353a7471..69b8d275b 100644 --- a/Engine/source/ts/tsMesh.cpp +++ b/Engine/source/ts/tsMesh.cpp @@ -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 { From 0e717ea707cb4557f86c09df7562b16d58c3e37e Mon Sep 17 00:00:00 2001 From: James Urquhart Date: Thu, 1 Sep 2016 00:36:17 +0100 Subject: [PATCH 3/4] Fix edge case where an editable shape without a vbo is saved --- Engine/source/ts/tsMesh.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Engine/source/ts/tsMesh.cpp b/Engine/source/ts/tsMesh.cpp index 69b8d275b..822895bfd 100644 --- a/Engine/source/ts/tsMesh.cpp +++ b/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) From a46779fad6562e4deade6102945c7a740131efca Mon Sep 17 00:00:00 2001 From: James Urquhart Date: Sat, 3 Sep 2016 10:41:25 +0100 Subject: [PATCH 4/4] Defer re-init'ing the shape when TSShapeConstructor is loading a shape --- Engine/source/ts/tsShape.cpp | 80 ++++++++++++++------------ Engine/source/ts/tsShape.h | 12 +++- Engine/source/ts/tsShapeConstruct.cpp | 27 ++++++++- Engine/source/ts/tsShapeConstruct.h | 13 ++++- Engine/source/ts/tsShapeEdit.cpp | 82 +++++++++++++++++---------- 5 files changed, 143 insertions(+), 71 deletions(-) diff --git a/Engine/source/ts/tsShape.cpp b/Engine/source/ts/tsShape.cpp index ff55766c2..6f0bc94a0 100644 --- a/Engine/source/ts/tsShape.cpp +++ b/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 ); @@ -413,43 +414,51 @@ void TSShape::getObjectDetails(S32 objIndex, Vector& objDetails) void TSShape::init() { - S32 numSubShapes = subShapeFirstNode.size(); - AssertFatal(numSubShapes==subShapeFirstObject.size(),"TSShape::init"); + initObjects(); + initVertexFeatures(); + initMaterialList(); + mNeedReinit = false; +} - S32 i,j; +void TSShape::initObjects() +{ + S32 numSubShapes = subShapeFirstNode.size(); + AssertFatal(numSubShapes == subShapeFirstObject.size(), "TSShape::initObjects"); + + S32 i, j; // set up parent/child relationships on nodes and objects - for (i=0; i=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=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=0; i--) + for (i = mSmallestVisibleDL - 1; i >= 0; i--) { if (igetNumPolys() : 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; } } @@ -580,7 +589,7 @@ void TSShape::init() { 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]; @@ -592,9 +601,6 @@ void TSShape::init() mesh->mVertexFormat = &mVertexFormat; } - - initVertexFeatures(); - initMaterialList(); } void TSShape::initVertexBuffers() diff --git a/Engine/source/ts/tsShape.h b/Engine/source/ts/tsShape.h index 09a5a84f1..e72dd8617 100644 --- a/Engine/source/ts/tsShape.h +++ b/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(); /// @} }; diff --git a/Engine/source/ts/tsShapeConstruct.cpp b/Engine/source/ts/tsShapeConstruct.cpp index da93d9691..dd2a67644 100644 --- a/Engine/source/ts/tsShapeConstruct.cpp +++ b/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 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(); + } + } +} diff --git a/Engine/source/ts/tsShapeConstruct.h b/Engine/source/ts/tsShapeConstruct.h index 640c80c91..24dffe0da 100644 --- a/Engine/source/ts/tsShapeConstruct.h +++ b/Engine/source/ts/tsShapeConstruct.h @@ -246,6 +246,7 @@ public: TSShape* mShape; // Edited shape; NULL while not loaded; not a Resource 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 */ \ diff --git a/Engine/source/ts/tsShapeEdit.cpp b/Engine/source/ts/tsShapeEdit.cpp index 32a9792a2..94379de4d 100644 --- a/Engine/source/ts/tsShapeEdit.cpp +++ b/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; } @@ -855,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 ) @@ -893,14 +900,7 @@ bool TSShape::removeObject(const String& name) // Update smallest visible detail updateSmallestVisibleDL(); - // Ensure shape is dirty - if (meshes[0]) - { - meshes[0]->makeEditable(); - } - - // Re-initialise the shape - init(); + initObjects(); return true; } @@ -961,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)); @@ -1049,8 +1052,7 @@ bool TSShape::addMesh(TSMesh* mesh, const String& meshName) } } - // Re-initialise the shape - init(); + initObjects(); return true; } @@ -1140,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]; @@ -1151,8 +1156,7 @@ bool TSShape::setMeshSize(const String& meshName, S32 size) // Update smallest visible detail updateSmallestVisibleDL(); - // Re-initialise the shape - init(); + initObjects(); return true; } @@ -1167,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]); @@ -1179,8 +1186,7 @@ bool TSShape::removeMesh(const String& meshName) // Update smallest visible detail updateSmallestVisibleDL(); - // Re-initialise the shape - init(); + initObjects(); return true; } @@ -1294,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; } @@ -1310,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-- ) { @@ -1339,17 +1348,10 @@ bool TSShape::removeDetail( S32 size ) billboardDetails.erase( dl ); } - // Ensure shape is dirty - if (meshes[0]) - { - meshes[0]->makeEditable(); - } - // Update smallest visible detail updateSmallestVisibleDL(); - // Re-initialise the shape - init(); + initObjects(); return true; } @@ -2115,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 @@ -2140,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; +}