From db81b925d776103326128bf629cbdda576a223e7 Mon Sep 17 00:00:00 2001 From: sanine Date: Sat, 16 Apr 2022 11:55:09 -0500 Subject: move 3rd-party librarys into libs/ and add built-in honeysuckle --- .../code/AssetLib/Ogre/OgreBinarySerializer.cpp | 973 ++++++++++++++++++ .../code/AssetLib/Ogre/OgreBinarySerializer.h | 415 ++++++++ libs/assimp/code/AssetLib/Ogre/OgreImporter.cpp | 135 +++ libs/assimp/code/AssetLib/Ogre/OgreImporter.h | 98 ++ libs/assimp/code/AssetLib/Ogre/OgreMaterial.cpp | 500 +++++++++ libs/assimp/code/AssetLib/Ogre/OgreParsingUtils.h | 95 ++ libs/assimp/code/AssetLib/Ogre/OgreStructs.cpp | 1076 ++++++++++++++++++++ libs/assimp/code/AssetLib/Ogre/OgreStructs.h | 696 +++++++++++++ .../code/AssetLib/Ogre/OgreXmlSerializer.cpp | 755 ++++++++++++++ libs/assimp/code/AssetLib/Ogre/OgreXmlSerializer.h | 102 ++ 10 files changed, 4845 insertions(+) create mode 100644 libs/assimp/code/AssetLib/Ogre/OgreBinarySerializer.cpp create mode 100644 libs/assimp/code/AssetLib/Ogre/OgreBinarySerializer.h create mode 100644 libs/assimp/code/AssetLib/Ogre/OgreImporter.cpp create mode 100644 libs/assimp/code/AssetLib/Ogre/OgreImporter.h create mode 100644 libs/assimp/code/AssetLib/Ogre/OgreMaterial.cpp create mode 100644 libs/assimp/code/AssetLib/Ogre/OgreParsingUtils.h create mode 100644 libs/assimp/code/AssetLib/Ogre/OgreStructs.cpp create mode 100644 libs/assimp/code/AssetLib/Ogre/OgreStructs.h create mode 100644 libs/assimp/code/AssetLib/Ogre/OgreXmlSerializer.cpp create mode 100644 libs/assimp/code/AssetLib/Ogre/OgreXmlSerializer.h (limited to 'libs/assimp/code/AssetLib/Ogre') diff --git a/libs/assimp/code/AssetLib/Ogre/OgreBinarySerializer.cpp b/libs/assimp/code/AssetLib/Ogre/OgreBinarySerializer.cpp new file mode 100644 index 0000000..738e118 --- /dev/null +++ b/libs/assimp/code/AssetLib/Ogre/OgreBinarySerializer.cpp @@ -0,0 +1,973 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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 "OgreParsingUtils.h" +#include "OgreXmlSerializer.h" + +#include +#include + +#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER + +// Define as 1 to get verbose logging. +#define OGRE_BINARY_SERIALIZER_DEBUG 0 + +namespace Assimp { +namespace Ogre { + +static constexpr auto MESH_VERSION_1_8 = "[MeshSerializer_v1.8]"; +static constexpr auto SKELETON_VERSION_1_8 = "[Serializer_v1.80]"; +static constexpr auto 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() { + return (m_reader->GetU1() > 0); +} + +template <> +inline char OgreBinarySerializer::Read() { + return static_cast(m_reader->GetU1()); +} + +template <> +inline uint8_t OgreBinarySerializer::Read() { + return m_reader->GetU1(); +} + +template <> +inline uint16_t OgreBinarySerializer::Read() { + return m_reader->GetU2(); +} + +template <> +inline uint32_t OgreBinarySerializer::Read() { + return m_reader->GetU4(); +} + +template <> +inline float OgreBinarySerializer::Read() { + return m_reader->GetF4(); +} + +void OgreBinarySerializer::ReadBytes(char *dest, size_t numBytes) { + ReadBytes(static_cast(dest), numBytes); +} + +void OgreBinarySerializer::ReadBytes(uint8_t *dest, size_t numBytes) { + ReadBytes(static_cast(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(); + if (c == '\n') + break; + str += c; + } + return str; +} + +uint16_t OgreBinarySerializer::ReadHeader(bool readLen) { + uint16_t id = Read(); + if (readLen) + m_currentLen = Read(); + +#if (OGRE_BINARY_SERIALIZER_DEBUG == 1) + if (id != HEADER_CHUNK_ID) { + ASSIMP_LOG_DEBUG((assetMode == AM_Mesh ? MeshHeaderToString(static_cast(id)) : SkeletonHeaderToString(static_cast(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) + ASSIMP_LOG_VERBOSE_DEBUG("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 DeadlyImportError("Invalid Ogre Mesh file header."); + } + + /// @todo Check what we can actually support. + std::string version = serializer.ReadLine(); + if (version != MESH_VERSION_1_8) { + throw DeadlyImportError("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(); + + ASSIMP_LOG_VERBOSE_DEBUG("Reading Mesh"); + ASSIMP_LOG_VERBOSE_DEBUG(" - 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(); + bool manual = Read(); + + /// @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(); + bool is32bit = Read(); + + 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(); + ba.boneIndex = Read(); + ba.weight = Read(); + + dest->boneAssignments.push_back(ba); +} + +void OgreBinarySerializer::ReadSubMesh(Mesh *mesh) { + uint16_t id = 0; + + SubMesh *submesh = new SubMesh(); + submesh->materialRef = ReadLine(); + submesh->usesSharedVertexData = Read(); + + submesh->indexData->count = Read(); + submesh->indexData->faceCount = static_cast(submesh->indexData->count / 3); + submesh->indexData->is32bit = Read(); + + ASSIMP_LOG_VERBOSE_DEBUG("Reading SubMesh ", mesh->subMeshes.size()); + ASSIMP_LOG_VERBOSE_DEBUG(" - Material: '", submesh->materialRef, "'"); + ASSIMP_LOG_VERBOSE_DEBUG(" - 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)); + + ASSIMP_LOG_VERBOSE_DEBUG(" - ", 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 = static_cast(mesh->subMeshes.size()); + mesh->subMeshes.push_back(submesh); +} + +void OgreBinarySerializer::NormalizeBoneWeights(VertexData *vertexData) const { + if (!vertexData || vertexData->boneAssignments.empty()) + return; + + std::set 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 won't 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 (const uint32_t vertexIndex : influencedVertices) { + 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 (auto &boneAssign : vertexData->boneAssignments) { + if (boneAssign.vertexIndex == vertexIndex) + boneAssign.weight /= sum; + } + } + } +} + +void OgreBinarySerializer::ReadSubMeshOperation(SubMesh *submesh) { + submesh->operationType = static_cast(Read()); +} + +void OgreBinarySerializer::ReadSubMeshTextureAlias(SubMesh *submesh) { + submesh->textureAliasName = ReadLine(); + submesh->textureAliasRef = ReadLine(); +} + +void OgreBinarySerializer::ReadSubMeshNames(Mesh *mesh) { + uint16_t id = 0; + + if (!AtEnd()) { + id = ReadHeader(); + while (!AtEnd() && id == M_SUBMESH_NAME_TABLE_ELEMENT) { + uint16_t submeshIndex = Read(); + SubMesh *submesh = mesh->GetSubMesh(submeshIndex); + if (!submesh) { + throw DeadlyImportError("Ogre Mesh does not include submesh ", submeshIndex, " referenced in M_SUBMESH_NAME_TABLE_ELEMENT. Invalid mesh file."); + } + + submesh->name = ReadLine(); + ASSIMP_LOG_VERBOSE_DEBUG(" - SubMesh ", submesh->index, " name '", submesh->name, "'"); + + if (!AtEnd()) + id = ReadHeader(); + } + if (!AtEnd()) + RollbackHeader(); + } +} + +void OgreBinarySerializer::ReadGeometry(VertexData *dest) { + dest->count = Read(); + + ASSIMP_LOG_VERBOSE_DEBUG(" - 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(); + element.type = static_cast(Read()); + element.semantic = static_cast(Read()); + element.offset = Read(); + element.index = Read(); + + ASSIMP_LOG_VERBOSE_DEBUG(" - 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 vertexSize = Read(); + + 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)); + + ASSIMP_LOG_VERBOSE_DEBUG(" - 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(); + + if (!manual) { + m_reader->IncPtr(sizeof(uint8_t)); + uint32_t numTriangles = Read(); + uint32_t numEdgeGroups = Read(); + + 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 curId = ReadHeader(); + if (curId != 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(); + 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(); + pose->hasNormals = Read(); + + 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(); + 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(); + + 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(); + + // Advance to first track + id = ReadHeader(); + } + + while (!AtEnd() && id == M_ANIMATION_TRACK) { + VertexAnimationTrack track; + track.type = static_cast(Read()); + track.target = Read(); + + 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(); + bool hasNormals = Read(); + + 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(); + + if (!AtEnd()) { + id = ReadHeader(); + while (!AtEnd() && id == M_ANIMATION_POSE_REF) { + PoseRef pr; + pr.index = Read(); + pr.influence = Read(); + 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); + if (!reader) + return false; + + 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)) { + ASSIMP_LOG_ERROR("Imported Mesh is referencing to unsupported '", filename, "' skeleton file."); + return MemoryStreamReaderPtr(); + } + + if (!pIOHandler->Exists(filename)) { + ASSIMP_LOG_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 DeadlyImportError("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 DeadlyImportError("Skeleton version ", version, " not supported by this importer.", + " Supported versions: ", SKELETON_VERSION_1_8, " and ", SKELETON_VERSION_1_1); + } + + ASSIMP_LOG_VERBOSE_DEBUG("Reading Skeleton"); + + bool firstBone = true; + bool firstAnim = true; + + while (!AtEnd()) { + id = ReadHeader(); + switch (id) { + case SKELETON_BLENDMODE: { + skeleton->blendMode = static_cast(Read()); + break; + } + case SKELETON_BONE: { + if (firstBone) { + ASSIMP_LOG_VERBOSE_DEBUG(" - Bones"); + firstBone = false; + } + + ReadBone(skeleton); + break; + } + case SKELETON_BONE_PARENT: { + ReadBoneParent(skeleton); + break; + } + case SKELETON_ANIMATION: { + if (firstAnim) { + ASSIMP_LOG_VERBOSE_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(); + + // 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("Ogre Skeleton bone indexes not contiguous. Error at bone index ", bone->id); + } + + ASSIMP_LOG_VERBOSE_DEBUG(" ", bone->id, " ", bone->name); + + skeleton->bones.push_back(bone); +} + +void OgreBinarySerializer::ReadBoneParent(Skeleton *skeleton) { + uint16_t childId = Read(); + uint16_t parentId = Read(); + + Bone *child = skeleton->BoneById(childId); + Bone *parent = skeleton->BoneById(parentId); + + if (child && parent) + parent->AddChild(child); + else + throw DeadlyImportError("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(); + + if (!AtEnd()) { + uint16_t id = ReadHeader(); + if (id == SKELETON_ANIMATION_BASEINFO) { + anim->baseName = ReadLine(); + anim->baseTime = Read(); + + // 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); + + ASSIMP_LOG_VERBOSE_DEBUG(" ", anim->name, " (", anim->length, " sec, ", anim->tracks.size(), " tracks)"); +} + +void OgreBinarySerializer::ReadSkeletonAnimationTrack(Skeleton * /*skeleton*/, Animation *dest) { + uint16_t boneId = Read(); + Bone *bone = dest->parentSkeleton->BoneById(boneId); + if (!bone) { + throw DeadlyImportError("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(); + + // 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 +} + +} // namespace Ogre +} // namespace Assimp + +#endif // ASSIMP_BUILD_NO_OGRE_IMPORTER diff --git a/libs/assimp/code/AssetLib/Ogre/OgreBinarySerializer.h b/libs/assimp/code/AssetLib/Ogre/OgreBinarySerializer.h new file mode 100644 index 0000000..eb026ea --- /dev/null +++ b/libs/assimp/code/AssetLib/Ogre/OgreBinarySerializer.h @@ -0,0 +1,415 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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. + +---------------------------------------------------------------------- +*/ + +#ifndef AI_OGREBINARYSERIALIZER_H_INC +#define AI_OGREBINARYSERIALIZER_H_INC + +#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER + +#include "OgreStructs.h" +#include + +namespace Assimp { +namespace Ogre { + +typedef Assimp::StreamReaderLE MemoryStreamReader; +typedef std::shared_ptr MemoryStreamReaderPtr; + +class OgreBinarySerializer { +public: + /// Imports mesh and returns the result. + /** @note Fatal unrecoverable errors will throw a DeadlyImportError. */ + static Mesh *ImportMesh(MemoryStreamReader *reader); + + /// Imports skeleton to @c mesh into Mesh::skeleton. + /** If mesh does not have a skeleton reference or the skeleton file + cannot be found it is not a fatal DeadlyImportError. + @return If skeleton import was successful. */ + static bool ImportSkeleton(Assimp::IOSystem *pIOHandler, Mesh *mesh); + static bool ImportSkeleton(Assimp::IOSystem *pIOHandler, MeshXml *mesh); + +private: + enum AssetMode { + AM_Mesh, + AM_Skeleton + }; + + OgreBinarySerializer(MemoryStreamReader *reader, AssetMode mode) : + m_currentLen(0), + m_reader(reader), + assetMode(mode) { + } + + static MemoryStreamReaderPtr OpenReader(Assimp::IOSystem *pIOHandler, const std::string &filename); + + // Header + + uint16_t ReadHeader(bool readLen = true); + void RollbackHeader(); + + // Mesh + + void ReadMesh(Mesh *mesh); + void ReadMeshLodInfo(Mesh *mesh); + void ReadMeshSkeletonLink(Mesh *mesh); + void ReadMeshBounds(Mesh *mesh); + void ReadMeshExtremes(Mesh *mesh); + + void ReadSubMesh(Mesh *mesh); + void ReadSubMeshNames(Mesh *mesh); + void ReadSubMeshOperation(SubMesh *submesh); + void ReadSubMeshTextureAlias(SubMesh *submesh); + + void ReadBoneAssignment(VertexData *dest); + + void ReadGeometry(VertexData *dest); + void ReadGeometryVertexDeclaration(VertexData *dest); + void ReadGeometryVertexElement(VertexData *dest); + void ReadGeometryVertexBuffer(VertexData *dest); + + void ReadEdgeList(Mesh *mesh); + void ReadPoses(Mesh *mesh); + void ReadPoseVertices(Pose *pose); + + void ReadAnimations(Mesh *mesh); + void ReadAnimation(Animation *anim); + void ReadAnimationKeyFrames(Animation *anim, VertexAnimationTrack *track); + + void NormalizeBoneWeights(VertexData *vertexData) const; + + // Skeleton + + void ReadSkeleton(Skeleton *skeleton); + + void ReadBone(Skeleton *skeleton); + void ReadBoneParent(Skeleton *skeleton); + + void ReadSkeletonAnimation(Skeleton *skeleton); + void ReadSkeletonAnimationTrack(Skeleton *skeleton, Animation *dest); + void ReadSkeletonAnimationKeyFrame(VertexAnimationTrack *dest); + void ReadSkeletonAnimationLink(Skeleton *skeleton); + + // Reader utils + bool AtEnd() const; + + template + inline T Read(); + + void ReadBytes(char *dest, size_t numBytes); + void ReadBytes(uint8_t *dest, size_t numBytes); + void ReadBytes(void *dest, size_t numBytes); + uint8_t *ReadBytes(size_t numBytes); + + void ReadVector(aiVector3D &vec); + void ReadQuaternion(aiQuaternion &quat); + + std::string ReadString(size_t len); + std::string ReadLine(); + + void SkipBytes(size_t numBytes); + + uint32_t m_currentLen; + MemoryStreamReader *m_reader; + + AssetMode assetMode; +}; + +enum MeshChunkId { + M_HEADER = 0x1000, + // char* version : Version number check + M_MESH = 0x3000, + // bool skeletallyAnimated // important flag which affects h/w buffer policies + // Optional M_GEOMETRY chunk + M_SUBMESH = 0x4000, + // char* materialName + // bool useSharedVertices + // unsigned int indexCount + // bool indexes32Bit + // unsigned int* faceVertexIndices (indexCount) + // OR + // unsigned short* faceVertexIndices (indexCount) + // M_GEOMETRY chunk (Optional: present only if useSharedVertices = false) + M_SUBMESH_OPERATION = 0x4010, // optional, trilist assumed if missing + // unsigned short operationType + M_SUBMESH_BONE_ASSIGNMENT = 0x4100, + // Optional bone weights (repeating section) + // unsigned int vertexIndex; + // unsigned short boneIndex; + // float weight; + // Optional chunk that matches a texture name to an alias + // a texture alias is sent to the submesh material to use this texture name + // instead of the one in the texture unit with a matching alias name + M_SUBMESH_TEXTURE_ALIAS = 0x4200, // Repeating section + // char* aliasName; + // char* textureName; + + M_GEOMETRY = 0x5000, // NB this chunk is embedded within M_MESH and M_SUBMESH + // unsigned int vertexCount + M_GEOMETRY_VERTEX_DECLARATION = 0x5100, + M_GEOMETRY_VERTEX_ELEMENT = 0x5110, // Repeating section + // unsigned short source; // buffer bind source + // unsigned short type; // VertexElementType + // unsigned short semantic; // VertexElementSemantic + // unsigned short offset; // start offset in buffer in bytes + // unsigned short index; // index of the semantic (for colours and texture coords) + M_GEOMETRY_VERTEX_BUFFER = 0x5200, // Repeating section + // unsigned short bindIndex; // Index to bind this buffer to + // unsigned short vertexSize; // Per-vertex size, must agree with declaration at this index + M_GEOMETRY_VERTEX_BUFFER_DATA = 0x5210, + // raw buffer data + M_MESH_SKELETON_LINK = 0x6000, + // Optional link to skeleton + // char* skeletonName : name of .skeleton to use + M_MESH_BONE_ASSIGNMENT = 0x7000, + // Optional bone weights (repeating section) + // unsigned int vertexIndex; + // unsigned short boneIndex; + // float weight; + M_MESH_LOD = 0x8000, + // Optional LOD information + // string strategyName; + // unsigned short numLevels; + // bool manual; (true for manual alternate meshes, false for generated) + M_MESH_LOD_USAGE = 0x8100, + // Repeating section, ordered in increasing depth + // NB LOD 0 (full detail from 0 depth) is omitted + // LOD value - this is a distance, a pixel count etc, based on strategy + // float lodValue; + M_MESH_LOD_MANUAL = 0x8110, + // Required if M_MESH_LOD section manual = true + // String manualMeshName; + M_MESH_LOD_GENERATED = 0x8120, + // Required if M_MESH_LOD section manual = false + // Repeating section (1 per submesh) + // unsigned int indexCount; + // bool indexes32Bit + // unsigned short* faceIndexes; (indexCount) + // OR + // unsigned int* faceIndexes; (indexCount) + M_MESH_BOUNDS = 0x9000, + // float minx, miny, minz + // float maxx, maxy, maxz + // float radius + + // Added By DrEvil + // optional chunk that contains a table of submesh indexes and the names of + // the sub-meshes. + M_SUBMESH_NAME_TABLE = 0xA000, + // Subchunks of the name table. Each chunk contains an index & string + M_SUBMESH_NAME_TABLE_ELEMENT = 0xA100, + // short index + // char* name + // Optional chunk which stores precomputed edge data + M_EDGE_LISTS = 0xB000, + // Each LOD has a separate edge list + M_EDGE_LIST_LOD = 0xB100, + // unsigned short lodIndex + // bool isManual // If manual, no edge data here, loaded from manual mesh + // bool isClosed + // unsigned long numTriangles + // unsigned long numEdgeGroups + // Triangle* triangleList + // unsigned long indexSet + // unsigned long vertexSet + // unsigned long vertIndex[3] + // unsigned long sharedVertIndex[3] + // float normal[4] + + M_EDGE_GROUP = 0xB110, + // unsigned long vertexSet + // unsigned long triStart + // unsigned long triCount + // unsigned long numEdges + // Edge* edgeList + // unsigned long triIndex[2] + // unsigned long vertIndex[2] + // unsigned long sharedVertIndex[2] + // bool degenerate + // Optional poses section, referred to by pose keyframes + M_POSES = 0xC000, + M_POSE = 0xC100, + // char* name (may be blank) + // unsigned short target // 0 for shared geometry, + // 1+ for submesh index + 1 + // bool includesNormals [1.8+] + M_POSE_VERTEX = 0xC111, + // unsigned long vertexIndex + // float xoffset, yoffset, zoffset + // float xnormal, ynormal, znormal (optional, 1.8+) + // Optional vertex animation chunk + M_ANIMATIONS = 0xD000, + M_ANIMATION = 0xD100, + // char* name + // float length + M_ANIMATION_BASEINFO = 0xD105, + // [Optional] base keyframe information (pose animation only) + // char* baseAnimationName (blank for self) + // float baseKeyFrameTime + M_ANIMATION_TRACK = 0xD110, + // unsigned short type // 1 == morph, 2 == pose + // unsigned short target // 0 for shared geometry, + // 1+ for submesh index + 1 + M_ANIMATION_MORPH_KEYFRAME = 0xD111, + // float time + // bool includesNormals [1.8+] + // float x,y,z // repeat by number of vertices in original geometry + M_ANIMATION_POSE_KEYFRAME = 0xD112, + // float time + M_ANIMATION_POSE_REF = 0xD113, // repeat for number of referenced poses + // unsigned short poseIndex + // float influence + // Optional submesh extreme vertex list chink + M_TABLE_EXTREMES = 0xE000 + // unsigned short submesh_index; + // float extremes [n_extremes][3]; +}; + +/* +static std::string MeshHeaderToString(MeshChunkId id) +{ + switch(id) + { + case M_HEADER: return "HEADER"; + case M_MESH: return "MESH"; + case M_SUBMESH: return "SUBMESH"; + case M_SUBMESH_OPERATION: return "SUBMESH_OPERATION"; + case M_SUBMESH_BONE_ASSIGNMENT: return "SUBMESH_BONE_ASSIGNMENT"; + case M_SUBMESH_TEXTURE_ALIAS: return "SUBMESH_TEXTURE_ALIAS"; + case M_GEOMETRY: return "GEOMETRY"; + case M_GEOMETRY_VERTEX_DECLARATION: return "GEOMETRY_VERTEX_DECLARATION"; + case M_GEOMETRY_VERTEX_ELEMENT: return "GEOMETRY_VERTEX_ELEMENT"; + case M_GEOMETRY_VERTEX_BUFFER: return "GEOMETRY_VERTEX_BUFFER"; + case M_GEOMETRY_VERTEX_BUFFER_DATA: return "GEOMETRY_VERTEX_BUFFER_DATA"; + case M_MESH_SKELETON_LINK: return "MESH_SKELETON_LINK"; + case M_MESH_BONE_ASSIGNMENT: return "MESH_BONE_ASSIGNMENT"; + case M_MESH_LOD: return "MESH_LOD"; + case M_MESH_LOD_USAGE: return "MESH_LOD_USAGE"; + case M_MESH_LOD_MANUAL: return "MESH_LOD_MANUAL"; + case M_MESH_LOD_GENERATED: return "MESH_LOD_GENERATED"; + case M_MESH_BOUNDS: return "MESH_BOUNDS"; + case M_SUBMESH_NAME_TABLE: return "SUBMESH_NAME_TABLE"; + case M_SUBMESH_NAME_TABLE_ELEMENT: return "SUBMESH_NAME_TABLE_ELEMENT"; + case M_EDGE_LISTS: return "EDGE_LISTS"; + case M_EDGE_LIST_LOD: return "EDGE_LIST_LOD"; + case M_EDGE_GROUP: return "EDGE_GROUP"; + case M_POSES: return "POSES"; + case M_POSE: return "POSE"; + case M_POSE_VERTEX: return "POSE_VERTEX"; + case M_ANIMATIONS: return "ANIMATIONS"; + case M_ANIMATION: return "ANIMATION"; + case M_ANIMATION_BASEINFO: return "ANIMATION_BASEINFO"; + case M_ANIMATION_TRACK: return "ANIMATION_TRACK"; + case M_ANIMATION_MORPH_KEYFRAME: return "ANIMATION_MORPH_KEYFRAME"; + case M_ANIMATION_POSE_KEYFRAME: return "ANIMATION_POSE_KEYFRAME"; + case M_ANIMATION_POSE_REF: return "ANIMATION_POSE_REF"; + case M_TABLE_EXTREMES: return "TABLE_EXTREMES"; + } + return "Unknown_MeshChunkId"; +} +*/ + +enum SkeletonChunkId { + SKELETON_HEADER = 0x1000, + // char* version : Version number check + SKELETON_BLENDMODE = 0x1010, // optional + // unsigned short blendmode : SkeletonAnimationBlendMode + SKELETON_BONE = 0x2000, + // Repeating section defining each bone in the system. + // Bones are assigned indexes automatically based on their order of declaration + // starting with 0. + // char* name : name of the bone + // unsigned short handle : handle of the bone, should be contiguous & start at 0 + // Vector3 position : position of this bone relative to parent + // Quaternion orientation : orientation of this bone relative to parent + // Vector3 scale : scale of this bone relative to parent + SKELETON_BONE_PARENT = 0x3000, + // Record of the parent of a single bone, used to build the node tree + // Repeating section, listed in Bone Index order, one per Bone + // unsigned short handle : child bone + // unsigned short parentHandle : parent bone + SKELETON_ANIMATION = 0x4000, + // A single animation for this skeleton + // char* name : Name of the animation + // float length : Length of the animation in seconds + SKELETON_ANIMATION_BASEINFO = 0x4010, + // [Optional] base keyframe information + // char* baseAnimationName (blank for self) + // float baseKeyFrameTime + SKELETON_ANIMATION_TRACK = 0x4100, + // A single animation track (relates to a single bone) + // Repeating section (within SKELETON_ANIMATION) + // unsigned short boneIndex : Index of bone to apply to + SKELETON_ANIMATION_TRACK_KEYFRAME = 0x4110, + // A single keyframe within the track + // Repeating section + // float time : The time position (seconds) + // Quaternion rotate : Rotation to apply at this keyframe + // Vector3 translate : Translation to apply at this keyframe + // Vector3 scale : Scale to apply at this keyframe + SKELETON_ANIMATION_LINK = 0x5000 + // Link to another skeleton, to re-use its animations + // char* skeletonName : name of skeleton to get animations from + // float scale : scale to apply to trans/scale keys +}; + +/* +static std::string SkeletonHeaderToString(SkeletonChunkId id) +{ + switch(id) + { + case SKELETON_HEADER: return "HEADER"; + case SKELETON_BLENDMODE: return "BLENDMODE"; + case SKELETON_BONE: return "BONE"; + case SKELETON_BONE_PARENT: return "BONE_PARENT"; + case SKELETON_ANIMATION: return "ANIMATION"; + case SKELETON_ANIMATION_BASEINFO: return "ANIMATION_BASEINFO"; + case SKELETON_ANIMATION_TRACK: return "ANIMATION_TRACK"; + case SKELETON_ANIMATION_TRACK_KEYFRAME: return "ANIMATION_TRACK_KEYFRAME"; + case SKELETON_ANIMATION_LINK: return "ANIMATION_LINK"; + } + return "Unknown_SkeletonChunkId"; +} +*/ +} // namespace Ogre +} // namespace Assimp + +#endif // ASSIMP_BUILD_NO_OGRE_IMPORTER +#endif // AI_OGREBINARYSERIALIZER_H_INC diff --git a/libs/assimp/code/AssetLib/Ogre/OgreImporter.cpp b/libs/assimp/code/AssetLib/Ogre/OgreImporter.cpp new file mode 100644 index 0000000..860aed7 --- /dev/null +++ b/libs/assimp/code/AssetLib/Ogre/OgreImporter.cpp @@ -0,0 +1,135 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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. + +---------------------------------------------------------------------- +*/ + +#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER + +#include "OgreImporter.h" +#include "OgreBinarySerializer.h" +#include "OgreXmlSerializer.h" +#include +#include +#include + +static const aiImporterDesc desc = { + "Ogre3D Mesh Importer", + "", + "", + "", + aiImporterFlags_SupportTextFlavour | aiImporterFlags_SupportBinaryFlavour, + 0, + 0, + 0, + 0, + "mesh mesh.xml" +}; + +namespace Assimp { +namespace Ogre { + +const aiImporterDesc *OgreImporter::GetInfo() const { + return &desc; +} + +void OgreImporter::SetupProperties(const Importer *pImp) { + m_userDefinedMaterialLibFile = pImp->GetPropertyString(AI_CONFIG_IMPORT_OGRE_MATERIAL_FILE, "Scene.material"); + m_detectTextureTypeFromFilename = pImp->GetPropertyBool(AI_CONFIG_IMPORT_OGRE_TEXTURETYPE_FROM_FILENAME, false); +} + +bool OgreImporter::CanRead(const std::string &pFile, Assimp::IOSystem *pIOHandler, bool /*checkSig*/) const { + if (EndsWith(pFile, ".mesh.xml", false)) { + static const char *tokens[] = { "" }; + return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens)); + } + + /// @todo Read and validate first header chunk? + return EndsWith(pFile, ".mesh", false); +} + +void OgreImporter::InternReadFile(const std::string &pFile, aiScene *pScene, Assimp::IOSystem *pIOHandler) { + // Open source file + IOStream *f = pIOHandler->Open(pFile, "rb"); + if (!f) { + throw DeadlyImportError("Failed to open file ", pFile); + } + + // Binary .mesh import + if (EndsWith(pFile, ".mesh", false)) { + /// @note MemoryStreamReader takes ownership of f. + MemoryStreamReader reader(f); + + // Import mesh + std::unique_ptr mesh(OgreBinarySerializer::ImportMesh(&reader)); + + // Import skeleton + OgreBinarySerializer::ImportSkeleton(pIOHandler, mesh.get()); + + // Import mesh referenced materials + ReadMaterials(pFile, pIOHandler, pScene, mesh.get()); + + // Convert to Assimp + mesh->ConvertToAssimpScene(pScene); + return; + } + // XML .mesh.xml import + /// @note XmlReader does not take ownership of f, hence the scoped ptr. + std::unique_ptr scopedFile(f); + XmlParser xmlParser; + + //std::unique_ptr xmlStream(new CIrrXML_IOStreamReader(scopedFile.get())); + //std::unique_ptr reader(irr::io::createIrrXMLReader(xmlStream.get())); + xmlParser.parse(scopedFile.get()); + // Import mesh + std::unique_ptr mesh(OgreXmlSerializer::ImportMesh(&xmlParser)); + + // Import skeleton + OgreXmlSerializer::ImportSkeleton(pIOHandler, mesh.get()); + + // Import mesh referenced materials + ReadMaterials(pFile, pIOHandler, pScene, mesh.get()); + + // Convert to Assimp + mesh->ConvertToAssimpScene(pScene); +} + +} // namespace Ogre +} // namespace Assimp + +#endif // ASSIMP_BUILD_NO_OGRE_IMPORTER diff --git a/libs/assimp/code/AssetLib/Ogre/OgreImporter.h b/libs/assimp/code/AssetLib/Ogre/OgreImporter.h new file mode 100644 index 0000000..dc8b090 --- /dev/null +++ b/libs/assimp/code/AssetLib/Ogre/OgreImporter.h @@ -0,0 +1,98 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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. + +---------------------------------------------------------------------- +*/ +#pragma once +#ifndef AI_OGREIMPORTER_H_INC +#define AI_OGREIMPORTER_H_INC + +#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER + +#include +#include + +#include "OgreParsingUtils.h" +#include "OgreStructs.h" + +namespace Assimp { +namespace Ogre { + +/** Importer for Ogre mesh, skeleton and material formats. + @todo Support vertex colors. + @todo Support poses/animations from the mesh file. + Currently only skeleton file animations are supported. */ +class OgreImporter : public BaseImporter { +public: + /// BaseImporter override. + virtual bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const override; + +protected: + /// BaseImporter override. + virtual void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) override; + + /// BaseImporter override. + virtual const aiImporterDesc *GetInfo() const override; + + /// BaseImporter override. + virtual void SetupProperties(const Importer *pImp) override; + +private: + /// Read materials referenced by the @c mesh to @c pScene. + void ReadMaterials(const std::string &pFile, Assimp::IOSystem *pIOHandler, aiScene *pScene, Mesh *mesh); + void ReadMaterials(const std::string &pFile, Assimp::IOSystem *pIOHandler, aiScene *pScene, MeshXml *mesh); + void AssignMaterials(aiScene *pScene, std::vector &materials); + + /// Reads material + aiMaterial *ReadMaterial(const std::string &pFile, Assimp::IOSystem *pIOHandler, const std::string &MaterialName); + + // These functions parse blocks from a material file from @c ss. Starting parsing from "{" and ending it to "}". + bool ReadTechnique(const std::string &techniqueName, std::stringstream &ss, aiMaterial *material); + bool ReadPass(const std::string &passName, std::stringstream &ss, aiMaterial *material); + bool ReadTextureUnit(const std::string &textureUnitName, std::stringstream &ss, aiMaterial *material); + +private: + std::string m_userDefinedMaterialLibFile; + bool m_detectTextureTypeFromFilename; + std::map m_textures; +}; +} // namespace Ogre +} // namespace Assimp + +#endif // ASSIMP_BUILD_NO_OGRE_IMPORTER +#endif // AI_OGREIMPORTER_H_INC diff --git a/libs/assimp/code/AssetLib/Ogre/OgreMaterial.cpp b/libs/assimp/code/AssetLib/Ogre/OgreMaterial.cpp new file mode 100644 index 0000000..7478a80 --- /dev/null +++ b/libs/assimp/code/AssetLib/Ogre/OgreMaterial.cpp @@ -0,0 +1,500 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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. + +---------------------------------------------------------------------- +*/ + +#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER + +#include "OgreImporter.h" +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace std; + +namespace Assimp { +namespace Ogre { + +static const string partComment = "//"; +static const string partBlockStart = "{"; +static const string partBlockEnd = "}"; + +void OgreImporter::ReadMaterials(const std::string &pFile, Assimp::IOSystem *pIOHandler, aiScene *pScene, Mesh *mesh) { + std::vector materials; + + // Create materials that can be found and parsed via the IOSystem. + for (size_t i = 0, len = mesh->NumSubMeshes(); i < len; ++i) { + SubMesh *submesh = mesh->GetSubMesh(i); + if (submesh && !submesh->materialRef.empty()) { + aiMaterial *material = ReadMaterial(pFile, pIOHandler, submesh->materialRef); + if (material) { + submesh->materialIndex = static_cast(materials.size()); + materials.push_back(material); + } + } + } + + AssignMaterials(pScene, materials); +} + +void OgreImporter::ReadMaterials(const std::string &pFile, Assimp::IOSystem *pIOHandler, aiScene *pScene, MeshXml *mesh) { + std::vector materials; + + // Create materials that can be found and parsed via the IOSystem. + for (size_t i = 0, len = mesh->NumSubMeshes(); i < len; ++i) { + SubMeshXml *submesh = mesh->GetSubMesh(static_cast(i)); + if (submesh && !submesh->materialRef.empty()) { + aiMaterial *material = ReadMaterial(pFile, pIOHandler, submesh->materialRef); + if (material) { + submesh->materialIndex = static_cast(materials.size()); + materials.push_back(material); + } + } + } + + AssignMaterials(pScene, materials); +} + +void OgreImporter::AssignMaterials(aiScene *pScene, std::vector &materials) { + pScene->mNumMaterials = static_cast(materials.size()); + if (pScene->mNumMaterials > 0) { + pScene->mMaterials = new aiMaterial *[pScene->mNumMaterials]; + for (size_t i = 0; i < pScene->mNumMaterials; ++i) { + pScene->mMaterials[i] = materials[i]; + } + } +} + +aiMaterial *OgreImporter::ReadMaterial(const std::string &pFile, Assimp::IOSystem *pIOHandler, const std::string &materialName) { + if (materialName.empty()) { + return 0; + } + + // Full reference and examples of Ogre Material Script + // can be found from http://www.ogre3d.org/docs/manual/manual_14.html + + /*and here is another one: + + import * from abstract_base_passes_depth.material + import * from abstract_base.material + import * from mat_shadow_caster.material + import * from mat_character_singlepass.material + + material hero/hair/caster : mat_shadow_caster_skin_areject + { + set $diffuse_map "hero_hair_alpha_c.dds" + } + + material hero/hair_alpha : mat_char_cns_singlepass_areject_4weights + { + set $diffuse_map "hero_hair_alpha_c.dds" + set $specular_map "hero_hair_alpha_s.dds" + set $normal_map "hero_hair_alpha_n.dds" + set $light_map "black_lightmap.dds" + + set $shadow_caster_material "hero/hair/caster" + } + */ + + stringstream ss; + + // Scope for scopre_ptr auto release + { + /* There are three .material options in priority order: + 1) File with the material name (materialName) + 2) File with the mesh files base name (pFile) + 3) Optional user defined material library file (m_userDefinedMaterialLibFile) */ + std::vector potentialFiles; + potentialFiles.push_back(materialName + ".material"); + potentialFiles.push_back(pFile.substr(0, pFile.rfind(".mesh")) + ".material"); + if (!m_userDefinedMaterialLibFile.empty()) + potentialFiles.push_back(m_userDefinedMaterialLibFile); + + IOStream *materialFile = 0; + for (size_t i = 0; i < potentialFiles.size(); ++i) { + materialFile = pIOHandler->Open(potentialFiles[i]); + if (materialFile) { + break; + } + ASSIMP_LOG_VERBOSE_DEBUG("Source file for material '", materialName, "' ", potentialFiles[i], " does not exist"); + } + if (!materialFile) { + ASSIMP_LOG_ERROR("Failed to find source file for material '", materialName, "'"); + return 0; + } + + std::unique_ptr stream(materialFile); + if (stream->FileSize() == 0) { + ASSIMP_LOG_WARN("Source file for material '", materialName, "' is empty (size is 0 bytes)"); + return 0; + } + + // Read bytes + vector data(stream->FileSize()); + stream->Read(&data[0], stream->FileSize(), 1); + + // Convert to UTF-8 and terminate the string for ss + BaseImporter::ConvertToUTF8(data); + data.push_back('\0'); + + ss << &data[0]; + } + + ASSIMP_LOG_VERBOSE_DEBUG("Reading material '", materialName, "'"); + + aiMaterial *material = new aiMaterial(); + m_textures.clear(); + + aiString matName(materialName); + material->AddProperty(&matName, AI_MATKEY_NAME); + + // The stringstream will push words from a line until newline. + // It will also trim whitespace from line start and between words. + string linePart; + ss >> linePart; + + const string partMaterial = "material"; + const string partTechnique = "technique"; + + while (!ss.eof()) { + // Skip commented lines + if (linePart == partComment) { + NextAfterNewLine(ss, linePart); + continue; + } + if (linePart != partMaterial) { + ss >> linePart; + continue; + } + + ss >> linePart; + if (linePart != materialName) { + ss >> linePart; + continue; + } + + NextAfterNewLine(ss, linePart); + if (linePart != partBlockStart) { + ASSIMP_LOG_ERROR("Invalid material: block start missing near index ", ss.tellg()); + return material; + } + + ASSIMP_LOG_VERBOSE_DEBUG("material '", materialName, "'"); + + while (linePart != partBlockEnd) { + // Proceed to the first technique + ss >> linePart; + + if (linePart == partTechnique) { + std::string techniqueName = SkipLine(ss); + ReadTechnique(ai_trim(techniqueName), ss, material); + } + + // Read information from a custom material + /** @todo This "set $x y" does not seem to be a official Ogre material system feature. + Materials can inherit other materials and override texture units by using the (unique) + parent texture unit name in your cloned material. + This is not yet supported and below code is probably some hack from the original + author of this Ogre importer. Should be removed? */ + if (linePart == "set") { + ss >> linePart; + if (linePart == "$specular") //todo load this values: + { + } else if (linePart == "$diffuse") { + } else if (linePart == "$ambient") { + } else if (linePart == "$colormap") { + ss >> linePart; + aiString cm(linePart); + material->AddProperty(&cm, AI_MATKEY_TEXTURE(aiTextureType_DIFFUSE, 0)); + } else if (linePart == "$normalmap") { + ss >> linePart; + aiString nm(linePart); + material->AddProperty(&nm, AI_MATKEY_TEXTURE(aiTextureType_NORMALS, 0)); + } else if (linePart == "$shininess_strength") { + ss >> linePart; + float Shininess = fast_atof(linePart.c_str()); + material->AddProperty(&Shininess, 1, AI_MATKEY_SHININESS_STRENGTH); + } else if (linePart == "$shininess_exponent") { + ss >> linePart; + float Shininess = fast_atof(linePart.c_str()); + material->AddProperty(&Shininess, 1, AI_MATKEY_SHININESS); + } + //Properties from Venetica: + else if (linePart == "$diffuse_map") { + ss >> linePart; + if (linePart[0] == '"') // "file" -> file + linePart = linePart.substr(1, linePart.size() - 2); + aiString ts(linePart); + material->AddProperty(&ts, AI_MATKEY_TEXTURE(aiTextureType_DIFFUSE, 0)); + } else if (linePart == "$specular_map") { + ss >> linePart; + if (linePart[0] == '"') // "file" -> file + linePart = linePart.substr(1, linePart.size() - 2); + aiString ts(linePart); + material->AddProperty(&ts, AI_MATKEY_TEXTURE(aiTextureType_SHININESS, 0)); + } else if (linePart == "$normal_map") { + ss >> linePart; + if (linePart[0] == '"') // "file" -> file + linePart = linePart.substr(1, linePart.size() - 2); + aiString ts(linePart); + material->AddProperty(&ts, AI_MATKEY_TEXTURE(aiTextureType_NORMALS, 0)); + } else if (linePart == "$light_map") { + ss >> linePart; + if (linePart[0] == '"') { + linePart = linePart.substr(1, linePart.size() - 2); + } + aiString ts(linePart); + material->AddProperty(&ts, AI_MATKEY_TEXTURE(aiTextureType_LIGHTMAP, 0)); + } + } + } + ss >> linePart; + } + + return material; +} + +bool OgreImporter::ReadTechnique(const std::string &techniqueName, stringstream &ss, aiMaterial *material) { + string linePart; + ss >> linePart; + + if (linePart != partBlockStart) { + ASSIMP_LOG_ERROR("Invalid material: Technique block start missing near index ", ss.tellg()); + return false; + } + + ASSIMP_LOG_VERBOSE_DEBUG(" technique '", techniqueName, "'"); + + const string partPass = "pass"; + + while (linePart != partBlockEnd) { + ss >> linePart; + + // Skip commented lines + if (linePart == partComment) { + SkipLine(ss); + continue; + } + + /// @todo Techniques have other attributes than just passes. + if (linePart == partPass) { + string passName = SkipLine(ss); + ReadPass(ai_trim(passName), ss, material); + } + } + return true; +} + +bool OgreImporter::ReadPass(const std::string &passName, stringstream &ss, aiMaterial *material) { + string linePart; + ss >> linePart; + + if (linePart != partBlockStart) { + ASSIMP_LOG_ERROR("Invalid material: Pass block start missing near index ", ss.tellg()); + return false; + } + + ASSIMP_LOG_VERBOSE_DEBUG(" pass '", passName, "'"); + + const string partAmbient = "ambient"; + const string partDiffuse = "diffuse"; + const string partSpecular = "specular"; + const string partEmissive = "emissive"; + const string partTextureUnit = "texture_unit"; + + while (linePart != partBlockEnd) { + ss >> linePart; + + // Skip commented lines + if (linePart == partComment) { + SkipLine(ss); + continue; + } + + // Colors + /// @todo Support alpha via aiColor4D. + if (linePart == partAmbient || linePart == partDiffuse || linePart == partSpecular || linePart == partEmissive) { + float r, g, b; + ss >> r >> g >> b; + const aiColor3D color(r, g, b); + + ASSIMP_LOG_VERBOSE_DEBUG(" ", linePart, " ", r, " ", g, " ", b); + + if (linePart == partAmbient) { + material->AddProperty(&color, 1, AI_MATKEY_COLOR_AMBIENT); + } else if (linePart == partDiffuse) { + material->AddProperty(&color, 1, AI_MATKEY_COLOR_DIFFUSE); + } else if (linePart == partSpecular) { + material->AddProperty(&color, 1, AI_MATKEY_COLOR_SPECULAR); + } else if (linePart == partEmissive) { + material->AddProperty(&color, 1, AI_MATKEY_COLOR_EMISSIVE); + } + } else if (linePart == partTextureUnit) { + string textureUnitName = SkipLine(ss); + ReadTextureUnit(ai_trim(textureUnitName), ss, material); + } + } + return true; +} + +bool OgreImporter::ReadTextureUnit(const std::string &textureUnitName, stringstream &ss, aiMaterial *material) { + string linePart; + ss >> linePart; + + if (linePart != partBlockStart) { + ASSIMP_LOG_ERROR("Invalid material: Texture unit block start missing near index ", ss.tellg()); + return false; + } + + ASSIMP_LOG_VERBOSE_DEBUG(" texture_unit '", textureUnitName, "'"); + + const string partTexture = "texture"; + const string partTextCoordSet = "tex_coord_set"; + const string partColorOp = "colour_op"; + + aiTextureType textureType = aiTextureType_NONE; + std::string textureRef; + int uvCoord = 0; + + while (linePart != partBlockEnd) { + ss >> linePart; + + // Skip commented lines + if (linePart == partComment) { + SkipLine(ss); + continue; + } + + if (linePart == partTexture) { + ss >> linePart; + textureRef = linePart; + + // User defined Assimp config property to detect texture type from filename. + if (m_detectTextureTypeFromFilename) { + size_t posSuffix = textureRef.find_last_of('.'); + size_t posUnderscore = textureRef.find_last_of('_'); + + if (posSuffix != string::npos && posUnderscore != string::npos && posSuffix > posUnderscore) { + string identifier = ai_tolower(textureRef.substr(posUnderscore, posSuffix - posUnderscore)); + ASSIMP_LOG_VERBOSE_DEBUG("Detecting texture type from filename postfix '", identifier, "'"); + + if (identifier == "_n" || identifier == "_nrm" || identifier == "_nrml" || identifier == "_normal" || identifier == "_normals" || identifier == "_normalmap") { + textureType = aiTextureType_NORMALS; + } else if (identifier == "_s" || identifier == "_spec" || identifier == "_specular" || identifier == "_specularmap") { + textureType = aiTextureType_SPECULAR; + } else if (identifier == "_l" || identifier == "_light" || identifier == "_lightmap" || identifier == "_occ" || identifier == "_occlusion") { + textureType = aiTextureType_LIGHTMAP; + } else if (identifier == "_disp" || identifier == "_displacement") { + textureType = aiTextureType_DISPLACEMENT; + } else { + textureType = aiTextureType_DIFFUSE; + } + } else { + textureType = aiTextureType_DIFFUSE; + } + } + // Detect from texture unit name. This cannot be too broad as + // authors might give names like "LightSaber" or "NormalNinja". + else { + string unitNameLower = ai_tolower(textureUnitName); + if (unitNameLower.find("normalmap") != string::npos) { + textureType = aiTextureType_NORMALS; + } else if (unitNameLower.find("specularmap") != string::npos) { + textureType = aiTextureType_SPECULAR; + } else if (unitNameLower.find("lightmap") != string::npos) { + textureType = aiTextureType_LIGHTMAP; + } else if (unitNameLower.find("displacementmap") != string::npos) { + textureType = aiTextureType_DISPLACEMENT; + } else { + textureType = aiTextureType_DIFFUSE; + } + } + } else if (linePart == partTextCoordSet) { + ss >> uvCoord; + } + /// @todo Implement + else if (linePart == partColorOp) { + /* + ss >> linePart; + if("replace"==linePart)//I don't think, assimp has something for this... + { + } + else if("modulate"==linePart) + { + //TODO: set value + //material->AddProperty(aiTextureOp_Multiply) + } + */ + } + } + + if (textureRef.empty()) { + ASSIMP_LOG_WARN("Texture reference is empty, ignoring texture_unit."); + return false; + } + if (textureType == aiTextureType_NONE) { + ASSIMP_LOG_WARN("Failed to detect texture type for '", textureRef, "', ignoring texture_unit."); + return false; + } + + unsigned int textureTypeIndex = m_textures[textureType]; + m_textures[textureType]++; + + ASSIMP_LOG_VERBOSE_DEBUG(" texture '", textureRef, "' type ", textureType, + " index ", textureTypeIndex, " UV ", uvCoord); + + aiString assimpTextureRef(textureRef); + material->AddProperty(&assimpTextureRef, AI_MATKEY_TEXTURE(textureType, textureTypeIndex)); + material->AddProperty(&uvCoord, 1, AI_MATKEY_UVWSRC(textureType, textureTypeIndex)); + + return true; +} + +} // namespace Ogre +} // namespace Assimp + +#endif // ASSIMP_BUILD_NO_OGRE_IMPORTER diff --git a/libs/assimp/code/AssetLib/Ogre/OgreParsingUtils.h b/libs/assimp/code/AssetLib/Ogre/OgreParsingUtils.h new file mode 100644 index 0000000..60dd5cf --- /dev/null +++ b/libs/assimp/code/AssetLib/Ogre/OgreParsingUtils.h @@ -0,0 +1,95 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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. + +---------------------------------------------------------------------- +*/ + +#ifndef AI_OGREPARSINGUTILS_H_INC +#define AI_OGREPARSINGUTILS_H_INC + +#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER + +#include +#include +#include +#include +#include +#include + +namespace Assimp { +namespace Ogre { + + +/// Returns if @c s ends with @c suffix. If @c caseSensitive is false, both strings will be lower cased before matching. +static inline bool EndsWith(const std::string &s, const std::string &suffix, bool caseSensitive = true) { + if (s.empty() || suffix.empty()) { + return false; + } else if (s.length() < suffix.length()) { + return false; + } + + if (!caseSensitive) { + return EndsWith(ai_tolower(s), ai_tolower(suffix), true); + } + + size_t len = suffix.length(); + std::string sSuffix = s.substr(s.length() - len, len); + + return (ASSIMP_stricmp(sSuffix, suffix) == 0); +} + +// Skips a line from current @ss position until a newline. Returns the skipped part. +static inline std::string SkipLine(std::stringstream &ss) { + std::string skipped; + getline(ss, skipped); + return skipped; +} + +// Skips a line and reads next element from @c ss to @c nextElement. +/** @return Skipped line content until newline. */ +static inline std::string NextAfterNewLine(std::stringstream &ss, std::string &nextElement) { + std::string skipped = SkipLine(ss); + ss >> nextElement; + return skipped; +} + +} // namespace Ogre +} // namespace Assimp + +#endif // ASSIMP_BUILD_NO_OGRE_IMPORTER +#endif // AI_OGREPARSINGUTILS_H_INC diff --git a/libs/assimp/code/AssetLib/Ogre/OgreStructs.cpp b/libs/assimp/code/AssetLib/Ogre/OgreStructs.cpp new file mode 100644 index 0000000..d63dd93 --- /dev/null +++ b/libs/assimp/code/AssetLib/Ogre/OgreStructs.cpp @@ -0,0 +1,1076 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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. + +---------------------------------------------------------------------- +*/ + +#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER + +#include "OgreStructs.h" +#include +#include +#include +#include + +namespace Assimp { +namespace Ogre { + +// VertexElement + +VertexElement::VertexElement() : + index(0), + source(0), + offset(0), + type(VET_FLOAT1), + semantic(VES_POSITION) { +} + +size_t VertexElement::Size() const { + return TypeSize(type); +} + +size_t VertexElement::ComponentCount() const { + return ComponentCount(type); +} + +size_t VertexElement::ComponentCount(Type type) { + switch (type) { + case VET_COLOUR: + case VET_COLOUR_ABGR: + case VET_COLOUR_ARGB: + case VET_FLOAT1: + case VET_DOUBLE1: + case VET_SHORT1: + case VET_USHORT1: + case VET_INT1: + case VET_UINT1: + return 1; + case VET_FLOAT2: + case VET_DOUBLE2: + case VET_SHORT2: + case VET_USHORT2: + case VET_INT2: + case VET_UINT2: + return 2; + case VET_FLOAT3: + case VET_DOUBLE3: + case VET_SHORT3: + case VET_USHORT3: + case VET_INT3: + case VET_UINT3: + return 3; + case VET_FLOAT4: + case VET_DOUBLE4: + case VET_SHORT4: + case VET_USHORT4: + case VET_INT4: + case VET_UINT4: + case VET_UBYTE4: + return 4; + } + return 0; +} + +size_t VertexElement::TypeSize(Type type) { + switch (type) { + case VET_COLOUR: + case VET_COLOUR_ABGR: + case VET_COLOUR_ARGB: + return sizeof(unsigned int); + case VET_FLOAT1: + return sizeof(float); + case VET_FLOAT2: + return sizeof(float) * 2; + case VET_FLOAT3: + return sizeof(float) * 3; + case VET_FLOAT4: + return sizeof(float) * 4; + case VET_DOUBLE1: + return sizeof(double); + case VET_DOUBLE2: + return sizeof(double) * 2; + case VET_DOUBLE3: + return sizeof(double) * 3; + case VET_DOUBLE4: + return sizeof(double) * 4; + case VET_SHORT1: + return sizeof(short); + case VET_SHORT2: + return sizeof(short) * 2; + case VET_SHORT3: + return sizeof(short) * 3; + case VET_SHORT4: + return sizeof(short) * 4; + case VET_USHORT1: + return sizeof(unsigned short); + case VET_USHORT2: + return sizeof(unsigned short) * 2; + case VET_USHORT3: + return sizeof(unsigned short) * 3; + case VET_USHORT4: + return sizeof(unsigned short) * 4; + case VET_INT1: + return sizeof(int); + case VET_INT2: + return sizeof(int) * 2; + case VET_INT3: + return sizeof(int) * 3; + case VET_INT4: + return sizeof(int) * 4; + case VET_UINT1: + return sizeof(unsigned int); + case VET_UINT2: + return sizeof(unsigned int) * 2; + case VET_UINT3: + return sizeof(unsigned int) * 3; + case VET_UINT4: + return sizeof(unsigned int) * 4; + case VET_UBYTE4: + return sizeof(unsigned char) * 4; + } + return 0; +} + +std::string VertexElement::TypeToString() { + return TypeToString(type); +} + +std::string VertexElement::TypeToString(Type type) { + switch (type) { + case VET_COLOUR: return "COLOUR"; + case VET_COLOUR_ABGR: return "COLOUR_ABGR"; + case VET_COLOUR_ARGB: return "COLOUR_ARGB"; + case VET_FLOAT1: return "FLOAT1"; + case VET_FLOAT2: return "FLOAT2"; + case VET_FLOAT3: return "FLOAT3"; + case VET_FLOAT4: return "FLOAT4"; + case VET_DOUBLE1: return "DOUBLE1"; + case VET_DOUBLE2: return "DOUBLE2"; + case VET_DOUBLE3: return "DOUBLE3"; + case VET_DOUBLE4: return "DOUBLE4"; + case VET_SHORT1: return "SHORT1"; + case VET_SHORT2: return "SHORT2"; + case VET_SHORT3: return "SHORT3"; + case VET_SHORT4: return "SHORT4"; + case VET_USHORT1: return "USHORT1"; + case VET_USHORT2: return "USHORT2"; + case VET_USHORT3: return "USHORT3"; + case VET_USHORT4: return "USHORT4"; + case VET_INT1: return "INT1"; + case VET_INT2: return "INT2"; + case VET_INT3: return "INT3"; + case VET_INT4: return "INT4"; + case VET_UINT1: return "UINT1"; + case VET_UINT2: return "UINT2"; + case VET_UINT3: return "UINT3"; + case VET_UINT4: return "UINT4"; + case VET_UBYTE4: return "UBYTE4"; + } + return "Uknown_VertexElement::Type"; +} + +std::string VertexElement::SemanticToString() { + return SemanticToString(semantic); +} + +std::string VertexElement::SemanticToString(Semantic semantic) { + switch (semantic) { + case VES_POSITION: return "POSITION"; + case VES_BLEND_WEIGHTS: return "BLEND_WEIGHTS"; + case VES_BLEND_INDICES: return "BLEND_INDICES"; + case VES_NORMAL: return "NORMAL"; + case VES_DIFFUSE: return "DIFFUSE"; + case VES_SPECULAR: return "SPECULAR"; + case VES_TEXTURE_COORDINATES: return "TEXTURE_COORDINATES"; + case VES_BINORMAL: return "BINORMAL"; + case VES_TANGENT: return "TANGENT"; + } + return "Uknown_VertexElement::Semantic"; +} + +// IVertexData + +IVertexData::IVertexData() : + count(0) { +} + +bool IVertexData::HasBoneAssignments() const { + return !boneAssignments.empty(); +} + +void IVertexData::AddVertexMapping(uint32_t oldIndex, uint32_t newIndex) { + BoneAssignmentsForVertex(oldIndex, newIndex, boneAssignmentsMap[newIndex]); + vertexIndexMapping[oldIndex].push_back(newIndex); +} + +void IVertexData::BoneAssignmentsForVertex(uint32_t currentIndex, uint32_t newIndex, VertexBoneAssignmentList &dest) const { + for (const auto &boneAssign : boneAssignments) { + if (boneAssign.vertexIndex == currentIndex) { + VertexBoneAssignment a = boneAssign; + a.vertexIndex = newIndex; + dest.push_back(a); + } + } +} + +AssimpVertexBoneWeightList IVertexData::AssimpBoneWeights(size_t vertices) { + AssimpVertexBoneWeightList weights; + for (size_t vi = 0; vi < vertices; ++vi) { + VertexBoneAssignmentList &vertexWeights = boneAssignmentsMap[static_cast(vi)]; + for (VertexBoneAssignmentList::const_iterator iter = vertexWeights.begin(), end = vertexWeights.end(); + iter != end; ++iter) { + std::vector &boneWeights = weights[iter->boneIndex]; + boneWeights.push_back(aiVertexWeight(static_cast(vi), iter->weight)); + } + } + return weights; +} + +std::set IVertexData::ReferencedBonesByWeights() const { + std::set referenced; + for (const auto &boneAssign : boneAssignments) { + referenced.insert(boneAssign.boneIndex); + } + return referenced; +} + +// VertexData + +VertexData::VertexData() { +} + +VertexData::~VertexData() { + Reset(); +} + +void VertexData::Reset() { + // Releases shared ptr memory streams. + vertexBindings.clear(); + vertexElements.clear(); +} + +uint32_t VertexData::VertexSize(uint16_t source) const { + uint32_t size = 0; + for (const auto &element : vertexElements) { + if (element.source == source) + size += static_cast(element.Size()); + } + return size; +} + +MemoryStream *VertexData::VertexBuffer(uint16_t source) { + if (vertexBindings.find(source) != vertexBindings.end()) + return vertexBindings[source].get(); + return 0; +} + +VertexElement *VertexData::GetVertexElement(VertexElement::Semantic semantic, uint16_t index) { + for (auto &element : vertexElements) { + if (element.semantic == semantic && element.index == index) + return &element; + } + return 0; +} + +// VertexDataXml + +VertexDataXml::VertexDataXml() { +} + +bool VertexDataXml::HasPositions() const { + return !positions.empty(); +} + +bool VertexDataXml::HasNormals() const { + return !normals.empty(); +} + +bool VertexDataXml::HasTangents() const { + return !tangents.empty(); +} + +bool VertexDataXml::HasUvs() const { + return !uvs.empty(); +} + +size_t VertexDataXml::NumUvs() const { + return uvs.size(); +} + +// IndexData + +IndexData::IndexData() : + count(0), + faceCount(0), + is32bit(false) { +} + +IndexData::~IndexData() { + Reset(); +} + +void IndexData::Reset() { + // Release shared ptr memory stream. + buffer.reset(); +} + +size_t IndexData::IndexSize() const { + return (is32bit ? sizeof(uint32_t) : sizeof(uint16_t)); +} + +size_t IndexData::FaceSize() const { + return IndexSize() * 3; +} + +// Mesh + +Mesh::Mesh() : + hasSkeletalAnimations(false), + skeleton(nullptr), + sharedVertexData(nullptr), + subMeshes(), + animations(), + poses() { +} + +Mesh::~Mesh() { + Reset(); +} + +void Mesh::Reset() { + OGRE_SAFE_DELETE(skeleton) + OGRE_SAFE_DELETE(sharedVertexData) + + for (auto &mesh : subMeshes) { + OGRE_SAFE_DELETE(mesh) + } + subMeshes.clear(); + for (auto &anim : animations) { + OGRE_SAFE_DELETE(anim) + } + animations.clear(); + for (auto &pose : poses) { + OGRE_SAFE_DELETE(pose) + } + poses.clear(); +} + +size_t Mesh::NumSubMeshes() const { + return subMeshes.size(); +} + +SubMesh *Mesh::GetSubMesh(size_t index) const { + for (size_t i = 0; i < subMeshes.size(); ++i) { + if (subMeshes[i]->index == index) { + return subMeshes[i]; + } + } + return 0; +} + +void Mesh::ConvertToAssimpScene(aiScene *dest) { + if (nullptr == dest) { + return; + } + + // Setup + dest->mNumMeshes = static_cast(NumSubMeshes()); + dest->mMeshes = new aiMesh *[dest->mNumMeshes]; + + // Create root node + dest->mRootNode = new aiNode(); + dest->mRootNode->mNumMeshes = dest->mNumMeshes; + dest->mRootNode->mMeshes = new unsigned int[dest->mRootNode->mNumMeshes]; + + // Export meshes + for (size_t i = 0; i < dest->mNumMeshes; ++i) { + dest->mMeshes[i] = subMeshes[i]->ConvertToAssimpMesh(this); + dest->mRootNode->mMeshes[i] = static_cast(i); + } + + // Export skeleton + if (skeleton) { + // Bones + if (!skeleton->bones.empty()) { + BoneList rootBones = skeleton->RootBones(); + dest->mRootNode->mNumChildren = static_cast(rootBones.size()); + dest->mRootNode->mChildren = new aiNode *[dest->mRootNode->mNumChildren]; + + for (size_t i = 0, len = rootBones.size(); i < len; ++i) { + dest->mRootNode->mChildren[i] = rootBones[i]->ConvertToAssimpNode(skeleton, dest->mRootNode); + } + } + + // Animations + if (!skeleton->animations.empty()) { + dest->mNumAnimations = static_cast(skeleton->animations.size()); + dest->mAnimations = new aiAnimation *[dest->mNumAnimations]; + + for (size_t i = 0, len = skeleton->animations.size(); i < len; ++i) { + dest->mAnimations[i] = skeleton->animations[i]->ConvertToAssimpAnimation(); + } + } + } +} + +// ISubMesh + +ISubMesh::ISubMesh() : + index(0), + materialIndex(-1), + usesSharedVertexData(false), + operationType(OT_POINT_LIST) { +} + +// SubMesh + +SubMesh::SubMesh() : + vertexData(0), + indexData(new IndexData()) { +} + +SubMesh::~SubMesh() { + Reset(); +} + +void SubMesh::Reset(){ + OGRE_SAFE_DELETE(vertexData) + OGRE_SAFE_DELETE(indexData) +} + +aiMesh *SubMesh::ConvertToAssimpMesh(Mesh *parent) { + if (operationType != OT_TRIANGLE_LIST) { + throw DeadlyImportError("Only mesh operation type OT_TRIANGLE_LIST is supported. Found ", operationType); + } + + aiMesh *dest = new aiMesh(); + dest->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; + + if (!name.empty()) + dest->mName = name; + + // Material index + if (materialIndex != -1) + dest->mMaterialIndex = materialIndex; + + // Pick source vertex data from shader geometry or from internal geometry. + VertexData *src = (!usesSharedVertexData ? vertexData : parent->sharedVertexData); + + VertexElement *positionsElement = src->GetVertexElement(VertexElement::VES_POSITION); + VertexElement *normalsElement = src->GetVertexElement(VertexElement::VES_NORMAL); + VertexElement *uv1Element = src->GetVertexElement(VertexElement::VES_TEXTURE_COORDINATES, 0); + VertexElement *uv2Element = src->GetVertexElement(VertexElement::VES_TEXTURE_COORDINATES, 1); + + // Sanity checks + if (!positionsElement) { + throw DeadlyImportError("Failed to import Ogre VertexElement::VES_POSITION. Mesh does not have vertex positions!"); + } else if (positionsElement->type != VertexElement::VET_FLOAT3) { + throw DeadlyImportError("Ogre Mesh position vertex element type != VertexElement::VET_FLOAT3. This is not supported."); + } else if (normalsElement && normalsElement->type != VertexElement::VET_FLOAT3) { + throw DeadlyImportError("Ogre Mesh normal vertex element type != VertexElement::VET_FLOAT3. This is not supported."); + } + + // Faces + dest->mNumFaces = indexData->faceCount; + dest->mFaces = new aiFace[dest->mNumFaces]; + + // Assimp required unique vertices, we need to convert from Ogres shared indexing. + size_t uniqueVertexCount = dest->mNumFaces * 3; + dest->mNumVertices = static_cast(uniqueVertexCount); + dest->mVertices = new aiVector3D[dest->mNumVertices]; + + // Source streams + MemoryStream *positions = src->VertexBuffer(positionsElement->source); + MemoryStream *normals = (normalsElement ? src->VertexBuffer(normalsElement->source) : 0); + MemoryStream *uv1 = (uv1Element ? src->VertexBuffer(uv1Element->source) : 0); + MemoryStream *uv2 = (uv2Element ? src->VertexBuffer(uv2Element->source) : 0); + + // Element size + const size_t sizePosition = positionsElement->Size(); + const size_t sizeNormal = (normalsElement ? normalsElement->Size() : 0); + const size_t sizeUv1 = (uv1Element ? uv1Element->Size() : 0); + const size_t sizeUv2 = (uv2Element ? uv2Element->Size() : 0); + + // Vertex width + const size_t vWidthPosition = src->VertexSize(positionsElement->source); + const size_t vWidthNormal = (normalsElement ? src->VertexSize(normalsElement->source) : 0); + const size_t vWidthUv1 = (uv1Element ? src->VertexSize(uv1Element->source) : 0); + const size_t vWidthUv2 = (uv2Element ? src->VertexSize(uv2Element->source) : 0); + + bool boneAssignments = src->HasBoneAssignments(); + + // Prepare normals + if (normals) + dest->mNormals = new aiVector3D[dest->mNumVertices]; + + // Prepare UVs, ignoring incompatible UVs. + if (uv1) { + if (uv1Element->type == VertexElement::VET_FLOAT2 || uv1Element->type == VertexElement::VET_FLOAT3) { + dest->mNumUVComponents[0] = static_cast(uv1Element->ComponentCount()); + dest->mTextureCoords[0] = new aiVector3D[dest->mNumVertices]; + } else { + ASSIMP_LOG_WARN("Ogre imported UV0 type ", uv1Element->TypeToString(), " is not compatible with Assimp. Ignoring UV."); + uv1 = 0; + } + } + if (uv2) { + if (uv2Element->type == VertexElement::VET_FLOAT2 || uv2Element->type == VertexElement::VET_FLOAT3) { + dest->mNumUVComponents[1] = static_cast(uv2Element->ComponentCount()); + dest->mTextureCoords[1] = new aiVector3D[dest->mNumVertices]; + } else { + ASSIMP_LOG_WARN("Ogre imported UV0 type ", uv2Element->TypeToString(), " is not compatible with Assimp. Ignoring UV."); + uv2 = 0; + } + } + + aiVector3D *uv1Dest = (uv1 ? dest->mTextureCoords[0] : 0); + aiVector3D *uv2Dest = (uv2 ? dest->mTextureCoords[1] : 0); + + MemoryStream *faces = indexData->buffer.get(); + for (size_t fi = 0, isize = indexData->IndexSize(), fsize = indexData->FaceSize(); + fi < dest->mNumFaces; ++fi) { + // Source Ogre face + aiFace ogreFace; + ogreFace.mNumIndices = 3; + ogreFace.mIndices = new unsigned int[3]; + + faces->Seek(fi * fsize, aiOrigin_SET); + if (indexData->is32bit) { + faces->Read(&ogreFace.mIndices[0], isize, 3); + } else { + uint16_t iout = 0; + for (size_t ii = 0; ii < 3; ++ii) { + faces->Read(&iout, isize, 1); + ogreFace.mIndices[ii] = static_cast(iout); + } + } + + // Destination Assimp face + aiFace &face = dest->mFaces[fi]; + face.mNumIndices = 3; + face.mIndices = new unsigned int[3]; + + const size_t pos = fi * 3; + for (size_t v = 0; v < 3; ++v) { + const size_t newIndex = pos + v; + + // Write face index + face.mIndices[v] = static_cast(newIndex); + + // Ogres vertex index to ref into the source buffers. + const size_t ogreVertexIndex = ogreFace.mIndices[v]; + src->AddVertexMapping(static_cast(ogreVertexIndex), static_cast(newIndex)); + + // Position + positions->Seek((vWidthPosition * ogreVertexIndex) + positionsElement->offset, aiOrigin_SET); + positions->Read(&dest->mVertices[newIndex], sizePosition, 1); + + // Normal + if (normals) { + normals->Seek((vWidthNormal * ogreVertexIndex) + normalsElement->offset, aiOrigin_SET); + normals->Read(&dest->mNormals[newIndex], sizeNormal, 1); + } + // UV0 + if (uv1 && uv1Dest) { + uv1->Seek((vWidthUv1 * ogreVertexIndex) + uv1Element->offset, aiOrigin_SET); + uv1->Read(&uv1Dest[newIndex], sizeUv1, 1); + uv1Dest[newIndex].y = (uv1Dest[newIndex].y * -1) + 1; // Flip UV from Ogre to Assimp form + } + // UV1 + if (uv2 && uv2Dest) { + uv2->Seek((vWidthUv2 * ogreVertexIndex) + uv2Element->offset, aiOrigin_SET); + uv2->Read(&uv2Dest[newIndex], sizeUv2, 1); + uv2Dest[newIndex].y = (uv2Dest[newIndex].y * -1) + 1; // Flip UV from Ogre to Assimp form + } + } + } + + // Bones and bone weights + if (parent->skeleton && boneAssignments) { + AssimpVertexBoneWeightList weights = src->AssimpBoneWeights(dest->mNumVertices); + std::set referencedBones = src->ReferencedBonesByWeights(); + + dest->mNumBones = static_cast(referencedBones.size()); + dest->mBones = new aiBone *[dest->mNumBones]; + + size_t assimpBoneIndex = 0; + for (std::set::const_iterator rbIter = referencedBones.begin(), rbEnd = referencedBones.end(); rbIter != rbEnd; ++rbIter, ++assimpBoneIndex) { + Bone *bone = parent->skeleton->BoneById((*rbIter)); + dest->mBones[assimpBoneIndex] = bone->ConvertToAssimpBone(parent->skeleton, weights[bone->id]); + } + } + + return dest; +} + +// MeshXml + +MeshXml::MeshXml() : + skeleton(0), + sharedVertexData(0) { +} + +MeshXml::~MeshXml() { + Reset(); +} + +void MeshXml::Reset() { + OGRE_SAFE_DELETE(skeleton) + OGRE_SAFE_DELETE(sharedVertexData) + + for (auto &mesh : subMeshes) { + OGRE_SAFE_DELETE(mesh) + } + subMeshes.clear(); +} + +size_t MeshXml::NumSubMeshes() const { + return subMeshes.size(); +} + +SubMeshXml *MeshXml::GetSubMesh(uint16_t index) const { + for (size_t i = 0; i < subMeshes.size(); ++i) + if (subMeshes[i]->index == index) + return subMeshes[i]; + return 0; +} + +void MeshXml::ConvertToAssimpScene(aiScene *dest) { + // Setup + dest->mNumMeshes = static_cast(NumSubMeshes()); + dest->mMeshes = new aiMesh *[dest->mNumMeshes]; + + // Create root node + dest->mRootNode = new aiNode(); + dest->mRootNode->mNumMeshes = dest->mNumMeshes; + dest->mRootNode->mMeshes = new unsigned int[dest->mRootNode->mNumMeshes]; + + // Export meshes + for (size_t i = 0; i < dest->mNumMeshes; ++i) { + dest->mMeshes[i] = subMeshes[i]->ConvertToAssimpMesh(this); + dest->mRootNode->mMeshes[i] = static_cast(i); + } + + // Export skeleton + if (skeleton) { + // Bones + if (!skeleton->bones.empty()) { + BoneList rootBones = skeleton->RootBones(); + dest->mRootNode->mNumChildren = static_cast(rootBones.size()); + dest->mRootNode->mChildren = new aiNode *[dest->mRootNode->mNumChildren]; + + for (size_t i = 0, len = rootBones.size(); i < len; ++i) { + dest->mRootNode->mChildren[i] = rootBones[i]->ConvertToAssimpNode(skeleton, dest->mRootNode); + } + } + + // Animations + if (!skeleton->animations.empty()) { + dest->mNumAnimations = static_cast(skeleton->animations.size()); + dest->mAnimations = new aiAnimation *[dest->mNumAnimations]; + + for (size_t i = 0, len = skeleton->animations.size(); i < len; ++i) { + dest->mAnimations[i] = skeleton->animations[i]->ConvertToAssimpAnimation(); + } + } + } +} + +// SubMeshXml + +SubMeshXml::SubMeshXml() : + indexData(new IndexDataXml()), + vertexData(0) { +} + +SubMeshXml::~SubMeshXml() { + Reset(); +} + +void SubMeshXml::Reset(){ + OGRE_SAFE_DELETE(indexData) + OGRE_SAFE_DELETE(vertexData) +} + +aiMesh *SubMeshXml::ConvertToAssimpMesh(MeshXml *parent) { + aiMesh *dest = new aiMesh(); + dest->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; + + if (!name.empty()) + dest->mName = name; + + // Material index + if (materialIndex != -1) + dest->mMaterialIndex = materialIndex; + + // Faces + dest->mNumFaces = indexData->faceCount; + dest->mFaces = new aiFace[dest->mNumFaces]; + + // Assimp required unique vertices, we need to convert from Ogres shared indexing. + size_t uniqueVertexCount = dest->mNumFaces * 3; + dest->mNumVertices = static_cast(uniqueVertexCount); + dest->mVertices = new aiVector3D[dest->mNumVertices]; + + VertexDataXml *src = (!usesSharedVertexData ? vertexData : parent->sharedVertexData); + bool boneAssignments = src->HasBoneAssignments(); + bool normals = src->HasNormals(); + size_t uvs = src->NumUvs(); + + // Prepare normals + if (normals) + dest->mNormals = new aiVector3D[dest->mNumVertices]; + + // Prepare UVs + for (size_t uvi = 0; uvi < uvs; ++uvi) { + dest->mNumUVComponents[uvi] = 2; + dest->mTextureCoords[uvi] = new aiVector3D[dest->mNumVertices]; + } + + for (size_t fi = 0; fi < dest->mNumFaces; ++fi) { + // Source Ogre face + aiFace &ogreFace = indexData->faces[fi]; + + // Destination Assimp face + aiFace &face = dest->mFaces[fi]; + face.mNumIndices = 3; + face.mIndices = new unsigned int[3]; + + const size_t pos = fi * 3; + for (size_t v = 0; v < 3; ++v) { + const size_t newIndex = pos + v; + + // Write face index + face.mIndices[v] = static_cast(newIndex); + + // Ogres vertex index to ref into the source buffers. + const size_t ogreVertexIndex = ogreFace.mIndices[v]; + src->AddVertexMapping(static_cast(ogreVertexIndex), static_cast(newIndex)); + + // Position + dest->mVertices[newIndex] = src->positions[ogreVertexIndex]; + + // Normal + if (normals) + dest->mNormals[newIndex] = src->normals[ogreVertexIndex]; + + // UVs + for (size_t uvi = 0; uvi < uvs; ++uvi) { + aiVector3D *uvDest = dest->mTextureCoords[uvi]; + std::vector &uvSrc = src->uvs[uvi]; + uvDest[newIndex] = uvSrc[ogreVertexIndex]; + } + } + } + + // Bones and bone weights + if (parent->skeleton && boneAssignments) { + AssimpVertexBoneWeightList weights = src->AssimpBoneWeights(dest->mNumVertices); + std::set referencedBones = src->ReferencedBonesByWeights(); + + dest->mNumBones = static_cast(referencedBones.size()); + dest->mBones = new aiBone *[dest->mNumBones]; + + size_t assimpBoneIndex = 0; + for (std::set::const_iterator rbIter = referencedBones.begin(), rbEnd = referencedBones.end(); rbIter != rbEnd; ++rbIter, ++assimpBoneIndex) { + Bone *bone = parent->skeleton->BoneById((*rbIter)); + dest->mBones[assimpBoneIndex] = bone->ConvertToAssimpBone(parent->skeleton, weights[bone->id]); + } + } + + return dest; +} + +// Animation + +Animation::Animation(Skeleton *parent) : + parentMesh(nullptr), + parentSkeleton(parent), + length(0.0f), + baseTime(-1.0f) { + // empty +} + +Animation::Animation(Mesh *parent) : + parentMesh(parent), + parentSkeleton(0), + length(0.0f), + baseTime(-1.0f) { + // empty +} + +VertexData *Animation::AssociatedVertexData(VertexAnimationTrack *track) const { + if (nullptr == parentMesh) { + return nullptr; + } + + bool sharedGeom = (track->target == 0); + if (sharedGeom) { + return parentMesh->sharedVertexData; + } + + return parentMesh->GetSubMesh(track->target - 1)->vertexData; +} + +aiAnimation *Animation::ConvertToAssimpAnimation() { + aiAnimation *anim = new aiAnimation(); + anim->mName = name; + anim->mDuration = static_cast(length); + anim->mTicksPerSecond = 1.0; + + // Tracks + if (!tracks.empty()) { + anim->mNumChannels = static_cast(tracks.size()); + anim->mChannels = new aiNodeAnim *[anim->mNumChannels]; + + for (size_t i = 0, len = tracks.size(); i < len; ++i) { + anim->mChannels[i] = tracks[i].ConvertToAssimpAnimationNode(parentSkeleton); + } + } + return anim; +} + +// Skeleton + +Skeleton::Skeleton() : + bones(), + animations(), + blendMode(ANIMBLEND_AVERAGE) { +} + +Skeleton::~Skeleton() { + Reset(); +} + +void Skeleton::Reset() { + for (auto &bone : bones) { + OGRE_SAFE_DELETE(bone) + } + bones.clear(); + for (auto &anim : animations) { + OGRE_SAFE_DELETE(anim) + } + animations.clear(); +} + +BoneList Skeleton::RootBones() const { + BoneList rootBones; + for (BoneList::const_iterator iter = bones.begin(); iter != bones.end(); ++iter) { + if (!(*iter)->IsParented()) + rootBones.push_back((*iter)); + } + return rootBones; +} + +size_t Skeleton::NumRootBones() const { + size_t num = 0; + for (BoneList::const_iterator iter = bones.begin(); iter != bones.end(); ++iter) { + if (!(*iter)->IsParented()) + num++; + } + return num; +} + +Bone *Skeleton::BoneByName(const std::string &name) const { + for (BoneList::const_iterator iter = bones.begin(); iter != bones.end(); ++iter) { + if ((*iter)->name == name) + return (*iter); + } + return 0; +} + +Bone *Skeleton::BoneById(uint16_t id) const { + for (BoneList::const_iterator iter = bones.begin(); iter != bones.end(); ++iter) { + if ((*iter)->id == id) + return (*iter); + } + return 0; +} + +// Bone + +Bone::Bone() : + id(0), + parent(0), + parentId(-1), + scale(1.0f, 1.0f, 1.0f) { +} + +bool Bone::IsParented() const { + return (parentId != -1 && parent != 0); +} + +uint16_t Bone::ParentId() const { + return static_cast(parentId); +} + +void Bone::AddChild(Bone *bone) { + if (!bone) + return; + if (bone->IsParented()) + throw DeadlyImportError("Attaching child Bone that is already parented: ", bone->name); + + bone->parent = this; + bone->parentId = id; + children.push_back(bone->id); +} + +void Bone::CalculateWorldMatrixAndDefaultPose(Skeleton *skeleton) { + if (!IsParented()) + worldMatrix = aiMatrix4x4(scale, rotation, position).Inverse(); + else + worldMatrix = aiMatrix4x4(scale, rotation, position).Inverse() * parent->worldMatrix; + + defaultPose = aiMatrix4x4(scale, rotation, position); + + // Recursively for all children now that the parent matrix has been calculated. + for (auto boneId : children) { + Bone *child = skeleton->BoneById(boneId); + if (!child) { + throw DeadlyImportError("CalculateWorldMatrixAndDefaultPose: Failed to find child bone ", boneId, " for parent ", id, " ", name); + } + child->CalculateWorldMatrixAndDefaultPose(skeleton); + } +} + +aiNode *Bone::ConvertToAssimpNode(Skeleton *skeleton, aiNode *parentNode) { + // Bone node + aiNode *node = new aiNode(name); + node->mParent = parentNode; + node->mTransformation = defaultPose; + + // Children + if (!children.empty()) { + node->mNumChildren = static_cast(children.size()); + node->mChildren = new aiNode *[node->mNumChildren]; + + for (size_t i = 0, len = children.size(); i < len; ++i) { + Bone *child = skeleton->BoneById(children[i]); + if (!child) { + throw DeadlyImportError("ConvertToAssimpNode: Failed to find child bone ", children[i], " for parent ", id, " ", name); + } + node->mChildren[i] = child->ConvertToAssimpNode(skeleton, node); + } + } + return node; +} + +aiBone *Bone::ConvertToAssimpBone(Skeleton * /*parent*/, const std::vector &boneWeights) { + aiBone *bone = new aiBone(); + bone->mName = name; + bone->mOffsetMatrix = worldMatrix; + + if (!boneWeights.empty()) { + bone->mNumWeights = static_cast(boneWeights.size()); + bone->mWeights = new aiVertexWeight[boneWeights.size()]; + memcpy(bone->mWeights, &boneWeights[0], boneWeights.size() * sizeof(aiVertexWeight)); + } + + return bone; +} + +// VertexAnimationTrack + +VertexAnimationTrack::VertexAnimationTrack() : + type(VAT_NONE), + target(0) { +} + +aiNodeAnim *VertexAnimationTrack::ConvertToAssimpAnimationNode(Skeleton *skeleton) { + if (boneName.empty() || type != VAT_TRANSFORM) { + throw DeadlyImportError("VertexAnimationTrack::ConvertToAssimpAnimationNode: Cannot convert track that has no target bone name or is not type of VAT_TRANSFORM"); + } + + aiNodeAnim *nodeAnim = new aiNodeAnim(); + nodeAnim->mNodeName = boneName; + + Bone *bone = skeleton->BoneByName(boneName); + if (!bone) { + throw DeadlyImportError("VertexAnimationTrack::ConvertToAssimpAnimationNode: Failed to find bone ", boneName, " from parent Skeleton"); + } + + // Keyframes + size_t numKeyframes = transformKeyFrames.size(); + + nodeAnim->mPositionKeys = new aiVectorKey[numKeyframes]; + nodeAnim->mRotationKeys = new aiQuatKey[numKeyframes]; + nodeAnim->mScalingKeys = new aiVectorKey[numKeyframes]; + nodeAnim->mNumPositionKeys = static_cast(numKeyframes); + nodeAnim->mNumRotationKeys = static_cast(numKeyframes); + nodeAnim->mNumScalingKeys = static_cast(numKeyframes); + + for (size_t kfi = 0; kfi < numKeyframes; ++kfi) { + TransformKeyFrame &kfSource = transformKeyFrames[kfi]; + + // Calculate the complete transformation from world space to bone space + aiVector3D pos; + aiQuaternion rot; + aiVector3D scale; + + aiMatrix4x4 finalTransform = bone->defaultPose * kfSource.Transform(); + finalTransform.Decompose(scale, rot, pos); + + double t = static_cast(kfSource.timePos); + nodeAnim->mPositionKeys[kfi].mTime = t; + nodeAnim->mRotationKeys[kfi].mTime = t; + nodeAnim->mScalingKeys[kfi].mTime = t; + + nodeAnim->mPositionKeys[kfi].mValue = pos; + nodeAnim->mRotationKeys[kfi].mValue = rot; + nodeAnim->mScalingKeys[kfi].mValue = scale; + } + + return nodeAnim; +} + +// TransformKeyFrame + +TransformKeyFrame::TransformKeyFrame() : + timePos(0.0f), + scale(1.0f, 1.0f, 1.0f) { +} + +aiMatrix4x4 TransformKeyFrame::Transform() { + return aiMatrix4x4(scale, rotation, position); +} + +} // namespace Ogre +} // namespace Assimp + +#endif // ASSIMP_BUILD_NO_OGRE_IMPORTER diff --git a/libs/assimp/code/AssetLib/Ogre/OgreStructs.h b/libs/assimp/code/AssetLib/Ogre/OgreStructs.h new file mode 100644 index 0000000..be9ffa0 --- /dev/null +++ b/libs/assimp/code/AssetLib/Ogre/OgreStructs.h @@ -0,0 +1,696 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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. + +---------------------------------------------------------------------- +*/ + +#ifndef AI_OGRESTRUCTS_H_INC +#define AI_OGRESTRUCTS_H_INC + +#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER + +#include +#include +#include +#include +#include +#include + +struct aiNodeAnim; +struct aiAnimation; +struct aiNode; +struct aiMaterial; +struct aiScene; + +/** @note Parts of this implementation, for example enums, deserialization constants and logic + has been copied directly with minor modifications from the MIT licensed Ogre3D code base. + See more from https://bitbucket.org/sinbad/ogre. */ + +namespace Assimp +{ +namespace Ogre +{ + +// Forward decl +class Mesh; +class MeshXml; +class SubMesh; +class SubMeshXml; +class Skeleton; + +#define OGRE_SAFE_DELETE(p) delete p; p=0; + +// Typedefs +typedef Assimp::MemoryIOStream MemoryStream; +typedef std::shared_ptr MemoryStreamPtr; +typedef std::map VertexBufferBindings; + +// Ogre Vertex Element +class VertexElement +{ +public: + /// Vertex element semantics, used to identify the meaning of vertex buffer contents + enum Semantic { + /// Position, 3 reals per vertex + VES_POSITION = 1, + /// Blending weights + VES_BLEND_WEIGHTS = 2, + /// Blending indices + VES_BLEND_INDICES = 3, + /// Normal, 3 reals per vertex + VES_NORMAL = 4, + /// Diffuse colours + VES_DIFFUSE = 5, + /// Specular colours + VES_SPECULAR = 6, + /// Texture coordinates + VES_TEXTURE_COORDINATES = 7, + /// Binormal (Y axis if normal is Z) + VES_BINORMAL = 8, + /// Tangent (X axis if normal is Z) + VES_TANGENT = 9, + /// The number of VertexElementSemantic elements (note - the first value VES_POSITION is 1) + VES_COUNT = 9 + }; + + /// Vertex element type, used to identify the base types of the vertex contents + enum Type + { + VET_FLOAT1 = 0, + VET_FLOAT2 = 1, + VET_FLOAT3 = 2, + VET_FLOAT4 = 3, + /// alias to more specific colour type - use the current rendersystem's colour packing + VET_COLOUR = 4, + VET_SHORT1 = 5, + VET_SHORT2 = 6, + VET_SHORT3 = 7, + VET_SHORT4 = 8, + VET_UBYTE4 = 9, + /// D3D style compact colour + VET_COLOUR_ARGB = 10, + /// GL style compact colour + VET_COLOUR_ABGR = 11, + VET_DOUBLE1 = 12, + VET_DOUBLE2 = 13, + VET_DOUBLE3 = 14, + VET_DOUBLE4 = 15, + VET_USHORT1 = 16, + VET_USHORT2 = 17, + VET_USHORT3 = 18, + VET_USHORT4 = 19, + VET_INT1 = 20, + VET_INT2 = 21, + VET_INT3 = 22, + VET_INT4 = 23, + VET_UINT1 = 24, + VET_UINT2 = 25, + VET_UINT3 = 26, + VET_UINT4 = 27 + }; + + VertexElement(); + + /// Size of the vertex element in bytes. + size_t Size() const; + + /// Count of components in this element, eg. VET_FLOAT3 return 3. + size_t ComponentCount() const; + + /// Type as string. + std::string TypeToString(); + + /// Semantic as string. + std::string SemanticToString(); + + static size_t TypeSize(Type type); + static size_t ComponentCount(Type type); + static std::string TypeToString(Type type); + static std::string SemanticToString(Semantic semantic); + + uint16_t index; + uint16_t source; + uint16_t offset; + Type type; + Semantic semantic; +}; +typedef std::vector VertexElementList; + +/// Ogre Vertex Bone Assignment +struct VertexBoneAssignment +{ + uint32_t vertexIndex; + uint16_t boneIndex; + float weight; +}; +typedef std::vector VertexBoneAssignmentList; +typedef std::map VertexBoneAssignmentsMap; +typedef std::map > AssimpVertexBoneWeightList; + +// Ogre Vertex Data interface, inherited by the binary and XML implementations. +class IVertexData +{ +public: + IVertexData(); + + /// Returns if bone assignments are available. + bool HasBoneAssignments() const; + + /// Add vertex mapping from old to new index. + void AddVertexMapping(uint32_t oldIndex, uint32_t newIndex); + + /// Returns re-mapped bone assignments. + /** @note Uses mappings added via AddVertexMapping. */ + AssimpVertexBoneWeightList AssimpBoneWeights(size_t vertices); + + /// Returns a set of bone indexes that are referenced by bone assignments (weights). + std::set ReferencedBonesByWeights() const; + + /// Vertex count. + uint32_t count; + + /// Bone assignments. + VertexBoneAssignmentList boneAssignments; + +private: + void BoneAssignmentsForVertex(uint32_t currentIndex, uint32_t newIndex, VertexBoneAssignmentList &dest) const; + + std::map > vertexIndexMapping; + VertexBoneAssignmentsMap boneAssignmentsMap; +}; + +// Ogre Vertex Data +class VertexData : public IVertexData +{ +public: + VertexData(); + ~VertexData(); + + /// Releases all memory that this data structure owns. + void Reset(); + + /// Get vertex size for @c source. + uint32_t VertexSize(uint16_t source) const; + + /// Get vertex buffer for @c source. + MemoryStream *VertexBuffer(uint16_t source); + + /// Get vertex element for @c semantic for @c index. + VertexElement *GetVertexElement(VertexElement::Semantic semantic, uint16_t index = 0); + + /// Vertex elements. + VertexElementList vertexElements; + + /// Vertex buffers mapped to bind index. + VertexBufferBindings vertexBindings; +}; + +// Ogre Index Data +class IndexData +{ +public: + IndexData(); + ~IndexData(); + + /// Releases all memory that this data structure owns. + void Reset(); + + /// Index size in bytes. + size_t IndexSize() const; + + /// Face size in bytes. + size_t FaceSize() const; + + /// Index count. + uint32_t count; + + /// Face count. + uint32_t faceCount; + + /// If has 32-bit indexes. + bool is32bit; + + /// Index buffer. + MemoryStreamPtr buffer; +}; + +/// Ogre Pose +class Pose +{ +public: + struct Vertex + { + uint32_t index; + aiVector3D offset; + aiVector3D normal; + }; + typedef std::map PoseVertexMap; + + Pose() : target(0), hasNormals(false) {} + + /// Name. + std::string name; + + /// Target. + uint16_t target; + + /// Does vertices map have normals. + bool hasNormals; + + /// Vertex offset and normals. + PoseVertexMap vertices; +}; +typedef std::vector PoseList; + +/// Ogre Pose Key Frame Ref +struct PoseRef +{ + uint16_t index; + float influence; +}; +typedef std::vector PoseRefList; + +/// Ogre Pose Key Frame +struct PoseKeyFrame +{ + /// Time position in the animation. + float timePos; + + PoseRefList references; +}; +typedef std::vector PoseKeyFrameList; + +/// Ogre Morph Key Frame +struct MorphKeyFrame +{ + /// Time position in the animation. + float timePos; + + MemoryStreamPtr buffer; +}; +typedef std::vector MorphKeyFrameList; + +/// Ogre animation key frame +struct TransformKeyFrame +{ + TransformKeyFrame(); + + aiMatrix4x4 Transform(); + + float timePos; + + aiQuaternion rotation; + aiVector3D position; + aiVector3D scale; +}; +typedef std::vector TransformKeyFrameList; + +/// Ogre Animation Track +struct VertexAnimationTrack +{ + enum Type + { + /// No animation + VAT_NONE = 0, + /// Morph animation is made up of many interpolated snapshot keyframes + VAT_MORPH = 1, + /// Pose animation is made up of a single delta pose keyframe + VAT_POSE = 2, + /// Keyframe that has its on pos, rot and scale for a time position + VAT_TRANSFORM = 3 + }; + + VertexAnimationTrack(); + + /// Convert to Assimp node animation. + aiNodeAnim *ConvertToAssimpAnimationNode(Skeleton *skeleton); + + // Animation type. + Type type; + + /// Vertex data target. + /** 0 == shared geometry + >0 == submesh index + 1 */ + uint16_t target; + + /// Only valid for VAT_TRANSFORM. + std::string boneName; + + /// Only one of these will contain key frames, depending on the type enum. + PoseKeyFrameList poseKeyFrames; + MorphKeyFrameList morphKeyFrames; + TransformKeyFrameList transformKeyFrames; +}; +typedef std::vector VertexAnimationTrackList; + +/// Ogre Animation +class Animation +{ +public: + explicit Animation(Skeleton *parent); + explicit Animation(Mesh *parent); + + /// Returns the associated vertex data for a track in this animation. + /** @note Only valid to call when parent Mesh is set. */ + VertexData *AssociatedVertexData(VertexAnimationTrack *track) const; + + /// Convert to Assimp animation. + aiAnimation *ConvertToAssimpAnimation(); + + /// Parent mesh. + /** @note Set only when animation is read from a mesh. */ + Mesh *parentMesh; + + /// Parent skeleton. + /** @note Set only when animation is read from a skeleton. */ + Skeleton *parentSkeleton; + + /// Animation name. + std::string name; + + /// Base animation name. + std::string baseName; + + /// Length in seconds. + float length; + + /// Base animation key time. + float baseTime; + + /// Animation tracks. + VertexAnimationTrackList tracks; +}; +typedef std::vector AnimationList; + +/// Ogre Bone +class Bone +{ +public: + Bone(); + + /// Returns if this bone is parented. + bool IsParented() const; + + /// Parent index as uint16_t. Internally int32_t as -1 means unparented. + uint16_t ParentId() const; + + /// Add child bone. + void AddChild(Bone *bone); + + /// Calculates the world matrix for bone and its children. + void CalculateWorldMatrixAndDefaultPose(Skeleton *skeleton); + + /// Convert to Assimp node (animation nodes). + aiNode *ConvertToAssimpNode(Skeleton *parent, aiNode *parentNode = 0); + + /// Convert to Assimp bone (mesh bones). + aiBone *ConvertToAssimpBone(Skeleton *parent, const std::vector &boneWeights); + + uint16_t id; + std::string name; + + Bone *parent; + int32_t parentId; + std::vector children; + + aiVector3D position; + aiQuaternion rotation; + aiVector3D scale; + + aiMatrix4x4 worldMatrix; + aiMatrix4x4 defaultPose; +}; +typedef std::vector BoneList; + +/// Ogre Skeleton +class Skeleton +{ +public: + enum BlendMode + { + /// Animations are applied by calculating a weighted average of all animations + ANIMBLEND_AVERAGE = 0, + /// Animations are applied by calculating a weighted cumulative total + ANIMBLEND_CUMULATIVE = 1 + }; + + Skeleton(); + ~Skeleton(); + + /// Releases all memory that this data structure owns. + void Reset(); + + /// Returns unparented root bones. + BoneList RootBones() const; + + /// Returns number of unparented root bones. + size_t NumRootBones() const; + + /// Get bone by name. + Bone *BoneByName(const std::string &name) const; + + /// Get bone by id. + Bone *BoneById(uint16_t id) const; + + BoneList bones; + AnimationList animations; + + /// @todo Take blend mode into account, but where? + BlendMode blendMode; +}; + +/// Ogre Sub Mesh interface, inherited by the binary and XML implementations. +class ISubMesh +{ +public: + /// @note Full list of Ogre types, not all of them are supported and exposed to Assimp. + enum OperationType + { + /// A list of points, 1 vertex per point + OT_POINT_LIST = 1, + /// A list of lines, 2 vertices per line + OT_LINE_LIST = 2, + /// A strip of connected lines, 1 vertex per line plus 1 start vertex + OT_LINE_STRIP = 3, + /// A list of triangles, 3 vertices per triangle + OT_TRIANGLE_LIST = 4, + /// A strip of triangles, 3 vertices for the first triangle, and 1 per triangle after that + OT_TRIANGLE_STRIP = 5, + /// A fan of triangles, 3 vertices for the first triangle, and 1 per triangle after that + OT_TRIANGLE_FAN = 6 + }; + + ISubMesh(); + + /// SubMesh index. + unsigned int index; + + /// SubMesh name. + std::string name; + + /// Material used by this submesh. + std::string materialRef; + + /// Texture alias information. + std::string textureAliasName; + std::string textureAliasRef; + + /// Assimp scene material index used by this submesh. + /** -1 if no material or material could not be imported. */ + int materialIndex; + + /// If submesh uses shared geometry from parent mesh. + bool usesSharedVertexData; + + /// Operation type. + OperationType operationType; +}; + +/// Ogre SubMesh +class SubMesh : public ISubMesh +{ +public: + SubMesh(); + ~SubMesh(); + + /// Releases all memory that this data structure owns. + /** @note Vertex and index data contains shared ptrs + that are freed automatically. In practice the ref count + should be 0 after this reset. */ + void Reset(); + + /// Convert to Assimp mesh. + aiMesh *ConvertToAssimpMesh(Mesh *parent); + + /// Vertex data. + VertexData *vertexData; + + /// Index data. + IndexData *indexData; +}; +typedef std::vector SubMeshList; + +/// Ogre Mesh +class Mesh +{ +public: + /// Constructor. + Mesh(); + + /// Destructor. + ~Mesh(); + + /// Releases all memory that this data structure owns. + void Reset(); + + /// Returns number of subMeshes. + size_t NumSubMeshes() const; + + /// Returns submesh for @c index. + SubMesh *GetSubMesh( size_t index) const; + + /// Convert mesh to Assimp scene. + void ConvertToAssimpScene(aiScene* dest); + + /// Mesh has skeletal animations. + bool hasSkeletalAnimations; + + /// Skeleton reference. + std::string skeletonRef; + + /// Skeleton. + Skeleton *skeleton; + + /// Vertex data + VertexData *sharedVertexData; + + /// Sub meshes. + SubMeshList subMeshes; + + /// Animations + AnimationList animations; + + /// Poses + PoseList poses; +}; + +/// Ogre XML Vertex Data +class VertexDataXml : public IVertexData +{ +public: + VertexDataXml(); + + bool HasPositions() const; + bool HasNormals() const; + bool HasTangents() const; + bool HasUvs() const; + size_t NumUvs() const; + + std::vector positions; + std::vector normals; + std::vector tangents; + std::vector > uvs; +}; + +/// Ogre XML Index Data +class IndexDataXml +{ +public: + IndexDataXml() : faceCount(0) {} + + /// Face count. + uint32_t faceCount; + + std::vector faces; +}; + +/// Ogre XML SubMesh +class SubMeshXml : public ISubMesh +{ +public: + SubMeshXml(); + ~SubMeshXml(); + + /// Releases all memory that this data structure owns. + void Reset(); + + aiMesh *ConvertToAssimpMesh(MeshXml *parent); + + IndexDataXml *indexData; + VertexDataXml *vertexData; +}; +typedef std::vector SubMeshXmlList; + +/// Ogre XML Mesh +class MeshXml +{ +public: + MeshXml(); + ~MeshXml(); + + /// Releases all memory that this data structure owns. + void Reset(); + + /// Returns number of subMeshes. + size_t NumSubMeshes() const; + + /// Returns submesh for @c index. + SubMeshXml *GetSubMesh(uint16_t index) const; + + /// Convert mesh to Assimp scene. + void ConvertToAssimpScene(aiScene* dest); + + /// Skeleton reference. + std::string skeletonRef; + + /// Skeleton. + Skeleton *skeleton; + + /// Vertex data + VertexDataXml *sharedVertexData; + + /// Sub meshes. + SubMeshXmlList subMeshes; +}; + +} // Ogre +} // Assimp + +#endif // ASSIMP_BUILD_NO_OGRE_IMPORTER +#endif // AI_OGRESTRUCTS_H_INC diff --git a/libs/assimp/code/AssetLib/Ogre/OgreXmlSerializer.cpp b/libs/assimp/code/AssetLib/Ogre/OgreXmlSerializer.cpp new file mode 100644 index 0000000..545107e --- /dev/null +++ b/libs/assimp/code/AssetLib/Ogre/OgreXmlSerializer.cpp @@ -0,0 +1,755 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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 "OgreXmlSerializer.h" +#include "OgreBinarySerializer.h" +#include "OgreParsingUtils.h" + +#include +#include + +#include + +#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER + +// Define as 1 to get verbose logging. +#define OGRE_XML_SERIALIZER_DEBUG 0 + +namespace Assimp { +namespace Ogre { + +//AI_WONT_RETURN void ThrowAttibuteError(const XmlParser *reader, const std::string &name, const std::string &error = "") AI_WONT_RETURN_SUFFIX; + +AI_WONT_RETURN void ThrowAttibuteError(const std::string &nodeName, const std::string &name, const std::string &error) { + if (!error.empty()) { + throw DeadlyImportError(error, " in node '", nodeName, "' and attribute '", name, "'"); + } else { + throw DeadlyImportError("Attribute '", name, "' does not exist in node '", nodeName, "'"); + } +} + +template <> +int32_t OgreXmlSerializer::ReadAttribute(XmlNode &xmlNode, const char *name) const { + if (!XmlParser::hasAttribute(xmlNode, name)) { + ThrowAttibuteError(xmlNode.name(), name, "Not found"); + } + pugi::xml_attribute attr = xmlNode.attribute(name); + return static_cast(attr.as_int()); +} + +template <> +uint32_t OgreXmlSerializer::ReadAttribute(XmlNode &xmlNode, const char *name) const { + if (!XmlParser::hasAttribute(xmlNode, name)) { + ThrowAttibuteError(xmlNode.name(), name, "Not found"); + } + + // @note This is hackish. But we are never expecting unsigned values that go outside the + // int32_t range. Just monitor for negative numbers and kill the import. + int32_t temp = ReadAttribute(xmlNode, name); + if (temp < 0) { + ThrowAttibuteError(xmlNode.name(), name, "Found a negative number value where expecting a uint32_t value"); + } + + return static_cast(temp); +} + +template <> +uint16_t OgreXmlSerializer::ReadAttribute(XmlNode &xmlNode, const char *name) const { + if (!XmlParser::hasAttribute(xmlNode, name)) { + ThrowAttibuteError(xmlNode.name(), name, "Not found"); + } + + return static_cast(xmlNode.attribute(name).as_int()); +} + +template <> +float OgreXmlSerializer::ReadAttribute(XmlNode &xmlNode, const char *name) const { + if (!XmlParser::hasAttribute(xmlNode, name)) { + ThrowAttibuteError(xmlNode.name(), name, "Not found"); + } + + return xmlNode.attribute(name).as_float(); +} + +template <> +std::string OgreXmlSerializer::ReadAttribute(XmlNode &xmlNode, const char *name) const { + if (!XmlParser::hasAttribute(xmlNode, name)) { + ThrowAttibuteError(xmlNode.name(), name, "Not found"); + } + + return xmlNode.attribute(name).as_string(); +} + +template <> +bool OgreXmlSerializer::ReadAttribute(XmlNode &xmlNode, const char *name) const { + std::string value = ai_tolower(ReadAttribute(xmlNode, name)); + if (ASSIMP_stricmp(value, "true") == 0) { + return true; + } else if (ASSIMP_stricmp(value, "false") == 0) { + return false; + } + + ThrowAttibuteError(xmlNode.name(), name, "Boolean value is expected to be 'true' or 'false', encountered '" + value + "'"); + return false; +} + +// Mesh XML constants + +// +static const char *nnMesh = "mesh"; +static const char *nnSharedGeometry = "sharedgeometry"; +static const char *nnSubMeshes = "submeshes"; +static const char *nnSubMesh = "submesh"; +//static const char *nnSubMeshNames = "submeshnames"; +static const char *nnSkeletonLink = "skeletonlink"; +//static const char *nnLOD = "levelofdetail"; +//static const char *nnExtremes = "extremes"; +//static const char *nnPoses = "poses"; +static const char *nnAnimations = "animations"; + +// +static const char *nnFaces = "faces"; +static const char *nnFace = "face"; +static const char *nnGeometry = "geometry"; +//static const char *nnTextures = "textures"; + +// +static const char *nnBoneAssignments = "boneassignments"; + +// +static const char *nnVertexBuffer = "vertexbuffer"; + +// +//static const char *nnVertex = "vertex"; +static const char *nnPosition = "position"; +static const char *nnNormal = "normal"; +static const char *nnTangent = "tangent"; +//static const char *nnBinormal = "binormal"; +static const char *nnTexCoord = "texcoord"; +//static const char *nnColorDiffuse = "colour_diffuse"; +//static const char *nnColorSpecular = "colour_specular"; + +// +static const char *nnVertexBoneAssignment = "vertexboneassignment"; + +// Skeleton XML constants + +// +static const char *nnSkeleton = "skeleton"; +static const char *nnBones = "bones"; +static const char *nnBoneHierarchy = "bonehierarchy"; +//static const char *nnAnimationLinks = "animationlinks"; + +// +static const char *nnBone = "bone"; +static const char *nnRotation = "rotation"; +static const char *nnAxis = "axis"; +static const char *nnScale = "scale"; + +// +static const char *nnBoneParent = "boneparent"; + +// +static const char *nnAnimation = "animation"; +static const char *nnTracks = "tracks"; + +// +static const char *nnTrack = "track"; +static const char *nnKeyFrames = "keyframes"; +static const char *nnKeyFrame = "keyframe"; +static const char *nnTranslate = "translate"; +static const char *nnRotate = "rotate"; + +// Common XML constants +static const char *anX = "x"; +static const char *anY = "y"; +static const char *anZ = "z"; + +// Mesh + +OgreXmlSerializer::OgreXmlSerializer(XmlParser *parser) : + mParser(parser) { + // empty +} + +MeshXml *OgreXmlSerializer::ImportMesh(XmlParser *parser) { + if (nullptr == parser) { + return nullptr; + } + + OgreXmlSerializer serializer(parser); + + MeshXml *mesh = new MeshXml(); + serializer.ReadMesh(mesh); + + return mesh; +} + +void OgreXmlSerializer::ReadMesh(MeshXml *mesh) { + XmlNode root = mParser->getRootNode(); + if (nullptr == root) { + throw DeadlyImportError("Root node is <" + std::string(root.name()) + "> expecting "); + } + + XmlNode startNode = root.child(nnMesh); + if (startNode.empty()) { + throw DeadlyImportError("Root node is <" + std::string(root.name()) + "> expecting "); + } + for (XmlNode currentNode : startNode.children()) { + const std::string currentName = currentNode.name(); + if (currentName == nnSharedGeometry) { + mesh->sharedVertexData = new VertexDataXml(); + ReadGeometry(currentNode, mesh->sharedVertexData); + } else if (currentName == nnSubMeshes) { + for (XmlNode &subMeshesNode : currentNode.children()) { + const std::string ¤tSMName = subMeshesNode.name(); + if (currentSMName == nnSubMesh) { + ReadSubMesh(subMeshesNode, mesh); + } + } + } else if (currentName == nnBoneAssignments) { + ReadBoneAssignments(currentNode, mesh->sharedVertexData); + } else if (currentName == nnSkeletonLink) { + } + } + + ASSIMP_LOG_VERBOSE_DEBUG("Reading Mesh"); +} + +void OgreXmlSerializer::ReadGeometry(XmlNode &node, VertexDataXml *dest) { + dest->count = ReadAttribute(node, "vertexcount"); + ASSIMP_LOG_VERBOSE_DEBUG(" - Reading geometry of ", dest->count, " vertices"); + + for (XmlNode currentNode : node.children()) { + const std::string ¤tName = currentNode.name(); + if (currentName == nnVertexBuffer) { + ReadGeometryVertexBuffer(currentNode, dest); + } + } +} + +void OgreXmlSerializer::ReadGeometryVertexBuffer(XmlNode &node, VertexDataXml *dest) { + bool positions = (XmlParser::hasAttribute(node, "positions") && ReadAttribute(node, "positions")); + bool normals = (XmlParser::hasAttribute(node, "normals") && ReadAttribute(node, "normals")); + bool tangents = (XmlParser::hasAttribute(node, "tangents") && ReadAttribute(node, "tangents")); + uint32_t uvs = (XmlParser::hasAttribute(node, "texture_coords") ? ReadAttribute(node, "texture_coords") : 0); + + // Not having positions is a error only if a previous vertex buffer did not have them. + if (!positions && !dest->HasPositions()) { + throw DeadlyImportError("Vertex buffer does not contain positions!"); + } + + if (positions) { + ASSIMP_LOG_VERBOSE_DEBUG(" - Contains positions"); + dest->positions.reserve(dest->count); + } + if (normals) { + ASSIMP_LOG_VERBOSE_DEBUG(" - Contains normals"); + dest->normals.reserve(dest->count); + } + if (tangents) { + ASSIMP_LOG_VERBOSE_DEBUG(" - Contains tangents"); + dest->tangents.reserve(dest->count); + } + if (uvs > 0) { + ASSIMP_LOG_VERBOSE_DEBUG(" - Contains ", uvs, " texture coords"); + dest->uvs.resize(uvs); + for (size_t i = 0, len = dest->uvs.size(); i < len; ++i) { + dest->uvs[i].reserve(dest->count); + } + } + + for (XmlNode currentNode : node.children("vertex")) { + for (XmlNode vertexNode : currentNode.children()) { + const std::string ¤tName = vertexNode.name(); + if (positions && currentName == nnPosition) { + aiVector3D pos; + pos.x = ReadAttribute(vertexNode, anX); + pos.y = ReadAttribute(vertexNode, anY); + pos.z = ReadAttribute(vertexNode, anZ); + dest->positions.push_back(pos); + } else if (normals && currentName == nnNormal) { + aiVector3D normal; + normal.x = ReadAttribute(vertexNode, anX); + normal.y = ReadAttribute(vertexNode, anY); + normal.z = ReadAttribute(vertexNode, anZ); + dest->normals.push_back(normal); + } else if (tangents && currentName == nnTangent) { + aiVector3D tangent; + tangent.x = ReadAttribute(vertexNode, anX); + tangent.y = ReadAttribute(vertexNode, anY); + tangent.z = ReadAttribute(vertexNode, anZ); + dest->tangents.push_back(tangent); + } else if (uvs > 0 && currentName == nnTexCoord) { + for (auto &curUvs : dest->uvs) { + aiVector3D uv; + uv.x = ReadAttribute(vertexNode, "u"); + uv.y = (ReadAttribute(vertexNode, "v") * -1) + 1; // Flip UV from Ogre to Assimp form + curUvs.push_back(uv); + } + } + } + } + + // Sanity checks + if (dest->positions.size() != dest->count) { + throw DeadlyImportError("Read only ", dest->positions.size(), " positions when should have read ", dest->count); + } + if (normals && dest->normals.size() != dest->count) { + throw DeadlyImportError("Read only ", dest->normals.size(), " normals when should have read ", dest->count); + } + if (tangents && dest->tangents.size() != dest->count) { + throw DeadlyImportError("Read only ", dest->tangents.size(), " tangents when should have read ", dest->count); + } + for (unsigned int i = 0; i < dest->uvs.size(); ++i) { + if (dest->uvs[i].size() != dest->count) { + throw DeadlyImportError("Read only ", dest->uvs[i].size(), + " uvs for uv index ", i, " when should have read ", dest->count); + } + } +} + +void OgreXmlSerializer::ReadSubMesh(XmlNode &node, MeshXml *mesh) { + static const char *anMaterial = "material"; + static const char *anUseSharedVertices = "usesharedvertices"; + static const char *anCount = "count"; + static const char *anV1 = "v1"; + static const char *anV2 = "v2"; + static const char *anV3 = "v3"; + static const char *anV4 = "v4"; + + SubMeshXml *submesh = new SubMeshXml(); + + if (XmlParser::hasAttribute(node, anMaterial)) { + submesh->materialRef = ReadAttribute(node, anMaterial); + } + if (XmlParser::hasAttribute(node, anUseSharedVertices)) { + submesh->usesSharedVertexData = ReadAttribute(node, anUseSharedVertices); + } + + ASSIMP_LOG_VERBOSE_DEBUG("Reading SubMesh ", mesh->subMeshes.size()); + ASSIMP_LOG_VERBOSE_DEBUG(" - Material: '", submesh->materialRef, "'"); + ASSIMP_LOG_VERBOSE_DEBUG(" - Uses shared geometry: ", (submesh->usesSharedVertexData ? "true" : "false")); + + // TODO: maybe we have always just 1 faces and 1 geometry and always in this order. this loop will only work correct, when the order + // of faces and geometry changed, and not if we have more than one of one + /// @todo Fix above comment with better read logic below + + bool quadWarned = false; + + for (XmlNode ¤tNode : node.children()) { + const std::string ¤tName = currentNode.name(); + if (currentName == nnFaces) { + submesh->indexData->faceCount = ReadAttribute(currentNode, anCount); + submesh->indexData->faces.reserve(submesh->indexData->faceCount); + for (XmlNode currentChildNode : currentNode.children()) { + const std::string ¤tChildName = currentChildNode.name(); + if (currentChildName == nnFace) { + aiFace face; + face.mNumIndices = 3; + face.mIndices = new unsigned int[3]; + face.mIndices[0] = ReadAttribute(currentChildNode, anV1); + face.mIndices[1] = ReadAttribute(currentChildNode, anV2); + face.mIndices[2] = ReadAttribute(currentChildNode, anV3); + /// @todo Support quads if Ogre even supports them in XML (I'm not sure but I doubt it) + if (!quadWarned && XmlParser::hasAttribute(currentChildNode, anV4)) { + ASSIMP_LOG_WARN("Submesh has quads with , only triangles are supported at the moment!"); + quadWarned = true; + } + submesh->indexData->faces.push_back(face); + } + } + if (submesh->indexData->faces.size() == submesh->indexData->faceCount) { + ASSIMP_LOG_VERBOSE_DEBUG(" - Faces ", submesh->indexData->faceCount); + } else { + throw DeadlyImportError("Read only ", submesh->indexData->faces.size(), " faces when should have read ", submesh->indexData->faceCount); + } + } else if (currentName == nnGeometry) { + if (submesh->usesSharedVertexData) { + throw DeadlyImportError("Found in when use shared geometry is true. Invalid mesh file."); + } + + submesh->vertexData = new VertexDataXml(); + ReadGeometry(currentNode, submesh->vertexData); + } else if (currentName == nnBoneAssignments) { + ReadBoneAssignments(currentNode, submesh->vertexData); + } + } + + submesh->index = static_cast(mesh->subMeshes.size()); + mesh->subMeshes.push_back(submesh); +} + +void OgreXmlSerializer::ReadBoneAssignments(XmlNode &node, VertexDataXml *dest) { + if (!dest) { + throw DeadlyImportError("Cannot read bone assignments, vertex data is null."); + } + + static const char *anVertexIndex = "vertexindex"; + static const char *anBoneIndex = "boneindex"; + static const char *anWeight = "weight"; + + std::set influencedVertices; + for (XmlNode ¤tNode : node.children()) { + const std::string ¤tName = currentNode.name(); + if (currentName == nnVertexBoneAssignment) { + VertexBoneAssignment ba; + ba.vertexIndex = ReadAttribute(currentNode, anVertexIndex); + ba.boneIndex = ReadAttribute(currentNode, anBoneIndex); + ba.weight = ReadAttribute(currentNode, anWeight); + + dest->boneAssignments.push_back(ba); + influencedVertices.insert(ba.vertexIndex); + } + } + + /** Normalize bone weights. + Some exporters won't 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 (const uint32_t vertexIndex : influencedVertices) { + float sum = 0.0f; + for (VertexBoneAssignmentList::const_iterator baIter = dest->boneAssignments.begin(), baEnd = dest->boneAssignments.end(); baIter != baEnd; ++baIter) { + if (baIter->vertexIndex == vertexIndex) + sum += baIter->weight; + } + if ((sum < (1.0f - epsilon)) || (sum > (1.0f + epsilon))) { + for (auto &boneAssign : dest->boneAssignments) { + if (boneAssign.vertexIndex == vertexIndex) + boneAssign.weight /= sum; + } + } + } + + ASSIMP_LOG_VERBOSE_DEBUG(" - ", dest->boneAssignments.size(), " bone assignments"); +} + +// Skeleton + +bool OgreXmlSerializer::ImportSkeleton(Assimp::IOSystem *pIOHandler, MeshXml *mesh) { + if (!mesh || mesh->skeletonRef.empty()) + return false; + + // Highly unusual to see in read world cases but support + // XML mesh referencing a binary skeleton file. + if (EndsWith(mesh->skeletonRef, ".skeleton", false)) { + if (OgreBinarySerializer::ImportSkeleton(pIOHandler, mesh)) + return true; + + /** Last fallback if .skeleton failed to be read. Try reading from + .skeleton.xml even if the XML file referenced a binary skeleton. + @note This logic was in the previous version and I don't want to break + old code that might depends on it. */ + mesh->skeletonRef = mesh->skeletonRef + ".xml"; + } + + XmlParserPtr xmlParser = OpenXmlParser(pIOHandler, mesh->skeletonRef); + if (!xmlParser.get()) + return false; + + Skeleton *skeleton = new Skeleton(); + OgreXmlSerializer serializer(xmlParser.get()); + XmlNode root = xmlParser->getRootNode(); + serializer.ReadSkeleton(root, skeleton); + mesh->skeleton = skeleton; + return true; +} + +bool OgreXmlSerializer::ImportSkeleton(Assimp::IOSystem *pIOHandler, Mesh *mesh) { + if (!mesh || mesh->skeletonRef.empty()) { + return false; + } + + XmlParserPtr xmlParser = OpenXmlParser(pIOHandler, mesh->skeletonRef); + if (!xmlParser.get()) { + return false; + } + + Skeleton *skeleton = new Skeleton(); + OgreXmlSerializer serializer(xmlParser.get()); + XmlNode root = xmlParser->getRootNode(); + + serializer.ReadSkeleton(root, skeleton); + mesh->skeleton = skeleton; + + return true; +} + +XmlParserPtr OgreXmlSerializer::OpenXmlParser(Assimp::IOSystem *pIOHandler, const std::string &filename) { + if (!EndsWith(filename, ".skeleton.xml", false)) { + ASSIMP_LOG_ERROR("Imported Mesh is referencing to unsupported '", filename, "' skeleton file."); + return XmlParserPtr(); + } + + if (!pIOHandler->Exists(filename)) { + ASSIMP_LOG_ERROR("Failed to find skeleton file '", filename, "' that is referenced by imported Mesh."); + return XmlParserPtr(); + } + + std::unique_ptr file(pIOHandler->Open(filename)); + if (!file.get()) { + throw DeadlyImportError("Failed to open skeleton file ", filename); + } + + XmlParserPtr xmlParser = std::make_shared(); + if (!xmlParser->parse(file.get())) { + throw DeadlyImportError("Failed to create XML reader for skeleton file " + filename); + } + return xmlParser; +} + +void OgreXmlSerializer::ReadSkeleton(XmlNode &node, Skeleton *skeleton) { + if (node.name() != nnSkeleton) { + throw DeadlyImportError("Root node is <" + std::string(node.name()) + "> expecting "); + } + + ASSIMP_LOG_VERBOSE_DEBUG("Reading Skeleton"); + + // Optional blend mode from root node + if (XmlParser::hasAttribute(node, "blendmode")) { + skeleton->blendMode = (ai_tolower(ReadAttribute(node, "blendmode")) == "cumulative" ? Skeleton::ANIMBLEND_CUMULATIVE : Skeleton::ANIMBLEND_AVERAGE); + } + + for (XmlNode ¤tNode : node.children()) { + const std::string currentName = currentNode.name(); + if (currentName == nnBones) { + ReadBones(currentNode, skeleton); + } else if (currentName == nnBoneHierarchy) { + ReadBoneHierarchy(currentNode, skeleton); + } else if (currentName == nnAnimations) { + ReadAnimations(currentNode, skeleton); + } + } +} + +void OgreXmlSerializer::ReadAnimations(XmlNode &node, Skeleton *skeleton) { + if (skeleton->bones.empty()) { + throw DeadlyImportError("Cannot read for a Skeleton without bones"); + } + + ASSIMP_LOG_VERBOSE_DEBUG(" - Animations"); + + for (XmlNode ¤tNode : node.children()) { + const std::string currentName = currentNode.name(); + if (currentName == nnAnimation) { + Animation *anim = new Animation(skeleton); + anim->name = ReadAttribute(currentNode, "name"); + anim->length = ReadAttribute(currentNode, "length"); + for (XmlNode ¤tChildNode : currentNode.children()) { + const std::string currentChildName = currentNode.name(); + if (currentChildName == nnTracks) { + ReadAnimationTracks(currentChildNode, anim); + skeleton->animations.push_back(anim); + } else { + throw DeadlyImportError("No found in ", anim->name); + } + } + } + } +} + +void OgreXmlSerializer::ReadAnimationTracks(XmlNode &node, Animation *dest) { + for (XmlNode ¤tNode : node.children()) { + const std::string currentName = currentNode.name(); + if (currentName == nnTrack) { + VertexAnimationTrack track; + track.type = VertexAnimationTrack::VAT_TRANSFORM; + track.boneName = ReadAttribute(currentNode, "bone"); + for (XmlNode ¤tChildNode : currentNode.children()) { + const std::string currentChildName = currentNode.name(); + if (currentChildName == nnKeyFrames) { + ReadAnimationKeyFrames(currentChildNode, dest, &track); + dest->tracks.push_back(track); + } else { + throw DeadlyImportError("No found in ", dest->name); + } + } + } + } +} + +void OgreXmlSerializer::ReadAnimationKeyFrames(XmlNode &node, Animation *anim, VertexAnimationTrack *dest) { + const aiVector3D zeroVec(0.f, 0.f, 0.f); + for (XmlNode ¤tNode : node.children()) { + TransformKeyFrame keyframe; + const std::string currentName = currentNode.name(); + if (currentName == nnKeyFrame) { + keyframe.timePos = ReadAttribute(currentNode, "time"); + for (XmlNode ¤tChildNode : currentNode.children()) { + const std::string currentChildName = currentNode.name(); + if (currentChildName == nnTranslate) { + keyframe.position.x = ReadAttribute(currentChildNode, anX); + keyframe.position.y = ReadAttribute(currentChildNode, anY); + keyframe.position.z = ReadAttribute(currentChildNode, anZ); + } else if (currentChildName == nnRotate) { + float angle = ReadAttribute(currentChildNode, "angle"); + for (XmlNode ¤tChildChildNode : currentNode.children()) { + const std::string currentChildChildName = currentNode.name(); + if (currentChildChildName == nnAxis) { + aiVector3D axis; + axis.x = ReadAttribute(currentChildChildNode, anX); + axis.y = ReadAttribute(currentChildChildNode, anY); + axis.z = ReadAttribute(currentChildChildNode, anZ); + if (axis.Equal(zeroVec)) { + axis.x = 1.0f; + if (angle != 0) { + ASSIMP_LOG_WARN("Found invalid a key frame with a zero rotation axis in animation: ", anim->name); + } + } + keyframe.rotation = aiQuaternion(axis, angle); + } + } + } else if (currentChildName == nnScale) { + keyframe.scale.x = ReadAttribute(currentChildNode, anX); + keyframe.scale.y = ReadAttribute(currentChildNode, anY); + keyframe.scale.z = ReadAttribute(currentChildNode, anZ); + } + } + } + dest->transformKeyFrames.push_back(keyframe); + } +} + +void OgreXmlSerializer::ReadBoneHierarchy(XmlNode &node, Skeleton *skeleton) { + if (skeleton->bones.empty()) { + throw DeadlyImportError("Cannot read for a Skeleton without bones"); + } + + for (XmlNode ¤tNode : node.children()) { + const std::string currentName = currentNode.name(); + if (currentName == nnBoneParent) { + const std::string name = ReadAttribute(currentNode, "bone"); + const std::string parentName = ReadAttribute(currentNode, "parent"); + + Bone *bone = skeleton->BoneByName(name); + Bone *parent = skeleton->BoneByName(parentName); + + if (bone && parent) { + parent->AddChild(bone); + } else { + throw DeadlyImportError("Failed to find bones for parenting: Child ", name, " for parent ", parentName); + } + } + } + + // 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); + } +} + +static bool BoneCompare(Bone *a, Bone *b) { + ai_assert(nullptr != a); + ai_assert(nullptr != b); + + return (a->id < b->id); +} + +void OgreXmlSerializer::ReadBones(XmlNode &node, Skeleton *skeleton) { + ASSIMP_LOG_VERBOSE_DEBUG(" - Bones"); + + for (XmlNode ¤tNode : node.children()) { + const std::string currentName = currentNode.name(); + if (currentName == nnBone) { + Bone *bone = new Bone(); + bone->id = ReadAttribute(currentNode, "id"); + bone->name = ReadAttribute(currentNode, "name"); + for (XmlNode ¤tChildNode : currentNode.children()) { + const std::string currentChildName = currentNode.name(); + if (currentChildName == nnRotation) { + bone->position.x = ReadAttribute(currentChildNode, anX); + bone->position.y = ReadAttribute(currentChildNode, anY); + bone->position.z = ReadAttribute(currentChildNode, anZ); + } else if (currentChildName == nnScale) { + float angle = ReadAttribute(currentChildNode, "angle"); + for (XmlNode currentChildChildNode : currentChildNode.children()) { + const std::string ¤tChildChildName = currentChildChildNode.name(); + if (currentChildChildName == nnAxis) { + aiVector3D axis; + axis.x = ReadAttribute(currentChildChildNode, anX); + axis.y = ReadAttribute(currentChildChildNode, anY); + axis.z = ReadAttribute(currentChildChildNode, anZ); + + bone->rotation = aiQuaternion(axis, angle); + } else { + throw DeadlyImportError("No axis specified for bone rotation in bone ", bone->id); + } + } + } else if (currentChildName == nnScale) { + if (XmlParser::hasAttribute(currentChildNode, "factor")) { + float factor = ReadAttribute(currentChildNode, "factor"); + bone->scale.Set(factor, factor, factor); + } else { + if (XmlParser::hasAttribute(currentChildNode, anX)) + bone->scale.x = ReadAttribute(currentChildNode, anX); + if (XmlParser::hasAttribute(currentChildNode, anY)) + bone->scale.y = ReadAttribute(currentChildNode, anY); + if (XmlParser::hasAttribute(currentChildNode, anZ)) + bone->scale.z = ReadAttribute(currentChildNode, anZ); + } + } + } + skeleton->bones.push_back(bone); + } + } + + // Order bones by Id + std::sort(skeleton->bones.begin(), skeleton->bones.end(), BoneCompare); + + // Validate that bone indexes are not skipped. + /** @note Left this from original authors code, but not sure if this is strictly necessary + as per the Ogre skeleton spec. It might be more that other (later) code in this imported does not break. */ + for (size_t i = 0, len = skeleton->bones.size(); i < len; ++i) { + Bone *b = skeleton->bones[i]; + ASSIMP_LOG_VERBOSE_DEBUG(" ", b->id, " ", b->name); + + if (b->id != static_cast(i)) { + throw DeadlyImportError("Bone ids are not in sequence starting from 0. Missing index ", i); + } + } +} + +} // namespace Ogre +} // namespace Assimp + +#endif // ASSIMP_BUILD_NO_OGRE_IMPORTER diff --git a/libs/assimp/code/AssetLib/Ogre/OgreXmlSerializer.h b/libs/assimp/code/AssetLib/Ogre/OgreXmlSerializer.h new file mode 100644 index 0000000..406681f --- /dev/null +++ b/libs/assimp/code/AssetLib/Ogre/OgreXmlSerializer.h @@ -0,0 +1,102 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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. + +---------------------------------------------------------------------- +*/ + +#ifndef AI_OGREXMLSERIALIZER_H_INC +#define AI_OGREXMLSERIALIZER_H_INC + +#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER + +#include "OgreStructs.h" +#include + +namespace Assimp { + +namespace Ogre { + +using XmlParserPtr = std::shared_ptr<::Assimp::XmlParser> ; + +class OgreXmlSerializer { +public: + /// Imports mesh and returns the result. + /// @note Fatal unrecoverable errors will throw a DeadlyImportError. + static MeshXml *ImportMesh(XmlParser *parser); + + /// Imports skeleton to @c mesh. + /// If mesh does not have a skeleton reference or the skeleton file + /// cannot be found it is not a fatal DeadlyImportError. + /// @return If skeleton import was successful. + static bool ImportSkeleton(IOSystem *pIOHandler, MeshXml *mesh); + static bool ImportSkeleton(IOSystem *pIOHandler, Mesh *mesh); + +private: + explicit OgreXmlSerializer(XmlParser *xmlParser); + + static XmlParserPtr OpenXmlParser(Assimp::IOSystem *pIOHandler, const std::string &filename); + + // Mesh + void ReadMesh(MeshXml *mesh); + void ReadSubMesh(XmlNode &node, MeshXml *mesh); + void ReadGeometry(XmlNode &node, VertexDataXml *dest); + void ReadGeometryVertexBuffer(XmlNode &node, VertexDataXml *dest); + void ReadBoneAssignments(XmlNode &node, VertexDataXml *dest); + + // Skeleton + void ReadSkeleton(XmlNode &node, Skeleton *skeleton); + void ReadBones(XmlNode &node, Skeleton *skeleton); + void ReadBoneHierarchy(XmlNode &node, Skeleton *skeleton); + void ReadAnimations(XmlNode &node, Skeleton *skeleton); + void ReadAnimationTracks(XmlNode &node, Animation *dest); + void ReadAnimationKeyFrames(XmlNode &node, Animation *anim, VertexAnimationTrack *dest); + + template + T ReadAttribute(XmlNode &xmlNode, const char *name) const; + +private: + XmlParser *mParser; +}; + + +} // namespace Ogre +} // namespace Assimp + +#endif // ASSIMP_BUILD_NO_OGRE_IMPORTER +#endif // AI_OGREXMLSERIALIZER_H_INC -- cgit v1.2.1