瀏覽代碼

Added Clone() function to Model, which deep-copies all data including vertex/index buffers, so that the clone can be individually animated. Closes #402.

Lasse Öörni 11 年之前
父節點
當前提交
b80fbc8817

+ 1 - 1
Source/Engine/Graphics/Material.h

@@ -139,7 +139,7 @@ public:
     void RemoveShaderParameter(const String& name);
     /// Reset all shader pointers.
     void ReleaseShaders();
-    /// Clone material.
+    /// Clone the material.
     SharedPtr<Material> Clone(const String& cloneName = String::EMPTY) const;
     /// Ensure that material techniques are listed in correct order.
     void SortTechniques();

+ 123 - 3
Source/Engine/Graphics/Model.cpp

@@ -200,9 +200,9 @@ bool Model::Load(Deserializer& source)
         newMorph.name_ = source.ReadString();
         newMorph.nameHash_ = newMorph.name_;
         newMorph.weight_ = 0.0f;
-        unsigned nubuffers_ = source.ReadUInt();
+        unsigned numBuffers = source.ReadUInt();
         
-        for (unsigned j = 0; j < nubuffers_; ++j)
+        for (unsigned j = 0; j < numBuffers; ++j)
         {
             VertexBufferMorph newBuffer;
             unsigned bufferIndex = source.ReadUInt();
@@ -219,7 +219,8 @@ bool Model::Load(Deserializer& source)
                 vertexSize += sizeof(Vector3);
             if (newBuffer.elementMask_ & MASK_TANGENT)
                 vertexSize += sizeof(Vector3);
-            newBuffer.morphData_ = new unsigned char[newBuffer.vertexCount_ * vertexSize];
+            newBuffer.dataSize_ = newBuffer.vertexCount_ * vertexSize;
+            newBuffer.morphData_ = new unsigned char[newBuffer.dataSize_];
             
             source.Read(&newBuffer.morphData_[0], newBuffer.vertexCount_ * vertexSize);
             
@@ -463,6 +464,125 @@ void Model::SetMorphs(const Vector<ModelMorph>& morphs)
     morphs_ = morphs;
 }
 
