1075 lines
32 KiB
C++
1075 lines
32 KiB
C++
/*
|
|
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.emplace_back(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() = default;
|
|
|
|
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() = default;
|
|
|
|
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
|