Browse Source

Added optional save() function to Resource.
Implemented save for Model & Animation.

Lasse Öörni 15 years ago
parent
commit
4e37bbd3cc

+ 32 - 0
Engine/Renderer/Animation.cpp

@@ -24,6 +24,7 @@
 #include "Precompiled.h"
 #include "Animation.h"
 #include "Deserializer.h"
+#include "Serializer.h"
 
 #include "DebugNew.h"
 
@@ -66,6 +67,7 @@ void Animation::load(Deserializer& source, ResourceCache* cache)
     mTracks.resize(tracks);
     memoryUse += tracks * sizeof(AnimationTrack);
     
+    // Read tracks
     for (unsigned i = 0; i < tracks; ++i)
     {
         AnimationTrack& newTrack = mTracks[i];
@@ -77,6 +79,7 @@ void Animation::load(Deserializer& source, ResourceCache* cache)
         newTrack.mKeyFrames.resize(keyFrames);
         memoryUse += keyFrames * sizeof(AnimationKeyFrame);
         
+        // Read keyframes of the track
         for (unsigned j = 0; j < keyFrames; ++j)
         {
             AnimationKeyFrame& newKeyFrame = newTrack.mKeyFrames[j];
@@ -93,6 +96,35 @@ void Animation::load(Deserializer& source, ResourceCache* cache)
     setMemoryUse(memoryUse);
 }
 
+void Animation::save(Serializer& dest)
+{
+    dest.writeString(mAnimationName);
+    dest.writeFloat(mLength);
+    
+    // Write tracks
+    dest.writeUInt(mTracks.size());
+    for (unsigned i = 0; i < mTracks.size(); ++i)
+    {
+        const AnimationTrack& track = mTracks[i];
+        dest.writeString(track.mName);
+        dest.writeUByte(track.mChannelMask);
+        dest.writeUInt(track.mKeyFrames.size());
+        
+        // Write keyframes of the track
+        for (unsigned j = 0; j < track.mKeyFrames.size(); ++j)
+        {
+            const AnimationKeyFrame& keyFrame = track.mKeyFrames[j];
+            dest.writeFloat(keyFrame.mTime);
+            if (track.mChannelMask & CHANNEL_POSITION)
+                dest.writeVector3(keyFrame.mPosition);
+            if (track.mChannelMask & CHANNEL_ROTATION)
+                dest.writeQuaternion(keyFrame.mRotation);
+            if (track.mChannelMask & CHANNEL_SCALE)
+                dest.writeVector3(keyFrame.mScale);
+        }
+    }
+}
+
 unsigned Animation::getNumTracks() const
 {
     return mTracks.size();

+ 2 - 0
Engine/Renderer/Animation.h

@@ -78,6 +78,8 @@ public:
     
     //! Load resource. Throw exception on error
     virtual void load(Deserializer& source, ResourceCache* cache = 0);
+    //! Save resource. Throw exception on error
+    virtual void save(Serializer& dest);
     
     //! Return animation name
     const std::string& getAnimationName() const { return mAnimationName; }

+ 126 - 0
Engine/Renderer/Model.cpp

@@ -29,12 +29,35 @@
 #include "Model.h"
 #include "Profiler.h"
 #include "Renderer.h"
+#include "Serializer.h"
 #include "VertexBuffer.h"
 
 #include <cstring>
 
 #include "DebugNew.h"
 
+unsigned storeOrLookupVertexBuffer(VertexBuffer* buffer, std::vector<VertexBuffer*>& dest)
+{
+    for (unsigned i = 0; i < dest.size(); ++i)
+    {
+        if (dest[i] == buffer)
+            return i;
+    }
+    dest.push_back(buffer);
+    return dest.size() - 1;
+}
+
+unsigned storeOrLookupIndexBuffer(IndexBuffer* buffer, std::vector<IndexBuffer*>& dest)
+{
+    for (unsigned i = 0; i < dest.size(); ++i)
+    {
+        if (dest[i] == buffer)
+            return i;
+    }
+    dest.push_back(buffer);
+    return dest.size() - 1;
+}
+
 Model::Model(Renderer* renderer, const std::string& name) :
     Resource(name),
     mRenderer(renderer),
@@ -197,6 +220,109 @@ void Model::load(Deserializer& source, ResourceCache* cache)
     mOcclusionLodLevel = source.readUInt();
 }
 
+void Model::save(Serializer& dest)
+{
+    // Build lists of vertex and index buffers used by the geometries
+    std::vector<VertexBuffer*> vertexBuffers;
+    std::vector<IndexBuffer*> indexBuffers;
+    
+    for (unsigned i = 0; i < mGeometries.size(); ++i)
+    {
+        for (unsigned j = 0; j < mGeometries[i].size(); ++j)
+        {
+            storeOrLookupVertexBuffer(mGeometries[i][j]->getVertexBuffer(0), vertexBuffers);
+            storeOrLookupIndexBuffer(mGeometries[i][j]->getIndexBuffer(), indexBuffers);
+        }
+    }
+    
+    // Write vertex buffers
+    dest.writeUInt(vertexBuffers.size());
+    for (unsigned i = 0; i < vertexBuffers.size(); ++i)
+    {
+        VertexBuffer* buffer = vertexBuffers[i];
+        dest.writeUInt(buffer->getVertexCount());
+        dest.writeUInt(buffer->getElementMask());
+        dest.writeUInt(buffer->getMorphRangeStart());
+        dest.writeUInt(buffer->getMorphRangeCount());
+        void* data = buffer->lock(0, buffer->getVertexCount(), LOCK_READONLY);
+        dest.write(data, buffer->getVertexCount() * buffer->getVertexSize());
+        buffer->unlock();
+    }
+    // Write index buffers
+    dest.writeUInt(indexBuffers.size());
+    for (unsigned i = 0; i < indexBuffers.size(); ++i)
+    {
+        IndexBuffer* buffer = indexBuffers[i];
+        dest.writeUInt(buffer->getIndexCount());
+        dest.writeUInt(buffer->getIndexSize());
+        void* data = buffer->lock(0, buffer->getIndexCount(), LOCK_READONLY);
+        dest.write(data, buffer->getIndexCount() * buffer->getIndexSize());
+        buffer->unlock();
+    }
+    // Write geometries
+    dest.writeUInt(mGeometries.size());
+    for (unsigned i = 0; i < mGeometries.size(); ++i)
+    {
+        // Write bone mappings
+        dest.writeUInt(mGeometryBoneMappings[i].size());
+        for (unsigned j = 0; j < mGeometryBoneMappings[i].size(); ++j)
+            dest.writeUInt(mGeometryBoneMappings[i][j]);;
+        
+        // Write the LOD levels
+        dest.writeUInt(mGeometries[i].size());
+        for (unsigned j = 0; j < mGeometries[i].size(); ++j)
+        {
+            Geometry* geometry = mGeometries[i][j];
+            dest.writeFloat(geometry->getLodDistance());
+            dest.writeUInt(geometry->getPrimitiveType());
+            dest.writeUInt(storeOrLookupVertexBuffer(geometry->getVertexBuffer(0), vertexBuffers));
+            dest.writeUInt(storeOrLookupIndexBuffer(geometry->getIndexBuffer(), indexBuffers));
+            dest.writeUInt(geometry->getIndexStart());
+            dest.writeUInt(geometry->getIndexCount());
+        }
+    }
+    
+    // Write morphs
+    dest.writeUInt(mMorphs.size());
+    for (unsigned i = 0; i < mMorphs.size(); ++i)
+    {
+        dest.writeString(mMorphs[i].mName);
+        dest.writeUInt(mMorphs[i].mBuffers.size());
+        
+        // Write morph vertex buffers
+        for (std::map<unsigned int, VertexBufferMorph>::const_iterator j = mMorphs[i].mBuffers.begin();
+            j != mMorphs[i].mBuffers.end(); ++j)
+        {
+            dest.writeUInt(j->first);
+            dest.writeUInt(j->second.mElementMask);
+            dest.writeUInt(j->second.mVertexCount);
+            
+            // Base size: size of each vertex index
+            unsigned vertexSize = sizeof(unsigned);
+            // Add size of individual elements
+            if (j->second.mElementMask & MASK_POSITION)
+                vertexSize += sizeof(Vector3);
+            if (j->second.mElementMask & MASK_NORMAL)
+                vertexSize += sizeof(Vector3);
+            if (j->second.mElementMask & MASK_TANGENT)
+                vertexSize += sizeof(Vector3);
+            
+            dest.write(j->second.mMorphData.getPtr(), vertexSize * j->second.mVertexCount);
+        }
+    }
+    
+    // Write skeleton
+    mSkeleton.save(dest);
+    
+    // Write bounding box
+    dest.writeBoundingBox(mBoundingBox);
+    
+    // Write collision/raycast/occlusion LOD levels
+    dest.writeUInt(mCollisionLodLevel);
+    dest.writeUInt(mRaycastLodLevel);
+    dest.writeUInt(mOcclusionLodLevel);
+}
+
 void Model::setBoundingBox(const BoundingBox& box)
 {
     mBoundingBox = box;

+ 2 - 0
Engine/Renderer/Model.h

@@ -76,6 +76,8 @@ public:
     
     //! Load resource. Throw exception on error
     virtual void load(Deserializer& source, ResourceCache* cache = 0);
+    //! Save resource. Throw exception on error
+    virtual void save(Serializer& dest);
     
     //! Set bounding box
     void setBoundingBox(const BoundingBox& box);

+ 43 - 1
Engine/Renderer/Skeleton.cpp

@@ -24,6 +24,7 @@
 #include "Precompiled.h"
 #include "Deserializer.h"
 #include "Profiler.h"
+#include "Serializer.h"
 #include "Skeleton.h"
 
 #include "DebugNew.h"
@@ -91,6 +92,37 @@ void Skeleton::load(Deserializer& source)
     }
 }
 