+SharedPtr<Model> Model::Clone(const String& cloneName) const
+{
+    SharedPtr<Model> ret(new Model(context_));
+
+    ret->SetName(cloneName);
+    ret->boundingBox_ = boundingBox_;
+    ret->skeleton_ = skeleton_;
+    ret->geometryBoneMappings_ = geometryBoneMappings_;
+    ret->geometryCenters_ = geometryCenters_;
+    ret->morphs_ = morphs_;
+    ret->morphRangeStarts_ = morphRangeStarts_;
+    ret->morphRangeCounts_ = morphRangeCounts_;
+    
+    // Deep copy vertex/index buffers
+    HashMap<VertexBuffer*, VertexBuffer*> vbMapping;
+    for (Vector<SharedPtr<VertexBuffer> >::ConstIterator i = vertexBuffers_.Begin(); i != vertexBuffers_.End(); ++i)
+    {
+        VertexBuffer* origBuffer = *i;
+        SharedPtr<VertexBuffer> cloneBuffer;
+        
+        if (origBuffer)
+        {
+            cloneBuffer = new VertexBuffer(context_);
+            cloneBuffer->SetSize(origBuffer->GetVertexCount(), origBuffer->GetElementMask(), origBuffer->IsDynamic());
+            cloneBuffer->SetShadowed(origBuffer->IsShadowed());
+            if (origBuffer->IsShadowed())
+                cloneBuffer->SetData(origBuffer->GetShadowData());
+            else
+            {
+                void* origData = origBuffer->Lock(0, origBuffer->GetVertexCount());
+                if (origData)
+                    cloneBuffer->SetData(origData);
+                else
+                    LOGERROR("Failed to lock original vertex buffer for copying");
+            }
+            vbMapping[origBuffer] = cloneBuffer;
+        }
+
+        ret->vertexBuffers_.Push(cloneBuffer);
+    }
+    
+    HashMap<IndexBuffer*, IndexBuffer*> ibMapping;
+    for (Vector<SharedPtr<IndexBuffer> >::ConstIterator i = indexBuffers_.Begin(); i != indexBuffers_.End(); ++i)
+    {
+        IndexBuffer* origBuffer = *i;
+        SharedPtr<IndexBuffer> cloneBuffer;
+        
+        if (origBuffer)
+        {
+            cloneBuffer = new IndexBuffer(context_);
+            cloneBuffer->SetSize(origBuffer->GetIndexCount(), origBuffer->GetIndexSize() == sizeof(unsigned), origBuffer->IsDynamic());
+            cloneBuffer->SetShadowed(origBuffer->IsShadowed());
+            if (origBuffer->IsShadowed())
+                cloneBuffer->SetData(origBuffer->GetShadowData());
+            else
+            {
+                void* origData = origBuffer->Lock(0, origBuffer->GetIndexCount());
+                if (origData)
+                    cloneBuffer->SetData(origData);
+                else
+                    LOGERROR("Failed to lock original index buffer for copying");
+            }
+            ibMapping[origBuffer] = cloneBuffer;
+        }
+
+        ret->indexBuffers_.Push(cloneBuffer);
+    }
+    
+    // Deep copy all the geometry LOD levels and refer to the copied vertex/index buffers
+    ret->geometries_.Resize(geometries_.Size());
+    for (unsigned i = 0; i < geometries_.Size(); ++i)
+    {
+        ret->geometries_[i].Resize(geometries_[i].Size());
+        for (unsigned j = 0; j < geometries_[i].Size(); ++j)
+        {
+            SharedPtr<Geometry> cloneGeometry;
+            Geometry* origGeometry = geometries_[i][j];
+            
+            if (origGeometry)
+            {
+                cloneGeometry = new Geometry(context_);
+                cloneGeometry->SetIndexBuffer(ibMapping[origGeometry->GetIndexBuffer()]);
+                unsigned numVbs = origGeometry->GetNumVertexBuffers();
+                for (unsigned k = 0; k < numVbs; ++k)
+                {
+                    cloneGeometry->SetVertexBuffer(k, vbMapping[origGeometry->GetVertexBuffer(k)],
+                        origGeometry->GetVertexElementMask(k));
+                }
+                cloneGeometry->SetDrawRange(origGeometry->GetPrimitiveType(), origGeometry->GetIndexStart(),
+                    origGeometry->GetIndexCount(), origGeometry->GetVertexStart(), origGeometry->GetVertexCount(), false);
+                cloneGeometry->SetLodDistance(origGeometry->GetLodDistance());
+            }
+            
+            ret->geometries_[i][j] = cloneGeometry;
+        }
+    }
+    
+    
+    // Deep copy the morph data (if any) to allow modifying it
+    for (Vector<ModelMorph>::Iterator i = ret->morphs_.Begin(); i != ret->morphs_.End(); ++i)
+    {
+        ModelMorph& morph = *i;
+        for (HashMap<unsigned, VertexBufferMorph>::Iterator j = morph.buffers_.Begin(); j != morph.buffers_.End(); ++j)
+        {
+            VertexBufferMorph& vbMorph = j->second_;
+            if (vbMorph.dataSize_)
+            {
+                SharedArrayPtr<unsigned char> cloneData(new unsigned char[vbMorph.dataSize_]);
+                memcpy(cloneData.Get(), vbMorph.morphData_.Get(), vbMorph.dataSize_);
+                vbMorph.morphData_ = cloneData;
+            }
+        }
+    }
+    
+    ret->SetMemoryUse(GetMemoryUse());
+    
+    return ret;
+}
+
 unsigned Model::GetNumGeometryLodLevels(unsigned index) const
 {
     return index < geometries_.Size() ? geometries_[index].Size() : 0;

+ 4 - 0
Source/Engine/Graphics/Model.h

@@ -43,6 +43,8 @@ struct VertexBufferMorph
     unsigned elementMask_;
     /// Number of vertices.
     unsigned vertexCount_;
+    /// Morphed vertices data size as bytes.
+    unsigned dataSize_;
     /// Morphed vertices. Stored packed as <index, data> pairs.
     SharedArrayPtr<unsigned char> morphData_;
 };
