summaryrefslogtreecommitdiff
path: root/libs/assimp/code/AssetLib/Ogre
diff options
context:
space:
mode:
Diffstat (limited to 'libs/assimp/code/AssetLib/Ogre')
-rw-r--r--libs/assimp/code/AssetLib/Ogre/OgreBinarySerializer.cpp973
-rw-r--r--libs/assimp/code/AssetLib/Ogre/OgreBinarySerializer.h415
-rw-r--r--libs/assimp/code/AssetLib/Ogre/OgreImporter.cpp135
-rw-r--r--libs/assimp/code/AssetLib/Ogre/OgreImporter.h98
-rw-r--r--libs/assimp/code/AssetLib/Ogre/OgreMaterial.cpp500
-rw-r--r--libs/assimp/code/AssetLib/Ogre/OgreParsingUtils.h95
-rw-r--r--libs/assimp/code/AssetLib/Ogre/OgreStructs.cpp1076
-rw-r--r--libs/assimp/code/AssetLib/Ogre/OgreStructs.h696
-rw-r--r--libs/assimp/code/AssetLib/Ogre/OgreXmlSerializer.cpp755
-rw-r--r--libs/assimp/code/AssetLib/Ogre/OgreXmlSerializer.h102
10 files changed, 4845 insertions, 0 deletions
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 <assimp/TinyFormatter.h>
+#include <assimp/DefaultLogger.hpp>
+
+#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<bool>() {
+ return (m_reader->GetU1() > 0);
+}
+
+template <>
+inline char OgreBinarySerializer::Read<char>() {
+ return static_cast<char>(m_reader->GetU1());
+}
+
+template <>
+inline uint8_t OgreBinarySerializer::Read<uint8_t>() {
+ return m_reader->GetU1();
+}
+
+template <>
+inline uint16_t OgreBinarySerializer::Read<uint16_t>() {
+ return m_reader->GetU2();
+}
+
+template <>
+inline uint32_t OgreBinarySerializer::Read<uint32_t>() {
+ return m_reader->GetU4();
+}
+
+template <>
+inline float OgreBinarySerializer::Read<float>() {
+ return m_reader->GetF4();
+}
+
+void OgreBinarySerializer::ReadBytes(char *dest, size_t numBytes) {
+ ReadBytes(static_cast<void *>(dest), numBytes);
+}
+
+void OgreBinarySerializer::ReadBytes(uint8_t *dest, size_t numBytes) {
+ ReadBytes(static_cast<void *>(dest), numBytes);
+}
+
+void OgreBinarySerializer::ReadBytes(void *dest, size_t numBytes) {
+ m_reader->CopyAndAdvance(dest, numBytes);
+}
+
+uint8_t *OgreBinarySerializer::ReadBytes(size_t numBytes) {
+ uint8_t *bytes = new uint8_t[numBytes];
+ ReadBytes(bytes, numBytes);
+ return bytes;
+}
+
+void OgreBinarySerializer::ReadVector(aiVector3D &vec) {
+ m_reader->CopyAndAdvance(&vec.x, sizeof(float) * 3);
+}
+
+void OgreBinarySerializer::ReadQuaternion(aiQuaternion &quat) {
+ float temp[4];
+ m_reader->CopyAndAdvance(temp, sizeof(float) * 4);
+ quat.x = temp[0];
+ quat.y = temp[1];
+ quat.z = temp[2];
+ quat.w = temp[3];
+}
+
+bool OgreBinarySerializer::AtEnd() const {
+ return (m_reader->GetRemainingSize() == 0);
+}
+
+std::string OgreBinarySerializer::ReadString(size_t len) {
+ std::string str;
+ str.resize(len);
+ ReadBytes(&str[0], len);
+ return str;
+}
+
+std::string OgreBinarySerializer::ReadLine() {
+ std::string str;
+ while (!AtEnd()) {
+ char c = Read<char>();
+ if (c == '\n')
+ break;
+ str += c;
+ }
+ return str;
+}
+
+uint16_t OgreBinarySerializer::ReadHeader(bool readLen) {
+ uint16_t id = Read<uint16_t>();
+ if (readLen)
+ m_currentLen = Read<uint32_t>();
+
+#if (OGRE_BINARY_SERIALIZER_DEBUG == 1)
+ if (id != HEADER_CHUNK_ID) {
+ ASSIMP_LOG_DEBUG((assetMode == AM_Mesh ? MeshHeaderToString(static_cast<MeshChunkId>(id)) : SkeletonHeaderToString(static_cast<SkeletonChunkId>(id))));
+ }
+#endif
+
+ return id;
+}
+
+void OgreBinarySerializer::RollbackHeader() {
+ m_reader->IncPtr(-MSTREAM_OVERHEAD_SIZE);
+}
+
+void OgreBinarySerializer::SkipBytes(size_t numBytes) {
+#if (OGRE_BINARY_SERIALIZER_DEBUG == 1)
+ 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<bool>();
+
+ 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<uint16_t>();
+ bool manual = Read<bool>();
+
+ /// @note Main mesh is considered as LOD 0, start from index 1.
+ for (size_t i = 1; i < numLods; ++i) {
+ uint16_t id = ReadHeader();
+ if (id != M_MESH_LOD_USAGE) {
+ throw DeadlyImportError("M_MESH_LOD does not contain a M_MESH_LOD_USAGE for each LOD level");
+ }
+
+ m_reader->IncPtr(sizeof(float)); // user value
+
+ if (manual) {
+ id = ReadHeader();
+ if (id != M_MESH_LOD_MANUAL) {
+ throw DeadlyImportError("Manual M_MESH_LOD_USAGE does not contain M_MESH_LOD_MANUAL");
+ }
+
+ ReadLine(); // manual mesh name (ref to another mesh)
+ } else {
+ for (size_t si = 0, silen = mesh->NumSubMeshes(); si < silen; ++si) {
+ id = ReadHeader();
+ if (id != M_MESH_LOD_GENERATED) {
+ throw DeadlyImportError("Generated M_MESH_LOD_USAGE does not contain M_MESH_LOD_GENERATED");
+ }
+
+ uint32_t indexCount = Read<uint32_t>();
+ bool is32bit = Read<bool>();
+
+ if (indexCount > 0) {
+ uint32_t len = indexCount * (is32bit ? sizeof(uint32_t) : sizeof(uint16_t));
+ m_reader->IncPtr(len);
+ }
+ }
+ }
+ }
+}
+
+void OgreBinarySerializer::ReadMeshSkeletonLink(Mesh *mesh) {
+ mesh->skeletonRef = ReadLine();
+}
+
+void OgreBinarySerializer::ReadMeshBounds(Mesh * /*mesh*/) {
+ // Skip bounds, not compatible with Assimp.
+ // 2x float vec3 + 1x float sphere radius
+ SkipBytes(sizeof(float) * 7);
+}
+
+void OgreBinarySerializer::ReadMeshExtremes(Mesh * /*mesh*/) {
+ // Skip extremes, not compatible with Assimp.
+ size_t numBytes = m_currentLen - MSTREAM_OVERHEAD_SIZE;
+ SkipBytes(numBytes);
+}
+
+void OgreBinarySerializer::ReadBoneAssignment(VertexData *dest) {
+ if (!dest) {
+ throw DeadlyImportError("Cannot read bone assignments, vertex data is null.");
+ }
+
+ VertexBoneAssignment ba;
+ ba.vertexIndex = Read<uint32_t>();
+ ba.boneIndex = Read<uint16_t>();
+ ba.weight = Read<float>();
+
+ dest->boneAssignments.push_back(ba);
+}
+
+void OgreBinarySerializer::ReadSubMesh(Mesh *mesh) {
+ uint16_t id = 0;
+
+ SubMesh *submesh = new SubMesh();
+ submesh->materialRef = ReadLine();
+ submesh->usesSharedVertexData = Read<bool>();
+
+ submesh->indexData->count = Read<uint32_t>();
+ submesh->indexData->faceCount = static_cast<uint32_t>(submesh->indexData->count / 3);
+ submesh->indexData->is32bit = Read<bool>();
+
+ 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<unsigned int>(mesh->subMeshes.size());
+ mesh->subMeshes.push_back(submesh);
+}
+
+void OgreBinarySerializer::NormalizeBoneWeights(VertexData *vertexData) const {
+ if (!vertexData || vertexData->boneAssignments.empty())
+ return;
+
+ std::set<uint32_t> influencedVertices;
+ for (VertexBoneAssignmentList::const_iterator baIter = vertexData->boneAssignments.begin(), baEnd = vertexData->boneAssignments.end(); baIter != baEnd; ++baIter) {
+ influencedVertices.insert(baIter->vertexIndex);
+ }
+
+ /** Normalize bone weights.
+ Some exporters 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<SubMesh::OperationType>(Read<uint16_t>());
+}
+
+void OgreBinarySerializer::ReadSubMeshTextureAlias(SubMesh *submesh) {
+ submesh->textureAliasName = ReadLine();
+ submesh->textureAliasRef = ReadLine();
+}
+
+void OgreBinarySerializer::ReadSubMeshNames(Mesh *mesh) {
+ uint16_t id = 0;
+
+ if (!AtEnd()) {
+ id = ReadHeader();
+ while (!AtEnd() && id == M_SUBMESH_NAME_TABLE_ELEMENT) {
+ uint16_t submeshIndex = Read<uint16_t>();
+ SubMesh *submesh = mesh->GetSubMesh(submeshIndex);
+ if (!submesh) {
+ throw DeadlyImportError("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<uint32_t>();
+
+ 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<uint16_t>();
+ element.type = static_cast<VertexElement::Type>(Read<uint16_t>());
+ element.semantic = static_cast<VertexElement::Semantic>(Read<uint16_t>());
+ element.offset = Read<uint16_t>();
+ element.index = Read<uint16_t>();
+
+ 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>();
+ uint16_t vertexSize = Read<uint16_t>();
+
+ uint16_t id = ReadHeader();
+ if (id != M_GEOMETRY_VERTEX_BUFFER_DATA)
+ throw DeadlyImportError("M_GEOMETRY_VERTEX_BUFFER_DATA not found in M_GEOMETRY_VERTEX_BUFFER");
+
+ if (dest->VertexSize(bindIndex) != vertexSize)
+ throw DeadlyImportError("Vertex buffer size does not agree with vertex declaration in M_GEOMETRY_VERTEX_BUFFER");
+
+ size_t numBytes = dest->count * vertexSize;
+ uint8_t *vertexBuffer = ReadBytes(numBytes);
+ dest->vertexBindings[bindIndex] = MemoryStreamPtr(new Assimp::MemoryIOStream(vertexBuffer, numBytes, true));
+
+ 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<bool>();
+
+ if (!manual) {
+ m_reader->IncPtr(sizeof(uint8_t));
+ uint32_t numTriangles = Read<uint32_t>();
+ uint32_t numEdgeGroups = Read<uint32_t>();
+
+ size_t skipBytes = (sizeof(uint32_t) * 8 + sizeof(float) * 4) * numTriangles;
+ m_reader->IncPtr(skipBytes);
+
+ for (size_t i = 0; i < numEdgeGroups; ++i) {
+ uint16_t 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<uint32_t>();
+ for (size_t j = 0; j < numEdges; ++j) {
+ m_reader->IncPtr(sizeof(uint32_t) * 6 + sizeof(uint8_t));
+ }
+ }
+ }
+
+ if (!AtEnd())
+ id = ReadHeader();
+ }
+ if (!AtEnd())
+ RollbackHeader();
+ }
+}
+
+void OgreBinarySerializer::ReadPoses(Mesh *mesh) {
+ if (!AtEnd()) {
+ uint16_t id = ReadHeader();
+ while (!AtEnd() && id == M_POSE) {
+ Pose *pose = new Pose();
+ pose->name = ReadLine();
+ pose->target = Read<uint16_t>();
+ pose->hasNormals = Read<bool>();
+
+ ReadPoseVertices(pose);
+
+ mesh->poses.push_back(pose);
+
+ if (!AtEnd())
+ id = ReadHeader();
+ }
+ if (!AtEnd())
+ RollbackHeader();
+ }
+}
+
+void OgreBinarySerializer::ReadPoseVertices(Pose *pose) {
+ if (!AtEnd()) {
+ uint16_t id = ReadHeader();
+ while (!AtEnd() && id == M_POSE_VERTEX) {
+ Pose::Vertex v;
+ v.index = Read<uint32_t>();
+ ReadVector(v.offset);
+ if (pose->hasNormals)
+ ReadVector(v.normal);
+
+ pose->vertices[v.index] = v;
+
+ if (!AtEnd())
+ id = ReadHeader();
+ }
+ if (!AtEnd())
+ RollbackHeader();
+ }
+}
+
+void OgreBinarySerializer::ReadAnimations(Mesh *mesh) {
+ if (!AtEnd()) {
+ uint16_t id = ReadHeader();
+ while (!AtEnd() && id == M_ANIMATION) {
+ Animation *anim = new Animation(mesh);
+ anim->name = ReadLine();
+ anim->length = Read<float>();
+
+ ReadAnimation(anim);
+
+ mesh->animations.push_back(anim);
+
+ if (!AtEnd())
+ id = ReadHeader();
+ }
+ if (!AtEnd())
+ RollbackHeader();
+ }
+}
+
+void OgreBinarySerializer::ReadAnimation(Animation *anim) {
+ if (!AtEnd()) {
+ uint16_t id = ReadHeader();
+ if (id == M_ANIMATION_BASEINFO) {
+ anim->baseName = ReadLine();
+ anim->baseTime = Read<float>();
+
+ // Advance to first track
+ id = ReadHeader();
+ }
+
+ while (!AtEnd() && id == M_ANIMATION_TRACK) {
+ VertexAnimationTrack track;
+ track.type = static_cast<VertexAnimationTrack::Type>(Read<uint16_t>());
+ track.target = Read<uint16_t>();
+
+ ReadAnimationKeyFrames(anim, &track);
+
+ anim->tracks.push_back(track);
+
+ if (!AtEnd())
+ id = ReadHeader();
+ }
+ if (!AtEnd())
+ RollbackHeader();
+ }
+}
+
+void OgreBinarySerializer::ReadAnimationKeyFrames(Animation *anim, VertexAnimationTrack *track) {
+ if (!AtEnd()) {
+ uint16_t id = ReadHeader();
+ while (!AtEnd() &&
+ (id == M_ANIMATION_MORPH_KEYFRAME ||
+ id == M_ANIMATION_POSE_KEYFRAME)) {
+ if (id == M_ANIMATION_MORPH_KEYFRAME) {
+ MorphKeyFrame kf;
+ kf.timePos = Read<float>();
+ bool hasNormals = Read<bool>();
+
+ size_t vertexCount = anim->AssociatedVertexData(track)->count;
+ size_t vertexSize = sizeof(float) * (hasNormals ? 6 : 3);
+ size_t numBytes = vertexCount * vertexSize;
+
+ uint8_t *morphBuffer = ReadBytes(numBytes);
+ kf.buffer = MemoryStreamPtr(new Assimp::MemoryIOStream(morphBuffer, numBytes, true));
+
+ track->morphKeyFrames.push_back(kf);
+ } else if (id == M_ANIMATION_POSE_KEYFRAME) {
+ PoseKeyFrame kf;
+ kf.timePos = Read<float>();
+
+ if (!AtEnd()) {
+ id = ReadHeader();
+ while (!AtEnd() && id == M_ANIMATION_POSE_REF) {
+ PoseRef pr;
+ pr.index = Read<uint16_t>();
+ pr.influence = Read<float>();
+ kf.references.push_back(pr);
+
+ if (!AtEnd())
+ id = ReadHeader();
+ }
+ if (!AtEnd())
+ RollbackHeader();
+ }
+
+ track->poseKeyFrames.push_back(kf);
+ }
+
+ if (!AtEnd())
+ id = ReadHeader();
+ }
+ if (!AtEnd())
+ RollbackHeader();
+ }
+}
+
+// Skeleton
+
+bool OgreBinarySerializer::ImportSkeleton(Assimp::IOSystem *pIOHandler, Mesh *mesh) {
+ if (!mesh || mesh->skeletonRef.empty())
+ return false;
+
+ // Highly unusual to see in read world cases but support
+ // binary mesh referencing a XML skeleton file.
+ if (EndsWith(mesh->skeletonRef, ".skeleton.xml", false)) {
+ OgreXmlSerializer::ImportSkeleton(pIOHandler, mesh);
+ return false;
+ }
+
+ MemoryStreamReaderPtr reader = OpenReader(pIOHandler, mesh->skeletonRef);
+ 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<Skeleton::BlendMode>(Read<uint16_t>());
+ 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<uint16_t>();
+
+ // Pos and rot
+ ReadVector(bone->position);
+ ReadQuaternion(bone->rotation);
+
+ // Scale (optional)
+ if (m_currentLen > MSTREAM_BONE_SIZE_WITHOUT_SCALE)
+ ReadVector(bone->scale);
+
+ // Bone indexes need to start from 0 and be contiguous
+ if (bone->id != skeleton->bones.size()) {
+ throw DeadlyImportError("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>();
+ uint16_t parentId = Read<uint16_t>();
+
+ Bone *child = skeleton->BoneById(childId);
+ Bone *parent = skeleton->BoneById(parentId);
+
+ if (child && parent)
+ parent->AddChild(child);
+ else
+ throw DeadlyImportError("Failed to find bones for parenting: Child id ", childId, " for parent id ", parentId);
+}
+
+void OgreBinarySerializer::ReadSkeletonAnimation(Skeleton *skeleton) {
+ Animation *anim = new Animation(skeleton);
+ anim->name = ReadLine();
+ anim->length = Read<float>();
+
+ if (!AtEnd()) {
+ uint16_t id = ReadHeader();
+ if (id == SKELETON_ANIMATION_BASEINFO) {
+ anim->baseName = ReadLine();
+ anim->baseTime = Read<float>();
+
+ // Advance to first track
+ id = ReadHeader();
+ }
+
+ while (!AtEnd() && id == SKELETON_ANIMATION_TRACK) {
+ ReadSkeletonAnimationTrack(skeleton, anim);
+
+ if (!AtEnd())
+ id = ReadHeader();
+ }
+ if (!AtEnd())
+ RollbackHeader();
+ }
+
+ skeleton->animations.push_back(anim);
+
+ ASSIMP_LOG_VERBOSE_DEBUG(" ", anim->name, " (", anim->length, " sec, ", anim->tracks.size(), " tracks)");
+}
+
+void OgreBinarySerializer::ReadSkeletonAnimationTrack(Skeleton * /*skeleton*/, Animation *dest) {
+ uint16_t boneId = Read<uint16_t>();
+ Bone *bone = dest->parentSkeleton->BoneById(boneId);
+ if (!bone) {
+ throw DeadlyImportError("Cannot read animation track, target bone ", boneId, " not in target Skeleton");
+ }
+
+ VertexAnimationTrack track;
+ track.type = VertexAnimationTrack::VAT_TRANSFORM;
+ track.boneName = bone->name;
+
+ uint16_t id = ReadHeader();
+ while (!AtEnd() && id == SKELETON_ANIMATION_TRACK_KEYFRAME) {
+ ReadSkeletonAnimationKeyFrame(&track);
+
+ if (!AtEnd())
+ id = ReadHeader();
+ }
+ if (!AtEnd())
+ RollbackHeader();
+
+ dest->tracks.push_back(track);
+}
+
+void OgreBinarySerializer::ReadSkeletonAnimationKeyFrame(VertexAnimationTrack *dest) {
+ TransformKeyFrame keyframe;
+ keyframe.timePos = Read<float>();
+
+ // Rot and pos
+ ReadQuaternion(keyframe.rotation);
+ ReadVector(keyframe.position);
+
+ // Scale (optional)
+ if (m_currentLen > MSTREAM_KEYFRAME_SIZE_WITHOUT_SCALE)
+ ReadVector(keyframe.scale);
+
+ dest->transformKeyFrames.push_back(keyframe);
+}
+
+void OgreBinarySerializer::ReadSkeletonAnimationLink(Skeleton * /*skeleton*/) {
+ // Skip bounds, not compatible with Assimp.
+ ReadLine(); // skeleton name
+ SkipBytes(sizeof(float) * 3); // scale
+}
+
+} // 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 <assimp/StreamReader.h>
+
+namespace Assimp {
+namespace Ogre {
+
+typedef Assimp::StreamReaderLE MemoryStreamReader;
+typedef std::shared_ptr<MemoryStreamReader> 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 <typename T>
+ 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 <assimp/importerdesc.h>
+#include <assimp/Importer.hpp>
+#include <memory>
+
+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[] = { "<mesh>" };
+ 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> 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<IOStream> scopedFile(f);
+ XmlParser xmlParser;
+
+ //std::unique_ptr<CIrrXML_IOStreamReader> xmlStream(new CIrrXML_IOStreamReader(scopedFile.get()));
+ //std::unique_ptr<XmlReader> reader(irr::io::createIrrXMLReader(xmlStream.get()));
+ xmlParser.parse(scopedFile.get());
+ // Import mesh
+ std::unique_ptr<MeshXml> 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 <assimp/BaseImporter.h>
+#include <assimp/material.h>
+
+#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<aiMaterial *> &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<aiTextureType, unsigned int> 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 <assimp/StringUtils.h>
+#include <assimp/TinyFormatter.h>
+#include <assimp/fast_atof.h>
+#include <assimp/material.h>
+#include <assimp/scene.h>
+#include <assimp/DefaultLogger.hpp>
+
+#include <memory>
+#include <sstream>
+#include <vector>
+
+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<aiMaterial *> 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<int>(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<aiMaterial *> 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<uint16_t>(i));
+ if (submesh && !submesh->materialRef.empty()) {
+ aiMaterial *material = ReadMaterial(pFile, pIOHandler, submesh->materialRef);
+ if (material) {
+ submesh->materialIndex = static_cast<int>(materials.size());
+ materials.push_back(material);
+ }
+ }
+ }
+
+ AssignMaterials(pScene, materials);
+}
+
+void OgreImporter::AssignMaterials(aiScene *pScene, std::vector<aiMaterial *> &materials) {
+ pScene->mNumMaterials = static_cast<unsigned int>(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<string> 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<IOStream> 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<char> 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 <assimp/ParsingUtils.h>
+#include <cstdint>
+#include <algorithm>
+#include <cctype>
+#include <functional>
+#include <sstream>
+
+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 <assimp/Exceptional.h>
+#include <assimp/TinyFormatter.h>
+#include <assimp/scene.h>
+#include <assimp/DefaultLogger.hpp>
+
+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<unsigned int>(vi)];
+ for (VertexBoneAssignmentList::const_iterator iter = vertexWeights.begin(), end = vertexWeights.end();
+ iter != end; ++iter) {
+ std::vector<aiVertexWeight> &boneWeights = weights[iter->boneIndex];
+ boneWeights.push_back(aiVertexWeight(static_cast<unsigned int>(vi), iter->weight));
+ }
+ }
+ return weights;
+}
+
+std::set<uint16_t> IVertexData::ReferencedBonesByWeights() const {
+ std::set<uint16_t> 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<uint32_t>(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<unsigned int>(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<unsigned int>(i);
+ }
+
+ // Export skeleton
+ if (skeleton) {
+ // Bones
+ if (!skeleton->bones.empty()) {
+ BoneList rootBones = skeleton->RootBones();
+ dest->mRootNode->mNumChildren = static_cast<unsigned int>(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<unsigned int>(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<unsigned int>(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<unsigned int>(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<unsigned int>(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<unsigned int>(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<unsigned int>(newIndex);
+
+ // Ogres vertex index to ref into the source buffers.
+ const size_t ogreVertexIndex = ogreFace.mIndices[v];
+ src->AddVertexMapping(static_cast<uint32_t>(ogreVertexIndex), static_cast<uint32_t>(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<uint16_t> referencedBones = src->ReferencedBonesByWeights();
+
+ dest->mNumBones = static_cast<unsigned int>(referencedBones.size());
+ dest->mBones = new aiBone *[dest->mNumBones];
+
+ size_t assimpBoneIndex = 0;
+ for (std::set<uint16_t>::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<unsigned int>(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<unsigned int>(i);
+ }
+
+ // Export skeleton
+ if (skeleton) {
+ // Bones
+ if (!skeleton->bones.empty()) {
+ BoneList rootBones = skeleton->RootBones();
+ dest->mRootNode->mNumChildren = static_cast<unsigned int>(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<unsigned int>(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<unsigned int>(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<unsigned int>(newIndex);
+
+ // Ogres vertex index to ref into the source buffers.
+ const size_t ogreVertexIndex = ogreFace.mIndices[v];
+ src->AddVertexMapping(static_cast<uint32_t>(ogreVertexIndex), static_cast<uint32_t>(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<aiVector3D> &uvSrc = src->uvs[uvi];
+ uvDest[newIndex] = uvSrc[ogreVertexIndex];
+ }
+ }
+ }
+
+ // Bones and bone weights
+ if (parent->skeleton && boneAssignments) {
+ AssimpVertexBoneWeightList weights = src->AssimpBoneWeights(dest->mNumVertices);
+ std::set<uint16_t> referencedBones = src->ReferencedBonesByWeights();
+
+ dest->mNumBones = static_cast<unsigned int>(referencedBones.size());
+ dest->mBones = new aiBone *[dest->mNumBones];
+
+ size_t assimpBoneIndex = 0;
+ for (std::set<uint16_t>::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<double>(length);
+ anim->mTicksPerSecond = 1.0;
+
+ // Tracks
+ if (!tracks.empty()) {
+ anim->mNumChannels = static_cast<unsigned int>(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<uint16_t>(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<unsigned int>(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<aiVertexWeight> &boneWeights) {
+ aiBone *bone = new aiBone();
+ bone->mName = name;
+ bone->mOffsetMatrix = worldMatrix;
+
+ if (!boneWeights.empty()) {
+ bone->mNumWeights = static_cast<unsigned int>(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<unsigned int>(numKeyframes);
+ nodeAnim->mNumRotationKeys = static_cast<unsigned int>(numKeyframes);
+ nodeAnim->mNumScalingKeys = static_cast<unsigned int>(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<double>(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 <assimp/MemoryIOWrapper.h>
+#include <memory>
+#include <assimp/mesh.h>
+#include <map>
+#include <vector>
+#include <set>
+
+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<MemoryStream> MemoryStreamPtr;
+typedef std::map<uint16_t, MemoryStreamPtr> 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<VertexElement> VertexElementList;
+
+/// Ogre Vertex Bone Assignment
+struct VertexBoneAssignment
+{
+ uint32_t vertexIndex;
+ uint16_t boneIndex;
+ float weight;
+};
+typedef std::vector<VertexBoneAssignment> VertexBoneAssignmentList;
+typedef std::map<uint32_t, VertexBoneAssignmentList > VertexBoneAssignmentsMap;
+typedef std::map<uint16_t, std::vector<aiVertexWeight> > 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<uint16_t> 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<uint32_t, std::vector<uint32_t> > 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<uint32_t, Vertex> 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<Pose*> PoseList;
+
+/// Ogre Pose Key Frame Ref
+struct PoseRef
+{
+ uint16_t index;
+ float influence;
+};
+typedef std::vector<PoseRef> PoseRefList;
+
+/// Ogre Pose Key Frame
+struct PoseKeyFrame
+{
+ /// Time position in the animation.
+ float timePos;
+
+ PoseRefList references;
+};
+typedef std::vector<PoseKeyFrame> PoseKeyFrameList;
+
+/// Ogre Morph Key Frame
+struct MorphKeyFrame
+{
+ /// Time position in the animation.
+ float timePos;
+
+ MemoryStreamPtr buffer;
+};
+typedef std::vector<MorphKeyFrame> MorphKeyFrameList;
+
+/// Ogre animation key frame
+struct TransformKeyFrame
+{
+ TransformKeyFrame();
+
+ aiMatrix4x4 Transform();
+
+ float timePos;
+
+ aiQuaternion rotation;
+ aiVector3D position;
+ aiVector3D scale;
+};
+typedef std::vector<TransformKeyFrame> 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<VertexAnimationTrack> 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<Animation*> 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<aiVertexWeight> &boneWeights);
+
+ uint16_t id;
+ std::string name;
+
+ Bone *parent;
+ int32_t parentId;
+ std::vector<uint16_t> children;
+
+ aiVector3D position;
+ aiQuaternion rotation;
+ aiVector3D scale;
+
+ aiMatrix4x4 worldMatrix;
+ aiMatrix4x4 defaultPose;
+};
+typedef std::vector<Bone*> 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<SubMesh*> 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<aiVector3D> positions;
+ std::vector<aiVector3D> normals;
+ std::vector<aiVector3D> tangents;
+ std::vector<std::vector<aiVector3D> > uvs;
+};
+
+/// Ogre XML Index Data
+class IndexDataXml
+{
+public:
+ IndexDataXml() : faceCount(0) {}
+
+ /// Face count.
+ uint32_t faceCount;
+
+ std::vector<aiFace> 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<SubMeshXml*> 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 <assimp/TinyFormatter.h>
+#include <assimp/DefaultLogger.hpp>
+
+#include <memory>
+
+#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<int32_t>(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<int32_t>(attr.as_int());
+}
+
+template <>
+uint32_t OgreXmlSerializer::ReadAttribute<uint32_t>(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<int32_t>(xmlNode, name);
+ if (temp < 0) {
+ ThrowAttibuteError(xmlNode.name(), name, "Found a negative number value where expecting a uint32_t value");
+ }
+
+ return static_cast<uint32_t>(temp);
+}
+
+template <>
+uint16_t OgreXmlSerializer::ReadAttribute<uint16_t>(XmlNode &xmlNode, const char *name) const {
+ if (!XmlParser::hasAttribute(xmlNode, name)) {
+ ThrowAttibuteError(xmlNode.name(), name, "Not found");
+ }
+
+ return static_cast<uint16_t>(xmlNode.attribute(name).as_int());
+}
+
+template <>
+float OgreXmlSerializer::ReadAttribute<float>(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<std::string>(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<bool>(XmlNode &xmlNode, const char *name) const {
+ std::string value = ai_tolower(ReadAttribute<std::string>(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
+
+// <mesh>
+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";
+
+// <submesh>
+static const char *nnFaces = "faces";
+static const char *nnFace = "face";
+static const char *nnGeometry = "geometry";
+//static const char *nnTextures = "textures";
+
+// <mesh/submesh>
+static const char *nnBoneAssignments = "boneassignments";
+
+// <sharedgeometry/geometry>
+static const char *nnVertexBuffer = "vertexbuffer";
+
+// <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";
+
+// <boneassignments>
+static const char *nnVertexBoneAssignment = "vertexboneassignment";
+
+// Skeleton XML constants
+
+// <skeleton>
+static const char *nnSkeleton = "skeleton";
+static const char *nnBones = "bones";
+static const char *nnBoneHierarchy = "bonehierarchy";
+//static const char *nnAnimationLinks = "animationlinks";
+
+// <bones>
+static const char *nnBone = "bone";
+static const char *nnRotation = "rotation";
+static const char *nnAxis = "axis";
+static const char *nnScale = "scale";
+
+// <bonehierarchy>
+static const char *nnBoneParent = "boneparent";
+
+// <animations>
+static const char *nnAnimation = "animation";
+static const char *nnTracks = "tracks";
+
+// <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 <mesh>");
+ }
+
+ XmlNode startNode = root.child(nnMesh);
+ if (startNode.empty()) {
+ throw DeadlyImportError("Root node is <" + std::string(root.name()) + "> expecting <mesh>");
+ }
+ 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 &currentSMName = 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<uint32_t>(node, "vertexcount");
+ ASSIMP_LOG_VERBOSE_DEBUG(" - Reading geometry of ", dest->count, " vertices");
+
+ for (XmlNode currentNode : node.children()) {
+ const std::string &currentName = currentNode.name();
+ if (currentName == nnVertexBuffer) {
+ ReadGeometryVertexBuffer(currentNode, dest);
+ }
+ }
+}
+
+void OgreXmlSerializer::ReadGeometryVertexBuffer(XmlNode &node, VertexDataXml *dest) {
+ bool positions = (XmlParser::hasAttribute(node, "positions") && ReadAttribute<bool>(node, "positions"));
+ bool normals = (XmlParser::hasAttribute(node, "normals") && ReadAttribute<bool>(node, "normals"));
+ bool tangents = (XmlParser::hasAttribute(node, "tangents") && ReadAttribute<bool>(node, "tangents"));
+ uint32_t uvs = (XmlParser::hasAttribute(node, "texture_coords") ? ReadAttribute<uint32_t>(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 &currentName = vertexNode.name();
+ if (positions && currentName == nnPosition) {
+ aiVector3D pos;
+ pos.x = ReadAttribute<float>(vertexNode, anX);
+ pos.y = ReadAttribute<float>(vertexNode, anY);
+ pos.z = ReadAttribute<float>(vertexNode, anZ);
+ dest->positions.push_back(pos);
+ } else if (normals && currentName == nnNormal) {
+ aiVector3D normal;
+ normal.x = ReadAttribute<float>(vertexNode, anX);
+ normal.y = ReadAttribute<float>(vertexNode, anY);
+ normal.z = ReadAttribute<float>(vertexNode, anZ);
+ dest->normals.push_back(normal);
+ } else if (tangents && currentName == nnTangent) {
+ aiVector3D tangent;
+ tangent.x = ReadAttribute<float>(vertexNode, anX);
+ tangent.y = ReadAttribute<float>(vertexNode, anY);
+ tangent.z = ReadAttribute<float>(vertexNode, anZ);
+ dest->tangents.push_back(tangent);
+ } else if (uvs > 0 && currentName == nnTexCoord) {
+ for (auto &curUvs : dest->uvs) {
+ aiVector3D uv;
+ uv.x = ReadAttribute<float>(vertexNode, "u");
+ uv.y = (ReadAttribute<float>(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<std::string>(node, anMaterial);
+ }
+ if (XmlParser::hasAttribute(node, anUseSharedVertices)) {
+ submesh->usesSharedVertexData = ReadAttribute<bool>(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 &currentNode : node.children()) {
+ const std::string &currentName = currentNode.name();
+ if (currentName == nnFaces) {
+ submesh->indexData->faceCount = ReadAttribute<uint32_t>(currentNode, anCount);
+ submesh->indexData->faces.reserve(submesh->indexData->faceCount);
+ for (XmlNode currentChildNode : currentNode.children()) {
+ const std::string &currentChildName = currentChildNode.name();
+ if (currentChildName == nnFace) {
+ aiFace face;
+ face.mNumIndices = 3;
+ face.mIndices = new unsigned int[3];
+ face.mIndices[0] = ReadAttribute<uint32_t>(currentChildNode, anV1);
+ face.mIndices[1] = ReadAttribute<uint32_t>(currentChildNode, anV2);
+ face.mIndices[2] = ReadAttribute<uint32_t>(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 <face> has quads with <v4>, 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 <geometry> in <submesh> 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<unsigned int>(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<uint32_t> influencedVertices;
+ for (XmlNode &currentNode : node.children()) {
+ const std::string &currentName = currentNode.name();
+ if (currentName == nnVertexBoneAssignment) {
+ VertexBoneAssignment ba;
+ ba.vertexIndex = ReadAttribute<uint32_t>(currentNode, anVertexIndex);
+ ba.boneIndex = ReadAttribute<uint16_t>(currentNode, anBoneIndex);
+ ba.weight = ReadAttribute<float>(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<IOStream> file(pIOHandler->Open(filename));
+ if (!file.get()) {
+ throw DeadlyImportError("Failed to open skeleton file ", filename);
+ }
+
+ XmlParserPtr xmlParser = std::make_shared<XmlParser>();
+ 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 <skeleton>");
+ }
+
+ ASSIMP_LOG_VERBOSE_DEBUG("Reading Skeleton");
+
+ // Optional blend mode from root node
+ if (XmlParser::hasAttribute(node, "blendmode")) {
+ skeleton->blendMode = (ai_tolower(ReadAttribute<std::string>(node, "blendmode")) == "cumulative" ? Skeleton::ANIMBLEND_CUMULATIVE : Skeleton::ANIMBLEND_AVERAGE);
+ }
+
+ for (XmlNode &currentNode : 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 <animations> for a Skeleton without bones");
+ }
+
+ ASSIMP_LOG_VERBOSE_DEBUG(" - Animations");
+
+ for (XmlNode &currentNode : node.children()) {
+ const std::string currentName = currentNode.name();
+ if (currentName == nnAnimation) {
+ Animation *anim = new Animation(skeleton);
+ anim->name = ReadAttribute<std::string>(currentNode, "name");
+ anim->length = ReadAttribute<float>(currentNode, "length");
+ for (XmlNode &currentChildNode : currentNode.children()) {
+ const std::string currentChildName = currentNode.name();
+ if (currentChildName == nnTracks) {
+ ReadAnimationTracks(currentChildNode, anim);
+ skeleton->animations.push_back(anim);
+ } else {
+ throw DeadlyImportError("No <tracks> found in <animation> ", anim->name);
+ }
+ }
+ }
+ }
+}
+
+void OgreXmlSerializer::ReadAnimationTracks(XmlNode &node, Animation *dest) {
+ for (XmlNode &currentNode : node.children()) {
+ const std::string currentName = currentNode.name();
+ if (currentName == nnTrack) {
+ VertexAnimationTrack track;
+ track.type = VertexAnimationTrack::VAT_TRANSFORM;
+ track.boneName = ReadAttribute<std::string>(currentNode, "bone");
+ for (XmlNode &currentChildNode : currentNode.children()) {
+ const std::string currentChildName = currentNode.name();
+ if (currentChildName == nnKeyFrames) {
+ ReadAnimationKeyFrames(currentChildNode, dest, &track);
+ dest->tracks.push_back(track);
+ } else {
+ throw DeadlyImportError("No <keyframes> found in <track> ", dest->name);
+ }
+ }
+ }
+ }
+}
+
+void OgreXmlSerializer::ReadAnimationKeyFrames(XmlNode &node, Animation *anim, VertexAnimationTrack *dest) {
+ const aiVector3D zeroVec(0.f, 0.f, 0.f);
+ for (XmlNode &currentNode : node.children()) {
+ TransformKeyFrame keyframe;
+ const std::string currentName = currentNode.name();
+ if (currentName == nnKeyFrame) {
+ keyframe.timePos = ReadAttribute<float>(currentNode, "time");
+ for (XmlNode &currentChildNode : currentNode.children()) {
+ const std::string currentChildName = currentNode.name();
+ if (currentChildName == nnTranslate) {
+ keyframe.position.x = ReadAttribute<float>(currentChildNode, anX);
+ keyframe.position.y = ReadAttribute<float>(currentChildNode, anY);
+ keyframe.position.z = ReadAttribute<float>(currentChildNode, anZ);
+ } else if (currentChildName == nnRotate) {
+ float angle = ReadAttribute<float>(currentChildNode, "angle");
+ for (XmlNode &currentChildChildNode : currentNode.children()) {
+ const std::string currentChildChildName = currentNode.name();
+ if (currentChildChildName == nnAxis) {
+ aiVector3D axis;
+ axis.x = ReadAttribute<float>(currentChildChildNode, anX);
+ axis.y = ReadAttribute<float>(currentChildChildNode, anY);
+ axis.z = ReadAttribute<float>(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<float>(currentChildNode, anX);
+ keyframe.scale.y = ReadAttribute<float>(currentChildNode, anY);
+ keyframe.scale.z = ReadAttribute<float>(currentChildNode, anZ);
+ }
+ }
+ }
+ dest->transformKeyFrames.push_back(keyframe);
+ }
+}
+
+void OgreXmlSerializer::ReadBoneHierarchy(XmlNode &node, Skeleton *skeleton) {
+ if (skeleton->bones.empty()) {
+ throw DeadlyImportError("Cannot read <bonehierarchy> for a Skeleton without bones");
+ }
+
+ for (XmlNode &currentNode : node.children()) {
+ const std::string currentName = currentNode.name();
+ if (currentName == nnBoneParent) {
+ const std::string name = ReadAttribute<std::string>(currentNode, "bone");
+ const std::string parentName = ReadAttribute<std::string>(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 &currentNode : node.children()) {
+ const std::string currentName = currentNode.name();
+ if (currentName == nnBone) {
+ Bone *bone = new Bone();
+ bone->id = ReadAttribute<uint16_t>(currentNode, "id");
+ bone->name = ReadAttribute<std::string>(currentNode, "name");
+ for (XmlNode &currentChildNode : currentNode.children()) {
+ const std::string currentChildName = currentNode.name();
+ if (currentChildName == nnRotation) {
+ bone->position.x = ReadAttribute<float>(currentChildNode, anX);
+ bone->position.y = ReadAttribute<float>(currentChildNode, anY);
+ bone->position.z = ReadAttribute<float>(currentChildNode, anZ);
+ } else if (currentChildName == nnScale) {
+ float angle = ReadAttribute<float>(currentChildNode, "angle");
+ for (XmlNode currentChildChildNode : currentChildNode.children()) {
+ const std::string &currentChildChildName = currentChildChildNode.name();
+ if (currentChildChildName == nnAxis) {
+ aiVector3D axis;
+ axis.x = ReadAttribute<float>(currentChildChildNode, anX);
+ axis.y = ReadAttribute<float>(currentChildChildNode, anY);
+ axis.z = ReadAttribute<float>(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<float>(currentChildNode, "factor");
+ bone->scale.Set(factor, factor, factor);
+ } else {
+ if (XmlParser::hasAttribute(currentChildNode, anX))
+ bone->scale.x = ReadAttribute<float>(currentChildNode, anX);
+ if (XmlParser::hasAttribute(currentChildNode, anY))
+ bone->scale.y = ReadAttribute<float>(currentChildNode, anY);
+ if (XmlParser::hasAttribute(currentChildNode, anZ))
+ bone->scale.z = ReadAttribute<float>(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<uint16_t>(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 <assimp/XmlParser.h>
+
+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 <typename T>
+ 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