diff options
author | sanine <sanine.not@pm.me> | 2022-04-16 11:55:09 -0500 |
---|---|---|
committer | sanine <sanine.not@pm.me> | 2022-04-16 11:55:09 -0500 |
commit | db81b925d776103326128bf629cbdda576a223e7 (patch) | |
tree | 58bea8155c686733310009f6bed7363f91fbeb9d /libs/assimp/code/AssetLib/Ogre/OgreStructs.cpp | |
parent | 55860037b14fb3893ba21cf2654c83d349cc1082 (diff) |
move 3rd-party librarys into libs/ and add built-in honeysuckle
Diffstat (limited to 'libs/assimp/code/AssetLib/Ogre/OgreStructs.cpp')
-rw-r--r-- | libs/assimp/code/AssetLib/Ogre/OgreStructs.cpp | 1076 |
1 files changed, 1076 insertions, 0 deletions
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 |