+void Skeleton::save(Serializer& dest)
+{
+    dest.writeUInt(mBones.size());
+    for (unsigned i = 0; i < mBones.size(); ++i)
+    {
+        Bone* bone = mBones[i];
+        
+        // Bone name
+        dest.writeString(bone->getName());
+        
+        // Parent index, same as own if root bone
+        unsigned parentIndex = getBoneIndex(dynamic_cast<Bone*>(bone->getParent()));
+        if (parentIndex == M_MAX_UNSIGNED)
+            parentIndex = i;
+        dest.writeUInt(parentIndex);
+        
+        // Bind transform
+        dest.writeVector3(bone->getBindPosition());
+        dest.writeQuaternion(bone->getBindRotation());
+        dest.writeVector3(bone->getBindScale());
+        
+        // Collision info
+        unsigned char collisionMask = bone->getCollisionMask();
+        dest.writeUByte(collisionMask);
+        if (collisionMask & BONECOLLISION_SPHERE)
+            dest.writeFloat(bone->getRadius());
+        if (collisionMask & BONECOLLISION_BOX)
+            dest.writeBoundingBox(bone->getBoundingBox());
+    }
+}
+
 void Skeleton::define(const std::vector<SharedPtr<Bone > >& srcBones)
 {
     clearBones();
@@ -179,6 +211,16 @@ Bone* Skeleton::getBone(StringHash nameHash) const
     return 0;
 }
 
