diff options
author | Paul Lemire <paul.lemire@kdab.com> | 2014-06-27 09:31:11 +0200 |
---|---|---|
committer | Paul Lemire <paul.lemire@kdab.com> | 2014-07-17 10:14:00 +0200 |
commit | 09687cfaade1e3baa693f02861722ea860f3c716 (patch) | |
tree | 4d1084ee95ec45e880739ddf709ae32fe73d3caa /src/3rdparty/assimp/code/OgreBinarySerializer.cpp | |
parent | 22829fba69a36e447a14dcd59149164d482b3fe3 (diff) |
Upgrade to Assimp 3.1.1
https://github.com/assimp/assimp/releases/tag/v3.1.1
This commit imports assimp 3.1.1, including CHANGES, CREDITS, LICENSE,
README, Readme.md, and code, contrib, include directories. contrib/zlib,
contrib/cppunit-1.12.1/, contrib/cppunit_note.txt were excluded in this
change.
assimp.pri was also updated.
revision.h was generated from revision.h.in.
Change-Id: I7c0597eae9eee7f06a1170968b5fef6fd46b6f0d
Reviewed-by: Liang Qi <liang.qi@digia.com>
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
Diffstat (limited to 'src/3rdparty/assimp/code/OgreBinarySerializer.cpp')
-rw-r--r-- | src/3rdparty/assimp/code/OgreBinarySerializer.cpp | 1110 |
1 files changed, 1110 insertions, 0 deletions
diff --git a/src/3rdparty/assimp/code/OgreBinarySerializer.cpp b/src/3rdparty/assimp/code/OgreBinarySerializer.cpp new file mode 100644 index 000000000..6674f4cee --- /dev/null +++ b/src/3rdparty/assimp/code/OgreBinarySerializer.cpp @@ -0,0 +1,1110 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2012, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +#include "OgreBinarySerializer.h" +#include "OgreXmlSerializer.h" +#include "OgreParsingUtils.h" + +#include "TinyFormatter.h" + +#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER + +// Define as 1 to get verbose logging. +#define OGRE_BINARY_SERIALIZER_DEBUG 0 + +namespace Assimp +{ +namespace Ogre +{ + +const std::string MESH_VERSION_1_8 = "[MeshSerializer_v1.8]"; +const std::string SKELETON_VERSION_1_8 = "[Serializer_v1.80]"; +const std::string SKELETON_VERSION_1_1 = "[Serializer_v1.10]"; + +const unsigned short HEADER_CHUNK_ID = 0x1000; + +const long MSTREAM_OVERHEAD_SIZE = sizeof(uint16_t) + sizeof(uint32_t); +const long MSTREAM_BONE_SIZE_WITHOUT_SCALE = MSTREAM_OVERHEAD_SIZE + sizeof(unsigned short) + (sizeof(float) * 7); +const long MSTREAM_KEYFRAME_SIZE_WITHOUT_SCALE = MSTREAM_OVERHEAD_SIZE + (sizeof(float) * 8); + +template<> +inline bool OgreBinarySerializer::Read<bool>() +{ + return (m_reader->GetU1() > 0); +} + +template<> +inline char OgreBinarySerializer::Read<char>() +{ + return static_cast<char>(m_reader->GetU1()); +} + +template<> +inline uint8_t OgreBinarySerializer::Read<uint8_t>() +{ + return m_reader->GetU1(); +} + +template<> +inline uint16_t OgreBinarySerializer::Read<uint16_t>() +{ + return m_reader->GetU2(); +} + +template<> +inline uint32_t OgreBinarySerializer::Read<uint32_t>() +{ + return m_reader->GetU4(); +} + +template<> +inline float OgreBinarySerializer::Read<float>() +{ + return m_reader->GetF4(); +} + +void OgreBinarySerializer::ReadBytes(char *dest, size_t numBytes) +{ + ReadBytes(static_cast<void*>(dest), numBytes); +} + +void OgreBinarySerializer::ReadBytes(uint8_t *dest, size_t numBytes) +{ + ReadBytes(static_cast<void*>(dest), numBytes); +} + +void OgreBinarySerializer::ReadBytes(void *dest, size_t numBytes) +{ + m_reader->CopyAndAdvance(dest, numBytes); +} + +uint8_t *OgreBinarySerializer::ReadBytes(size_t numBytes) +{ + uint8_t *bytes = new uint8_t[numBytes]; + ReadBytes(bytes, numBytes); + return bytes; +} + +void OgreBinarySerializer::ReadVector(aiVector3D &vec) +{ + m_reader->CopyAndAdvance(&vec.x, sizeof(float)*3); +} + +void OgreBinarySerializer::ReadQuaternion(aiQuaternion &quat) +{ + float temp[4]; + m_reader->CopyAndAdvance(temp, sizeof(float)*4); + quat.x = temp[0]; + quat.y = temp[1]; + quat.z = temp[2]; + quat.w = temp[3]; +} + +bool OgreBinarySerializer::AtEnd() const +{ + return (m_reader->GetRemainingSize() == 0); +} + +std::string OgreBinarySerializer::ReadString(size_t len) +{ + std::string str; + str.resize(len); + ReadBytes(&str[0], len); + return str; +} + +std::string OgreBinarySerializer::ReadLine() +{ + std::string str; + while(!AtEnd()) + { + char c = Read<char>(); + if (c == '\n') + break; + str += c; + } + return str; +} + +uint16_t OgreBinarySerializer::ReadHeader(bool readLen) +{ + uint16_t id = Read<uint16_t>(); + if (readLen) + m_currentLen = Read<uint32_t>(); + +#if (OGRE_BINARY_SERIALIZER_DEBUG == 1) + if (id != HEADER_CHUNK_ID) + { + DefaultLogger::get()->debug(Formatter::format() << (assetMode == AM_Mesh + ? MeshHeaderToString(static_cast<MeshChunkId>(id)) : SkeletonHeaderToString(static_cast<SkeletonChunkId>(id)))); + } +#endif + + return id; +} + +void OgreBinarySerializer::RollbackHeader() +{ + m_reader->IncPtr(-MSTREAM_OVERHEAD_SIZE); +} + +void OgreBinarySerializer::SkipBytes(size_t numBytes) +{ +#if (OGRE_BINARY_SERIALIZER_DEBUG == 1) + DefaultLogger::get()->debug(Formatter::format() << "Skipping " << numBytes << " bytes"); +#endif + + m_reader->IncPtr(numBytes); +} + +// Mesh + +Mesh *OgreBinarySerializer::ImportMesh(MemoryStreamReader *stream) +{ + OgreBinarySerializer serializer(stream, OgreBinarySerializer::AM_Mesh); + + uint16_t id = serializer.ReadHeader(false); + if (id != HEADER_CHUNK_ID) { + throw DeadlyExportError("Invalid Ogre Mesh file header."); + } + + /// @todo Check what we can actually support. + std::string version = serializer.ReadLine(); + if (version != MESH_VERSION_1_8) + { + throw DeadlyExportError(Formatter::format() << "Mesh version " << version << " not supported by this importer. Run OgreMeshUpgrader tool on the file and try again." + << " Supported versions: " << MESH_VERSION_1_8); + } + + Mesh *mesh = new Mesh(); + while (!serializer.AtEnd()) + { + id = serializer.ReadHeader(); + switch(id) + { + case M_MESH: + { + serializer.ReadMesh(mesh); + break; + } + } + } + return mesh; +} + +void OgreBinarySerializer::ReadMesh(Mesh *mesh) +{ + mesh->hasSkeletalAnimations = Read<bool>(); + + DefaultLogger::get()->debug("Reading Mesh"); + DefaultLogger::get()->debug(Formatter::format() << " - Skeletal animations: " << (mesh->hasSkeletalAnimations ? "true" : "false")); + + if (!AtEnd()) + { + uint16_t id = ReadHeader(); + while (!AtEnd() && + (id == M_GEOMETRY || + id == M_SUBMESH || + id == M_MESH_SKELETON_LINK || + id == M_MESH_BONE_ASSIGNMENT || + id == M_MESH_LOD || + id == M_MESH_BOUNDS || + id == M_SUBMESH_NAME_TABLE || + id == M_EDGE_LISTS || + id == M_POSES || + id == M_ANIMATIONS || + id == M_TABLE_EXTREMES)) + { + switch(id) + { + case M_GEOMETRY: + { + mesh->sharedVertexData = new VertexData(); + ReadGeometry(mesh->sharedVertexData); + break; + } + case M_SUBMESH: + { + ReadSubMesh(mesh); + break; + } + case M_MESH_SKELETON_LINK: + { + ReadMeshSkeletonLink(mesh); + break; + } + case M_MESH_BONE_ASSIGNMENT: + { + ReadBoneAssignment(mesh->sharedVertexData); + break; + } + case M_MESH_LOD: + { + ReadMeshLodInfo(mesh); + break; + } + case M_MESH_BOUNDS: + { + ReadMeshBounds(mesh); + break; + } + case M_SUBMESH_NAME_TABLE: + { + ReadSubMeshNames(mesh); + break; + } + case M_EDGE_LISTS: + { + ReadEdgeList(mesh); + break; + } + case M_POSES: + { + ReadPoses(mesh); + break; + } + case M_ANIMATIONS: + { + ReadAnimations(mesh); + break; + } + case M_TABLE_EXTREMES: + { + ReadMeshExtremes(mesh); + break; + } + } + + if (!AtEnd()) + id = ReadHeader(); + } + if (!AtEnd()) + RollbackHeader(); + } + + NormalizeBoneWeights(mesh->sharedVertexData); +} + +void OgreBinarySerializer::ReadMeshLodInfo(Mesh *mesh) +{ + // Assimp does not acknowledge LOD levels as far as I can see it. This info is just skipped. + // @todo Put this stuff to scene/mesh custom properties. If manual mesh the app can use the information. + ReadLine(); // strategy name + uint16_t numLods = Read<uint16_t>(); + bool manual = Read<bool>(); + + /// @note Main mesh is considered as LOD 0, start from index 1. + for (size_t i=1; i<numLods; ++i) + { + uint16_t id = ReadHeader(); + if (id != M_MESH_LOD_USAGE) { + throw DeadlyImportError("M_MESH_LOD does not contain a M_MESH_LOD_USAGE for each LOD level"); + } + + m_reader->IncPtr(sizeof(float)); // user value + + if (manual) + { + id = ReadHeader(); + if (id != M_MESH_LOD_MANUAL) { + throw DeadlyImportError("Manual M_MESH_LOD_USAGE does not contain M_MESH_LOD_MANUAL"); + } + + ReadLine(); // manual mesh name (ref to another mesh) + } + else + { + for(size_t si=0, silen=mesh->NumSubMeshes(); si<silen; ++si) + { + id = ReadHeader(); + if (id != M_MESH_LOD_GENERATED) { + throw DeadlyImportError("Generated M_MESH_LOD_USAGE does not contain M_MESH_LOD_GENERATED"); + } + + uint32_t indexCount = Read<uint32_t>(); + bool is32bit = Read<bool>(); + + if (indexCount > 0) + { + uint32_t len = indexCount * (is32bit ? sizeof(uint32_t) : sizeof(uint16_t)); + m_reader->IncPtr(len); + } + } + } + } +} + +void OgreBinarySerializer::ReadMeshSkeletonLink(Mesh *mesh) +{ + mesh->skeletonRef = ReadLine(); +} + +void OgreBinarySerializer::ReadMeshBounds(Mesh *mesh) +{ + // Skip bounds, not compatible with Assimp. + // 2x float vec3 + 1x float sphere radius + SkipBytes(sizeof(float) * 7); +} + +void OgreBinarySerializer::ReadMeshExtremes(Mesh *mesh) +{ + // Skip extremes, not compatible with Assimp. + size_t numBytes = m_currentLen - MSTREAM_OVERHEAD_SIZE; + SkipBytes(numBytes); +} + +void OgreBinarySerializer::ReadBoneAssignment(VertexData *dest) +{ + if (!dest) { + throw DeadlyImportError("Cannot read bone assignments, vertex data is null."); + } + + VertexBoneAssignment ba; + ba.vertexIndex = Read<uint32_t>(); + ba.boneIndex = Read<uint16_t>(); + ba.weight = Read<float>(); + + dest->boneAssignments.push_back(ba); +} + +void OgreBinarySerializer::ReadSubMesh(Mesh *mesh) +{ + uint16_t id = 0; + + SubMesh *submesh = new SubMesh(); + submesh->materialRef = ReadLine(); + submesh->usesSharedVertexData = Read<bool>(); + + submesh->indexData->count = Read<uint32_t>(); + submesh->indexData->faceCount = static_cast<uint32_t>(submesh->indexData->count / 3); + submesh->indexData->is32bit = Read<bool>(); + + DefaultLogger::get()->debug(Formatter::format() << "Reading SubMesh " << mesh->subMeshes.size()); + DefaultLogger::get()->debug(Formatter::format() << " - Material: '" << submesh->materialRef << "'"); + DefaultLogger::get()->debug(Formatter::format() << " - Uses shared geometry: " << (submesh->usesSharedVertexData ? "true" : "false")); + + // Index buffer + if (submesh->indexData->count > 0) + { + uint32_t numBytes = submesh->indexData->count * (submesh->indexData->is32bit ? sizeof(uint32_t) : sizeof(uint16_t)); + uint8_t *indexBuffer = ReadBytes(numBytes); + submesh->indexData->buffer = MemoryStreamPtr(new Assimp::MemoryIOStream(indexBuffer, numBytes, true)); + + DefaultLogger::get()->debug(Formatter::format() << " - " << submesh->indexData->faceCount + << " faces from " << submesh->indexData->count << (submesh->indexData->is32bit ? " 32bit" : " 16bit") + << " indexes of " << numBytes << " bytes"); + } + + // Vertex buffer if not referencing the shared geometry + if (!submesh->usesSharedVertexData) + { + id = ReadHeader(); + if (id != M_GEOMETRY) { + throw DeadlyImportError("M_SUBMESH does not contain M_GEOMETRY, but shader geometry is set to false"); + } + + submesh->vertexData = new VertexData(); + ReadGeometry(submesh->vertexData); + } + + // Bone assignment, submesh operation and texture aliases + if (!AtEnd()) + { + id = ReadHeader(); + while (!AtEnd() && + (id == M_SUBMESH_OPERATION || + id == M_SUBMESH_BONE_ASSIGNMENT || + id == M_SUBMESH_TEXTURE_ALIAS)) + { + switch(id) + { + case M_SUBMESH_OPERATION: + { + ReadSubMeshOperation(submesh); + break; + } + case M_SUBMESH_BONE_ASSIGNMENT: + { + ReadBoneAssignment(submesh->vertexData); + break; + } + case M_SUBMESH_TEXTURE_ALIAS: + { + ReadSubMeshTextureAlias(submesh); + break; + } + } + + if (!AtEnd()) + id = ReadHeader(); + } + if (!AtEnd()) + RollbackHeader(); + } + + NormalizeBoneWeights(submesh->vertexData); + + submesh->index = mesh->subMeshes.size(); + mesh->subMeshes.push_back(submesh); +} + +void OgreBinarySerializer::NormalizeBoneWeights(VertexData *vertexData) const +{ + if (!vertexData || vertexData->boneAssignments.empty()) + return; + + std::set<uint32_t> influencedVertices; + for (VertexBoneAssignmentList::const_iterator baIter=vertexData->boneAssignments.begin(), baEnd=vertexData->boneAssignments.end(); baIter != baEnd; ++baIter) { + influencedVertices.insert(baIter->vertexIndex); + } + + /** Normalize bone weights. + Some exporters wont care if the sum of all bone weights + for a single vertex equals 1 or not, so validate here. */ + const float epsilon = 0.05f; + for(std::set<uint32_t>::const_iterator iter=influencedVertices.begin(), end=influencedVertices.end(); iter != end; ++iter) + { + const uint32_t vertexIndex = (*iter); + + float sum = 0.0f; + for (VertexBoneAssignmentList::const_iterator baIter=vertexData->boneAssignments.begin(), baEnd=vertexData->boneAssignments.end(); baIter != baEnd; ++baIter) + { + if (baIter->vertexIndex == vertexIndex) + sum += baIter->weight; + } + if ((sum < (1.0f - epsilon)) || (sum > (1.0f + epsilon))) + { + for (VertexBoneAssignmentList::iterator baIter=vertexData->boneAssignments.begin(), baEnd=vertexData->boneAssignments.end(); baIter != baEnd; ++baIter) + { + if (baIter->vertexIndex == vertexIndex) + baIter->weight /= sum; + } + } + } +} + +void OgreBinarySerializer::ReadSubMeshOperation(SubMesh *submesh) +{ + submesh->operationType = static_cast<SubMesh::OperationType>(Read<uint16_t>()); +} + +void OgreBinarySerializer::ReadSubMeshTextureAlias(SubMesh *submesh) +{ + submesh->textureAliasName = ReadLine(); + submesh->textureAliasRef = ReadLine(); +} + +void OgreBinarySerializer::ReadSubMeshNames(Mesh *mesh) +{ + uint16_t id = 0; + uint16_t submeshIndex = 0; + + if (!AtEnd()) + { + id = ReadHeader(); + while (!AtEnd() && id == M_SUBMESH_NAME_TABLE_ELEMENT) + { + uint16_t submeshIndex = Read<uint16_t>(); + SubMesh *submesh = mesh->GetSubMesh(submeshIndex); + if (!submesh) { + throw DeadlyImportError(Formatter::format() << "Ogre Mesh does not include submesh " << submeshIndex << " referenced in M_SUBMESH_NAME_TABLE_ELEMENT. Invalid mesh file."); + } + + submesh->name = ReadLine(); + DefaultLogger::get()->debug(Formatter::format() << " - SubMesh " << submesh->index << " name '" << submesh->name << "'"); + + if (!AtEnd()) + id = ReadHeader(); + } + if (!AtEnd()) + RollbackHeader(); + } +} + +void OgreBinarySerializer::ReadGeometry(VertexData *dest) +{ + dest->count = Read<uint32_t>(); + + DefaultLogger::get()->debug(Formatter::format() << " - Reading geometry of " << dest->count << " vertices"); + + if (!AtEnd()) + { + uint16_t id = ReadHeader(); + while (!AtEnd() && + (id == M_GEOMETRY_VERTEX_DECLARATION || + id == M_GEOMETRY_VERTEX_BUFFER)) + { + switch(id) + { + case M_GEOMETRY_VERTEX_DECLARATION: + { + ReadGeometryVertexDeclaration(dest); + break; + } + case M_GEOMETRY_VERTEX_BUFFER: + { + ReadGeometryVertexBuffer(dest); + break; + } + } + + if (!AtEnd()) + id = ReadHeader(); + } + if (!AtEnd()) + RollbackHeader(); + } +} + +void OgreBinarySerializer::ReadGeometryVertexDeclaration(VertexData *dest) +{ + if (!AtEnd()) + { + uint16_t id = ReadHeader(); + while (!AtEnd() && id == M_GEOMETRY_VERTEX_ELEMENT) + { + ReadGeometryVertexElement(dest); + + if (!AtEnd()) + id = ReadHeader(); + } + if (!AtEnd()) + RollbackHeader(); + } +} + +void OgreBinarySerializer::ReadGeometryVertexElement(VertexData *dest) +{ + VertexElement element; + element.source = Read<uint16_t>(); + element.type = static_cast<VertexElement::Type>(Read<uint16_t>()); + element.semantic = static_cast<VertexElement::Semantic>(Read<uint16_t>()); + element.offset = Read<uint16_t>(); + element.index = Read<uint16_t>(); + + DefaultLogger::get()->debug(Formatter::format() << " - Vertex element " << element.SemanticToString() << " of type " + << element.TypeToString() << " index=" << element.index << " source=" << element.source); + + dest->vertexElements.push_back(element); +} + +void OgreBinarySerializer::ReadGeometryVertexBuffer(VertexData *dest) +{ + uint16_t bindIndex = Read<uint16_t>(); + uint16_t vertexSize = Read<uint16_t>(); + + uint16_t id = ReadHeader(); + if (id != M_GEOMETRY_VERTEX_BUFFER_DATA) + throw DeadlyImportError("M_GEOMETRY_VERTEX_BUFFER_DATA not found in M_GEOMETRY_VERTEX_BUFFER"); + + if (dest->VertexSize(bindIndex) != vertexSize) + throw DeadlyImportError("Vertex buffer size does not agree with vertex declaration in M_GEOMETRY_VERTEX_BUFFER"); + + size_t numBytes = dest->count * vertexSize; + uint8_t *vertexBuffer = ReadBytes(numBytes); + dest->vertexBindings[bindIndex] = MemoryStreamPtr(new Assimp::MemoryIOStream(vertexBuffer, numBytes, true)); + + DefaultLogger::get()->debug(Formatter::format() << " - Read vertex buffer for source " << bindIndex << " of " << numBytes << " bytes"); +} + +void OgreBinarySerializer::ReadEdgeList(Mesh *mesh) +{ + // Assimp does not acknowledge LOD levels as far as I can see it. This info is just skipped. + + if (!AtEnd()) + { + uint16_t id = ReadHeader(); + while (!AtEnd() && id == M_EDGE_LIST_LOD) + { + m_reader->IncPtr(sizeof(uint16_t)); // lod index + bool manual = Read<bool>(); + + if (!manual) + { + m_reader->IncPtr(sizeof(uint8_t)); + uint32_t numTriangles = Read<uint32_t>(); + uint32_t numEdgeGroups = Read<uint32_t>(); + + size_t skipBytes = (sizeof(uint32_t) * 8 + sizeof(float) * 4) * numTriangles; + m_reader->IncPtr(skipBytes); + + for (size_t i=0; i<numEdgeGroups; ++i) + { + uint16_t id = ReadHeader(); + if (id != M_EDGE_GROUP) + throw DeadlyImportError("M_EDGE_GROUP not found in M_EDGE_LIST_LOD"); + + m_reader->IncPtr(sizeof(uint32_t) * 3); + uint32_t numEdges = Read<uint32_t>(); + for (size_t j=0; j<numEdges; ++j) + { + m_reader->IncPtr(sizeof(uint32_t) * 6 + sizeof(uint8_t)); + } + } + } + + if (!AtEnd()) + id = ReadHeader(); + } + if (!AtEnd()) + RollbackHeader(); + } +} + +void OgreBinarySerializer::ReadPoses(Mesh *mesh) +{ + if (!AtEnd()) + { + uint16_t id = ReadHeader(); + while (!AtEnd() && id == M_POSE) + { + Pose *pose = new Pose(); + pose->name = ReadLine(); + pose->target = Read<uint16_t>(); + pose->hasNormals = Read<bool>(); + + ReadPoseVertices(pose); + + mesh->poses.push_back(pose); + + if (!AtEnd()) + id = ReadHeader(); + } + if (!AtEnd()) + RollbackHeader(); + } +} + +void OgreBinarySerializer::ReadPoseVertices(Pose *pose) +{ + if (!AtEnd()) + { + uint16_t id = ReadHeader(); + while (!AtEnd() && id == M_POSE_VERTEX) + { + Pose::Vertex v; + v.index = Read<uint32_t>(); + ReadVector(v.offset); + if (pose->hasNormals) + ReadVector(v.normal); + + pose->vertices[v.index] = v; + + if (!AtEnd()) + id = ReadHeader(); + } + if (!AtEnd()) + RollbackHeader(); + } +} + +void OgreBinarySerializer::ReadAnimations(Mesh *mesh) +{ + if (!AtEnd()) + { + uint16_t id = ReadHeader(); + while (!AtEnd() && id == M_ANIMATION) + { + Animation *anim = new Animation(mesh); + anim->name = ReadLine(); + anim->length = Read<float>(); + + ReadAnimation(anim); + + mesh->animations.push_back(anim); + + if (!AtEnd()) + id = ReadHeader(); + } + if (!AtEnd()) + RollbackHeader(); + } +} + +void OgreBinarySerializer::ReadAnimation(Animation *anim) +{ + if (!AtEnd()) + { + uint16_t id = ReadHeader(); + if (id == M_ANIMATION_BASEINFO) + { + anim->baseName = ReadLine(); + anim->baseTime = Read<float>(); + + // Advance to first track + id = ReadHeader(); + } + + while (!AtEnd() && id == M_ANIMATION_TRACK) + { + VertexAnimationTrack track; + track.type = static_cast<VertexAnimationTrack::Type>(Read<uint16_t>()); + track.target = Read<uint16_t>(); + + ReadAnimationKeyFrames(anim, &track); + + anim->tracks.push_back(track); + + if (!AtEnd()) + id = ReadHeader(); + } + if (!AtEnd()) + RollbackHeader(); + } +} + +void OgreBinarySerializer::ReadAnimationKeyFrames(Animation *anim, VertexAnimationTrack *track) +{ + if (!AtEnd()) + { + uint16_t id = ReadHeader(); + while (!AtEnd() && + (id == M_ANIMATION_MORPH_KEYFRAME || + id == M_ANIMATION_POSE_KEYFRAME)) + { + if (id == M_ANIMATION_MORPH_KEYFRAME) + { + MorphKeyFrame kf; + kf.timePos = Read<float>(); + bool hasNormals = Read<bool>(); + + size_t vertexCount = anim->AssociatedVertexData(track)->count; + size_t vertexSize = sizeof(float) * (hasNormals ? 6 : 3); + size_t numBytes = vertexCount * vertexSize; + + uint8_t *morphBuffer = ReadBytes(numBytes); + kf.buffer = MemoryStreamPtr(new Assimp::MemoryIOStream(morphBuffer, numBytes, true)); + + track->morphKeyFrames.push_back(kf); + } + else if (id == M_ANIMATION_POSE_KEYFRAME) + { + PoseKeyFrame kf; + kf.timePos = Read<float>(); + + if (!AtEnd()) + { + id = ReadHeader(); + while (!AtEnd() && id == M_ANIMATION_POSE_REF) + { + PoseRef pr; + pr.index = Read<uint16_t>(); + pr.influence = Read<float>(); + kf.references.push_back(pr); + + if (!AtEnd()) + id = ReadHeader(); + } + if (!AtEnd()) + RollbackHeader(); + } + + track->poseKeyFrames.push_back(kf); + } + + if (!AtEnd()) + id = ReadHeader(); + } + if (!AtEnd()) + RollbackHeader(); + } +} + +// Skeleton + +bool OgreBinarySerializer::ImportSkeleton(Assimp::IOSystem *pIOHandler, Mesh *mesh) +{ + if (!mesh || mesh->skeletonRef.empty()) + return false; + + // Highly unusual to see in read world cases but support + // binary mesh referencing a XML skeleton file. + if (EndsWith(mesh->skeletonRef, ".skeleton.xml", false)) + { + OgreXmlSerializer::ImportSkeleton(pIOHandler, mesh); + return false; + } + + MemoryStreamReaderPtr reader = OpenReader(pIOHandler, mesh->skeletonRef); + + Skeleton *skeleton = new Skeleton(); + OgreBinarySerializer serializer(reader.get(), OgreBinarySerializer::AM_Skeleton); + serializer.ReadSkeleton(skeleton); + mesh->skeleton = skeleton; + return true; +} + +bool OgreBinarySerializer::ImportSkeleton(Assimp::IOSystem *pIOHandler, MeshXml *mesh) +{ + if (!mesh || mesh->skeletonRef.empty()) + return false; + + MemoryStreamReaderPtr reader = OpenReader(pIOHandler, mesh->skeletonRef); + if (!reader.get()) + return false; + + Skeleton *skeleton = new Skeleton(); + OgreBinarySerializer serializer(reader.get(), OgreBinarySerializer::AM_Skeleton); + serializer.ReadSkeleton(skeleton); + mesh->skeleton = skeleton; + return true; +} + +MemoryStreamReaderPtr OgreBinarySerializer::OpenReader(Assimp::IOSystem *pIOHandler, const std::string &filename) +{ + if (!EndsWith(filename, ".skeleton", false)) + { + DefaultLogger::get()->error("Imported Mesh is referencing to unsupported '" + filename + "' skeleton file."); + return MemoryStreamReaderPtr(); + } + + if (!pIOHandler->Exists(filename)) + { + DefaultLogger::get()->error("Failed to find skeleton file '" + filename + "' that is referenced by imported Mesh."); + return MemoryStreamReaderPtr(); + } + + IOStream *f = pIOHandler->Open(filename, "rb"); + if (!f) { + throw DeadlyImportError("Failed to open skeleton file " + filename); + } + + return MemoryStreamReaderPtr(new MemoryStreamReader(f)); +} + +void OgreBinarySerializer::ReadSkeleton(Skeleton *skeleton) +{ + uint16_t id = ReadHeader(false); + if (id != HEADER_CHUNK_ID) { + throw DeadlyExportError("Invalid Ogre Skeleton file header."); + } + + // This deserialization supports both versions of the skeleton spec + std::string version = ReadLine(); + if (version != SKELETON_VERSION_1_8 && version != SKELETON_VERSION_1_1) + { + throw DeadlyExportError(Formatter::format() << "Skeleton version " << version << " not supported by this importer." + << " Supported versions: " << SKELETON_VERSION_1_8 << " and " << SKELETON_VERSION_1_1); + } + + DefaultLogger::get()->debug("Reading Skeleton"); + + bool firstBone = true; + bool firstAnim = true; + + while (!AtEnd()) + { + id = ReadHeader(); + switch(id) + { + case SKELETON_BLENDMODE: + { + skeleton->blendMode = static_cast<Skeleton::BlendMode>(Read<uint16_t>()); + break; + } + case SKELETON_BONE: + { + if (firstBone) + { + DefaultLogger::get()->debug(" - Bones"); + firstBone = false; + } + + ReadBone(skeleton); + break; + } + case SKELETON_BONE_PARENT: + { + ReadBoneParent(skeleton); + break; + } + case SKELETON_ANIMATION: + { + if (firstAnim) + { + DefaultLogger::get()->debug(" - Animations"); + firstAnim = false; + } + + ReadSkeletonAnimation(skeleton); + break; + } + case SKELETON_ANIMATION_LINK: + { + ReadSkeletonAnimationLink(skeleton); + break; + } + } + } + + // Calculate bone matrices for root bones. Recursively calculates their children. + for (size_t i=0, len=skeleton->bones.size(); i<len; ++i) + { + Bone *bone = skeleton->bones[i]; + if (!bone->IsParented()) + bone->CalculateWorldMatrixAndDefaultPose(skeleton); + } +} + +void OgreBinarySerializer::ReadBone(Skeleton *skeleton) +{ + Bone *bone = new Bone(); + bone->name = ReadLine(); + bone->id = Read<uint16_t>(); + + // Pos and rot + ReadVector(bone->position); + ReadQuaternion(bone->rotation); + + // Scale (optional) + if (m_currentLen > MSTREAM_BONE_SIZE_WITHOUT_SCALE) + ReadVector(bone->scale); + + // Bone indexes need to start from 0 and be contiguous + if (bone->id != skeleton->bones.size()) { + throw DeadlyImportError(Formatter::format() << "Ogre Skeleton bone indexes not contiguous. Error at bone index " << bone->id); + } + + DefaultLogger::get()->debug(Formatter::format() << " " << bone->id << " " << bone->name); + + skeleton->bones.push_back(bone); +} + +void OgreBinarySerializer::ReadBoneParent(Skeleton *skeleton) +{ + uint16_t childId = Read<uint16_t>(); + uint16_t parentId = Read<uint16_t>(); + + Bone *child = skeleton->BoneById(childId); + Bone *parent = skeleton->BoneById(parentId); + + if (child && parent) + parent->AddChild(child); + else + throw DeadlyImportError(Formatter::format() << "Failed to find bones for parenting: Child id " << childId << " for parent id " << parentId); +} + +void OgreBinarySerializer::ReadSkeletonAnimation(Skeleton *skeleton) +{ + Animation *anim = new Animation(skeleton); + anim->name = ReadLine(); + anim->length = Read<float>(); + + if (!AtEnd()) + { + uint16_t id = ReadHeader(); + if (id == SKELETON_ANIMATION_BASEINFO) + { + anim->baseName = ReadLine(); + anim->baseTime = Read<float>(); + + // Advance to first track + id = ReadHeader(); + } + + while (!AtEnd() && id == SKELETON_ANIMATION_TRACK) + { + ReadSkeletonAnimationTrack(skeleton, anim); + + if (!AtEnd()) + id = ReadHeader(); + } + if (!AtEnd()) + RollbackHeader(); + } + + skeleton->animations.push_back(anim); + + DefaultLogger::get()->debug(Formatter::format() << " " << anim->name << " (" << anim->length << " sec, " << anim->tracks.size() << " tracks)"); +} + +void OgreBinarySerializer::ReadSkeletonAnimationTrack(Skeleton *skeleton, Animation *dest) +{ + uint16_t boneId = Read<uint16_t>(); + Bone *bone = dest->parentSkeleton->BoneById(boneId); + if (!bone) { + throw DeadlyImportError(Formatter::format() << "Cannot read animation track, target bone " << boneId << " not in target Skeleton"); + } + + VertexAnimationTrack track; + track.type = VertexAnimationTrack::VAT_TRANSFORM; + track.boneName = bone->name; + + uint16_t id = ReadHeader(); + while (!AtEnd() && id == SKELETON_ANIMATION_TRACK_KEYFRAME) + { + ReadSkeletonAnimationKeyFrame(&track); + + if (!AtEnd()) + id = ReadHeader(); + } + if (!AtEnd()) + RollbackHeader(); + + dest->tracks.push_back(track); +} + +void OgreBinarySerializer::ReadSkeletonAnimationKeyFrame(VertexAnimationTrack *dest) +{ + TransformKeyFrame keyframe; + keyframe.timePos = Read<float>(); + + // Rot and pos + ReadQuaternion(keyframe.rotation); + ReadVector(keyframe.position); + + // Scale (optional) + if (m_currentLen > MSTREAM_KEYFRAME_SIZE_WITHOUT_SCALE) + ReadVector(keyframe.scale); + + dest->transformKeyFrames.push_back(keyframe); +} + +void OgreBinarySerializer::ReadSkeletonAnimationLink(Skeleton *skeleton) +{ + // Skip bounds, not compatible with Assimp. + ReadLine(); // skeleton name + SkipBytes(sizeof(float) * 3); // scale +} + +} // Ogre +} // Assimp + +#endif // ASSIMP_BUILD_NO_OGRE_IMPORTER |