bool ModelDefinition::Deserialize(Deserializer &deserializer) { // Clear everything. this->ClearMeshes(); this->ClearBones(); this->ClearAnimations(true); // <-- 'true' = clear animation segments, too. this->ClearConvexHulls(); // We keep looping until we hit the null or unknown chunk. Serialization::ChunkHeader header; while (deserializer.Peek(&header, sizeof(header)) == sizeof(header)) { bool finished = false; switch (header.id) { case Serialization::ChunkID_Model_Bones: { deserializer.Seek(sizeof(header)); if (header.version == 1) { uint32_t boneCount; deserializer.Read(boneCount); for (uint32_t iBone = 0; iBone < boneCount; ++iBone) { // Name. String name; deserializer.ReadString(name); // Local transform. glm::vec3 position; glm::quat rotation; glm::vec3 scale; deserializer.Read(position); deserializer.Read(rotation); deserializer.Read(scale); // 4x4 offset matrix. glm::mat4 offsetMatrix; deserializer.Read(offsetMatrix); auto bone = new Bone; bone->SetName(name.c_str()); bone->SetPosition(position); bone->SetRotation(rotation); bone->SetScale(scale); bone->SetOffsetMatrix(offsetMatrix); this->AddBone(bone); // We need to create a channel for this bone. We then need to map that channel to a bone. auto &channel = m_animation.CreateChannel(); this->animationChannelBones.Add(bone, &channel); } // Parents. auto boneParentIndices = static_cast<uint32_t*>(malloc(boneCount * sizeof(uint32_t))); deserializer.Read(boneParentIndices, boneCount * sizeof(uint32_t)); for (uint32_t iBone = 0; iBone < boneCount; ++iBone) { uint32_t parentIndex = boneParentIndices[iBone]; if (parentIndex != static_cast<uint32_t>(-1)) { m_bones[parentIndex]->AttachChild(*m_bones[iBone]); } } free(boneParentIndices); } else { // Unsupported Version. deserializer.Seek(header.sizeInBytes); } break; } case Serialization::ChunkID_Model_Meshes: { deserializer.Seek(sizeof(header)); if (header.version == 1) { uint32_t meshCount; deserializer.Read(meshCount); for (uint32_t iMesh = 0; iMesh < meshCount; ++iMesh) { ModelDefinition::Mesh newMesh(m_context); // Name. deserializer.ReadString(newMesh.name); // Material String materialName; deserializer.ReadString(materialName); newMesh.material = m_context.GetMaterialLibrary().Create(materialName.c_str()); // Geometry VertexFormat vertexFormat; vertexFormat.Deserialize(deserializer); newMesh.geometry = Renderer::CreateVertexArray(VertexArrayUsage_Static, vertexFormat); // Vertices. uint32_t vertexCount; deserializer.Read(vertexCount); if (vertexCount > 0) { newMesh.geometry->SetVertexData(nullptr, static_cast<size_t>(vertexCount)); auto vertexData = newMesh.geometry->MapVertexData(); { deserializer.Read(vertexData, vertexCount * vertexFormat.GetSizeInBytes()); } newMesh.geometry->UnmapVertexData(); } // Indices. uint32_t indexCount; deserializer.Read(indexCount); if (indexCount > 0) { newMesh.geometry->SetIndexData(nullptr, static_cast<size_t>(indexCount)); auto indexData = newMesh.geometry->MapIndexData(); { deserializer.Read(indexData, indexCount * sizeof(uint32_t)); } newMesh.geometry->UnmapIndexData(); } // Skinning Vertex Attributes uint32_t skinningVertexAttributeCount; deserializer.Read(skinningVertexAttributeCount); if (skinningVertexAttributeCount > 0) { newMesh.skinningVertexAttributes = new SkinningVertexAttribute[skinningVertexAttributeCount]; auto counts = static_cast<uint16_t*>(malloc(skinningVertexAttributeCount * sizeof(uint16_t))); deserializer.Read(counts, skinningVertexAttributeCount * sizeof(uint16_t)); uint32_t totalBoneWeights; deserializer.Read(totalBoneWeights); auto boneWeights = static_cast<BoneWeightPair*>(malloc(totalBoneWeights * sizeof(BoneWeightPair))); deserializer.Read(boneWeights, totalBoneWeights * sizeof(BoneWeightPair)); auto currentBoneWeight = boneWeights; for (uint32_t iVertex = 0; iVertex < skinningVertexAttributeCount; ++iVertex) { auto count = counts[iVertex]; // Here we allocate the buffer for the bones. We trick the vector here by modifying attributes directly. newMesh.skinningVertexAttributes[iVertex].bones.Reserve(count); newMesh.skinningVertexAttributes[iVertex].bones.count = count; for (uint16_t iBone = 0; iBone < count; ++iBone) { newMesh.skinningVertexAttributes[iVertex].bones[iBone] = *currentBoneWeight++; } } free(counts); free(boneWeights); } // Uniforms. newMesh.defaultUniforms.Deserialize(deserializer); // Finally, add the mesh. this->AddMesh(newMesh); } } else { // Unsupported Version. deserializer.Seek(header.sizeInBytes); } break; } case Serialization::ChunkID_Model_Animation: { deserializer.Seek(sizeof(header)); if (header.version == 1) { uint32_t keyFrameCount; deserializer.Read(keyFrameCount); for (size_t iKeyFrame = 0; iKeyFrame < keyFrameCount; ++iKeyFrame) { float time; deserializer.Read(time); size_t keyFrameIndex = m_animation.AppendKeyFrame(static_cast<double>(time)); // With the key frame added, we now need to iterate over each channel in the key frame. uint32_t channelCount; deserializer.Read(channelCount); for (uint32_t iChannel = 0; iChannel < channelCount; ++iChannel) { uint32_t boneIndex; deserializer.Read(boneIndex); auto bone = m_bones[boneIndex]; assert(bone != nullptr); { auto iChannelBone = this->animationChannelBones.Find(bone); assert(iChannelBone != nullptr); { auto channel = iChannelBone->value; glm::vec3 position; glm::quat rotation; glm::vec3 scale; deserializer.Read(position); deserializer.Read(rotation); deserializer.Read(scale); auto key = new TransformAnimationKey(position, rotation, scale); channel->SetKey(keyFrameIndex, key); // We need to cache the key. this->animationKeyCache.PushBack(key); } } } } deserializer.Read(this->animationAABBPadding); } else { // Unsupported Version. deserializer.Seek(header.sizeInBytes); } break; } case Serialization::ChunkID_Model_AnimationSegments: { deserializer.Seek(sizeof(header)); if (header.version == 1) { uint32_t animationSegmentCount; deserializer.Read(animationSegmentCount); for (uint32_t iSegment = 0; iSegment < animationSegmentCount; ++iSegment) { String name; uint32_t startKeyFrame; uint32_t endKeyFrame; deserializer.ReadString(name); deserializer.Read(startKeyFrame); deserializer.Read(endKeyFrame); m_animation.AddNamedSegment(name.c_str(), static_cast<size_t>(startKeyFrame), static_cast<size_t>(endKeyFrame)); } } else { // Unsupported Version. deserializer.Seek(header.sizeInBytes); } break; } case Serialization::ChunkID_Model_AnimationSequences: { deserializer.Seek(sizeof(header)); if (header.version == 1) { } else { // Unsupported Version. deserializer.Seek(header.sizeInBytes); } break; } case Serialization::ChunkID_Model_ConvexHulls: { deserializer.Seek(sizeof(header)); if (header.version == 1) { uint32_t convexHullCount; deserializer.Read(convexHullCount); uint32_t* vertexCounts = static_cast<uint32_t*>(malloc(convexHullCount * sizeof(uint32_t))); uint32_t* indexCounts = static_cast<uint32_t*>(malloc(convexHullCount * sizeof(uint32_t))); deserializer.Read(vertexCounts, convexHullCount * sizeof(uint32_t)); deserializer.Read(indexCounts, convexHullCount * sizeof(uint32_t)); uint32_t totalVertexCount; deserializer.Read(totalVertexCount); uint32_t totalIndexCount; deserializer.Read(totalIndexCount); auto vertices = static_cast<float* >(malloc(totalVertexCount * sizeof(float))); auto indices = static_cast<uint32_t*>(malloc(totalIndexCount * sizeof(uint32_t))); deserializer.Read(vertices, totalVertexCount * sizeof(float)); deserializer.Read(indices, totalIndexCount * sizeof(uint32_t)); auto currentVertices = vertices; auto currentIndices = indices; for (uint32_t iConvexHull = 0; iConvexHull < convexHullCount; ++iConvexHull) { size_t vertexCount = static_cast<size_t>(vertexCounts[iConvexHull]); size_t indexCount = static_cast<size_t>(indexCounts[iConvexHull]); m_convexHulls.PushBack(new ConvexHull(currentVertices, vertexCount, currentIndices, indexCount)); // Now we need to move our pointers forward. currentVertices += vertexCount * 3; currentIndices += indexCount; } // Build Settings. deserializer.Read(this->convexHullBuildSettings); free(vertexCounts); free(indexCounts); free(vertices); free(indices); } else { // Unsupported Version. deserializer.Seek(header.sizeInBytes); } break; } case Serialization::ChunkID_Null: { deserializer.Seek(sizeof(header)); finished = true; break; } default: { // Unknown chunk = Error. finished = true; return false; } } if (finished) { break; } } // If we get here, we were successful. return true; }