+unsigned Skeleton::getBoneIndex(Bone* bone) const
+{
+    for (unsigned i = 0; i < mBones.size(); ++i)
+    {
+        if (mBones[i] == bone)
+            return i;
+    }
+    return M_MAX_UNSIGNED;
+}
+
 bool Skeleton::hasAttachedNodes()
 {
     if ((mRootBone) && (mRootBone->areAttachedNodesDirty()))
@@ -207,7 +249,7 @@ bool Skeleton::hasAttachedNodes()
         
         mRootBone->clearAttachedNodesDirty();
     }
-
+    
     return mHasAttachedNodes;
 }
 

+ 6 - 1
Engine/Renderer/Skeleton.h

@@ -28,6 +28,7 @@
 
 class Deserializer;
 class ResourceCache;
+class Serializer;
 
 //! A hierarchical collection of bones
 class Skeleton
@@ -38,8 +39,10 @@ public:
     //! Destruct. Detach the root bone from its parent
     ~Skeleton();
     
-    //! Read from stream
+    //! Read from a stream
     void load(Deserializer& source);
+    //! Write to a stream
+    void save(Serializer& dest);
     //! Define from source bones
     void define(const std::vector<SharedPtr<Bone> >& srcBones);
     //! Reset all animated bones, or all bones if forced
@@ -57,6 +60,8 @@ public:
     Bone* getBone(const std::string& boneName) const;
     //! Return bone by name hash
     Bone* getBone(StringHash boneNameHash) const;
+    //! Return bone index from pointer, or 0xffffffff if not found
+    unsigned getBoneIndex(Bone* bone) const;
     //! Return whether bones have attached scene nodes
     bool hasAttachedNodes();
     

+ 6 - 0
Engine/Resource/Resource.cpp

@@ -22,6 +22,7 @@
 //
 
 #include "Precompiled.h"
+#include "Exception.h"
 #include "Resource.h"
 
 Resource::Resource(const std::string& name) :
@@ -31,6 +32,11 @@ Resource::Resource(const std::string& name) :
 {
 }
 
+void Resource::save(Serializer& dest)
+{
+    EXCEPTION("Save not supported for " + getTypeName());
+}
+
 void Resource::setMemoryUse(unsigned size)
 {
     mMemoryUse = size;

+ 3 - 0
Engine/Resource/Resource.h

@@ -29,6 +29,7 @@
 
 class Deserializer;
 class ResourceCache;
+class Serializer;
 
 //! Base class for resources
 class Resource : public HashedType
@@ -39,6 +40,8 @@ public:
     
     //! Load resource. Throw exception on error
     virtual void load(Deserializer& source, ResourceCache* cache = 0) = 0;
+    //! Save resource. Throw exception if not supported or on error
+    virtual void save(Serializer& dest);
     
     //! Set memory use in bytes, possibly approximate
     void setMemoryUse(unsigned size);