@@ -98,6 +100,8 @@ public:
     void SetGeometryBoneMappings(const Vector<PODVector<unsigned> >& mappings);
     /// Set vertex morphs.
     void SetMorphs(const Vector<ModelMorph>& morphs);
+    /// Clone the model. The geometry data is deep-copied and can be modified in the clone without affecting the original.
+    SharedPtr<Model> Clone(const String& cloneName = String::EMPTY) const;
     
     /// Return bounding box.
     const BoundingBox& GetBoundingBox() const { return boundingBox_; }

+ 18 - 1
Source/Engine/LuaScript/pkgs/Graphics/Model.pkg

@@ -2,6 +2,9 @@ $#include "Model.h"
 
 class Model : public Resource
 {
+    // SharedPtr<Model> Clone(const String cloneName = String::EMPTY) const;
+    tolua_outside Model* ModelClone @ Clone(const String cloneName = String::EMPTY) const;
+
     const BoundingBox& GetBoundingBox() const;
     Skeleton& GetSkeleton();
     unsigned GetNumGeometries() const;
@@ -13,9 +16,23 @@ class Model : public Resource
     const ModelMorph* GetMorph(unsigned index) const;
     unsigned GetMorphRangeStart(unsigned bufferIndex) const;
     unsigned GetMorphRangeCount(unsigned bufferIndex) const;
-    
+
     tolua_readonly tolua_property__get_set BoundingBox& boundingBox;
     tolua_readonly tolua_property__get_set Skeleton skeleton;
     tolua_readonly tolua_property__get_set unsigned numGeometries;
     tolua_readonly tolua_property__get_set unsigned numMorphs;
 };
+
+${
+static Model* ModelClone(const Model* model, const String& cloneName = String::EMPTY)
+{
+    if (!model)
+        return 0;
+        
+    SharedPtr<Model> clonedModelPtr = model->Clone(cloneName);
+    Model* clonedModel = clonedModelPtr.Get();
+    clonedModelPtr.Detach();
+    
+    return clonedModel;
+}
+$}

+ 10 - 0
Source/Engine/Script/GraphicsAPI.cpp

@@ -663,9 +663,19 @@ static void RegisterMaterial(asIScriptEngine* engine)
     engine->RegisterGlobalFunction("String GetTextureUnitName(TextureUnit)", asFUNCTION(Material::GetTextureUnitName), asCALL_CDECL);
 }
 
+static Model* ModelClone(const String& cloneName, Model* ptr)
+{
+    SharedPtr<Model> clone = ptr->Clone(cloneName);
+    // The shared pointer will go out of scope, so have to increment the reference count
+    // (here an auto handle can not be used)
+    clone->AddRef();
+    return clone.Get();
+}
+
 static void RegisterModel(asIScriptEngine* engine)
 {
     RegisterResource<Model>(engine, "Model");
+    engine->RegisterObjectMethod("Model", "Model@ Clone(const String&in cloneName = String()) const", asFUNCTION(ModelClone), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("Model", "const BoundingBox& get_boundingBox() const", asMETHOD(Model, GetBoundingBox), asCALL_THISCALL);
     engine->RegisterObjectMethod("Model", "Skeleton@+ get_skeleton()", asMETHOD(Model, GetSkeleton), asCALL_THISCALL);
     engine->RegisterObjectMethod("Model", "uint get_numGeometries() const", asMETHOD(Model, GetNumGeometries), asCALL_THISCALL);