OgreImporter: Proper rewrite of the XML parser to OgreXmlSerializer. Now more robust for XML sources, previously had hardcoded expectations on the child node ordering. Implement common Skeleton class for both binary and xml serialization. Implement shared IVertexData with proper bone assignment to Assimp bone weights functionality.
parent
75598f69b7
commit
0b937c5a4b
|
@ -331,8 +331,6 @@ SET( Ogre_SRCS
|
||||||
OgreBinarySerializer.cpp
|
OgreBinarySerializer.cpp
|
||||||
OgreXmlSerializer.cpp
|
OgreXmlSerializer.cpp
|
||||||
OgreMaterial.cpp
|
OgreMaterial.cpp
|
||||||
OgreMesh.cpp
|
|
||||||
OgreSkeleton.cpp
|
|
||||||
)
|
)
|
||||||
SOURCE_GROUP( Ogre FILES ${Ogre_SRCS})
|
SOURCE_GROUP( Ogre FILES ${Ogre_SRCS})
|
||||||
|
|
||||||
|
|
|
@ -206,7 +206,7 @@ void OgreBinarySerializer::ReadMesh(Mesh *mesh)
|
||||||
{
|
{
|
||||||
mesh->hasSkeletalAnimations = Read<bool>();
|
mesh->hasSkeletalAnimations = Read<bool>();
|
||||||
|
|
||||||
DefaultLogger::get()->debug(Formatter::format() << "Reading Mesh");
|
DefaultLogger::get()->debug("Reading Mesh");
|
||||||
DefaultLogger::get()->debug(Formatter::format() << " - Skeletal animations: " << (mesh->hasSkeletalAnimations ? "true" : "false"));
|
DefaultLogger::get()->debug(Formatter::format() << " - Skeletal animations: " << (mesh->hasSkeletalAnimations ? "true" : "false"));
|
||||||
|
|
||||||
if (!AtEnd())
|
if (!AtEnd())
|
||||||
|
@ -230,7 +230,7 @@ void OgreBinarySerializer::ReadMesh(Mesh *mesh)
|
||||||
case M_GEOMETRY:
|
case M_GEOMETRY:
|
||||||
{
|
{
|
||||||
mesh->sharedVertexData = new VertexData();
|
mesh->sharedVertexData = new VertexData();
|
||||||
ReadGeometry(mesh, mesh->sharedVertexData);
|
ReadGeometry(mesh->sharedVertexData);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case M_SUBMESH:
|
case M_SUBMESH:
|
||||||
|
@ -245,7 +245,7 @@ void OgreBinarySerializer::ReadMesh(Mesh *mesh)
|
||||||
}
|
}
|
||||||
case M_MESH_BONE_ASSIGNMENT:
|
case M_MESH_BONE_ASSIGNMENT:
|
||||||
{
|
{
|
||||||
ReadBoneAssignment(mesh);
|
ReadBoneAssignment(mesh->sharedVertexData);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case M_MESH_LOD:
|
case M_MESH_LOD:
|
||||||
|
@ -291,6 +291,8 @@ void OgreBinarySerializer::ReadMesh(Mesh *mesh)
|
||||||
if (!AtEnd())
|
if (!AtEnd())
|
||||||
RollbackHeader();
|
RollbackHeader();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NormalizeBoneWeights(mesh->sharedVertexData);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OgreBinarySerializer::ReadMeshLodInfo(Mesh *mesh)
|
void OgreBinarySerializer::ReadMeshLodInfo(Mesh *mesh)
|
||||||
|
@ -361,18 +363,12 @@ void OgreBinarySerializer::ReadMeshExtremes(Mesh *mesh)
|
||||||
SkipBytes(numBytes);
|
SkipBytes(numBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OgreBinarySerializer::ReadBoneAssignment(Mesh *dest)
|
void OgreBinarySerializer::ReadBoneAssignment(VertexData *dest)
|
||||||
{
|
|
||||||
VertexBoneAssignment ba;
|
|
||||||
ba.vertexIndex = Read<uint32_t>();
|
|
||||||
ba.boneIndex = Read<uint16_t>();
|
|
||||||
ba.weight = Read<float>();
|
|
||||||
|
|
||||||
dest->boneAssignments.push_back(ba);
|
|
||||||
}
|
|
||||||
|
|
||||||
void OgreBinarySerializer::ReadBoneAssignment(SubMesh2 *dest)
|
|
||||||
{
|
{
|
||||||
|
if (!dest) {
|
||||||
|
throw DeadlyImportError("Cannot read bone assignments, vertex data is null.");
|
||||||
|
}
|
||||||
|
|
||||||
VertexBoneAssignment ba;
|
VertexBoneAssignment ba;
|
||||||
ba.vertexIndex = Read<uint32_t>();
|
ba.vertexIndex = Read<uint32_t>();
|
||||||
ba.boneIndex = Read<uint16_t>();
|
ba.boneIndex = Read<uint16_t>();
|
||||||
|
@ -385,7 +381,7 @@ void OgreBinarySerializer::ReadSubMesh(Mesh *mesh)
|
||||||
{
|
{
|
||||||
uint16_t id = 0;
|
uint16_t id = 0;
|
||||||
|
|
||||||
SubMesh2 *submesh = new SubMesh2();
|
SubMesh *submesh = new SubMesh();
|
||||||
submesh->materialRef = ReadLine();
|
submesh->materialRef = ReadLine();
|
||||||
submesh->usesSharedVertexData = Read<bool>();
|
submesh->usesSharedVertexData = Read<bool>();
|
||||||
|
|
||||||
|
@ -418,7 +414,7 @@ void OgreBinarySerializer::ReadSubMesh(Mesh *mesh)
|
||||||
}
|
}
|
||||||
|
|
||||||
submesh->vertexData = new VertexData();
|
submesh->vertexData = new VertexData();
|
||||||
ReadGeometry(mesh, submesh->vertexData);
|
ReadGeometry(submesh->vertexData);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bone assignment, submesh operation and texture aliases
|
// Bone assignment, submesh operation and texture aliases
|
||||||
|
@ -439,7 +435,7 @@ void OgreBinarySerializer::ReadSubMesh(Mesh *mesh)
|
||||||
}
|
}
|
||||||
case M_SUBMESH_BONE_ASSIGNMENT:
|
case M_SUBMESH_BONE_ASSIGNMENT:
|
||||||
{
|
{
|
||||||
ReadBoneAssignment(submesh);
|
ReadBoneAssignment(submesh->vertexData);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case M_SUBMESH_TEXTURE_ALIAS:
|
case M_SUBMESH_TEXTURE_ALIAS:
|
||||||
|
@ -455,17 +451,54 @@ void OgreBinarySerializer::ReadSubMesh(Mesh *mesh)
|
||||||
if (!AtEnd())
|
if (!AtEnd())
|
||||||
RollbackHeader();
|
RollbackHeader();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NormalizeBoneWeights(submesh->vertexData);
|
||||||
|
|
||||||
submesh->index = mesh->subMeshes.size();
|
submesh->index = mesh->subMeshes.size();
|
||||||
mesh->subMeshes.push_back(submesh);
|
mesh->subMeshes.push_back(submesh);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OgreBinarySerializer::ReadSubMeshOperation(SubMesh2 *submesh)
|
void OgreBinarySerializer::NormalizeBoneWeights(VertexData *vertexData) const
|
||||||
{
|
{
|
||||||
submesh->operationType = static_cast<SubMesh2::OperationType>(Read<uint16_t>());
|
if (!vertexData || vertexData->boneAssignments.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::set<uint32_t> influencedVertices;
|
||||||
|
for (VertexBoneAssignmentList::const_iterator baIter=vertexData->boneAssignments.begin(), baEnd=vertexData->boneAssignments.end(); baIter != baEnd; ++baIter) {
|
||||||
|
influencedVertices.insert(baIter->vertexIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Normalize bone weights.
|
||||||
|
Some exporters wont care if the sum of all bone weights
|
||||||
|
for a single vertex equals 1 or not, so validate here. */
|
||||||
|
const float epsilon = 0.05f;
|
||||||
|
for(std::set<uint32_t>::const_iterator iter=influencedVertices.begin(), end=influencedVertices.end(); iter != end; ++iter)
|
||||||
|
{
|
||||||
|
const uint32_t vertexIndex = (*iter);
|
||||||
|
|
||||||
|
float sum = 0.0f;
|
||||||
|
for (VertexBoneAssignmentList::const_iterator baIter=vertexData->boneAssignments.begin(), baEnd=vertexData->boneAssignments.end(); baIter != baEnd; ++baIter)
|
||||||
|
{
|
||||||
|
if (baIter->vertexIndex == vertexIndex)
|
||||||
|
sum += baIter->weight;
|
||||||
|
}
|
||||||
|
if ((sum < (1.0f - epsilon)) || (sum > (1.0f + epsilon)))
|
||||||
|
{
|
||||||
|
for (VertexBoneAssignmentList::iterator baIter=vertexData->boneAssignments.begin(), baEnd=vertexData->boneAssignments.end(); baIter != baEnd; ++baIter)
|
||||||
|
{
|
||||||
|
if (baIter->vertexIndex == vertexIndex)
|
||||||
|
baIter->weight /= sum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OgreBinarySerializer::ReadSubMeshTextureAlias(SubMesh2 *submesh)
|
void OgreBinarySerializer::ReadSubMeshOperation(SubMesh *submesh)
|
||||||
|
{
|
||||||
|
submesh->operationType = static_cast<SubMesh::OperationType>(Read<uint16_t>());
|
||||||
|
}
|
||||||
|
|
||||||
|
void OgreBinarySerializer::ReadSubMeshTextureAlias(SubMesh *submesh)
|
||||||
{
|
{
|
||||||
submesh->textureAliasName = ReadLine();
|
submesh->textureAliasName = ReadLine();
|
||||||
submesh->textureAliasRef = ReadLine();
|
submesh->textureAliasRef = ReadLine();
|
||||||
|
@ -482,7 +515,7 @@ void OgreBinarySerializer::ReadSubMeshNames(Mesh *mesh)
|
||||||
while (!AtEnd() && id == M_SUBMESH_NAME_TABLE_ELEMENT)
|
while (!AtEnd() && id == M_SUBMESH_NAME_TABLE_ELEMENT)
|
||||||
{
|
{
|
||||||
uint16_t submeshIndex = Read<uint16_t>();
|
uint16_t submeshIndex = Read<uint16_t>();
|
||||||
SubMesh2 *submesh = mesh->SubMesh(submeshIndex);
|
SubMesh *submesh = mesh->GetSubMesh(submeshIndex);
|
||||||
if (!submesh) {
|
if (!submesh) {
|
||||||
throw DeadlyImportError(Formatter::format() << "Ogre Mesh does not include submesh " << submeshIndex << " referenced in M_SUBMESH_NAME_TABLE_ELEMENT. Invalid mesh file.");
|
throw DeadlyImportError(Formatter::format() << "Ogre Mesh does not include submesh " << submeshIndex << " referenced in M_SUBMESH_NAME_TABLE_ELEMENT. Invalid mesh file.");
|
||||||
}
|
}
|
||||||
|
@ -498,7 +531,7 @@ void OgreBinarySerializer::ReadSubMeshNames(Mesh *mesh)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OgreBinarySerializer::ReadGeometry(Mesh *mesh, VertexData *dest)
|
void OgreBinarySerializer::ReadGeometry(VertexData *dest)
|
||||||
{
|
{
|
||||||
dest->count = Read<uint32_t>();
|
dest->count = Read<uint32_t>();
|
||||||
|
|
||||||
|
@ -515,12 +548,12 @@ void OgreBinarySerializer::ReadGeometry(Mesh *mesh, VertexData *dest)
|
||||||
{
|
{
|
||||||
case M_GEOMETRY_VERTEX_DECLARATION:
|
case M_GEOMETRY_VERTEX_DECLARATION:
|
||||||
{
|
{
|
||||||
ReadGeometryVertexDeclaration(mesh, dest);
|
ReadGeometryVertexDeclaration(dest);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case M_GEOMETRY_VERTEX_BUFFER:
|
case M_GEOMETRY_VERTEX_BUFFER:
|
||||||
{
|
{
|
||||||
ReadGeometryVertexBuffer(mesh, dest);
|
ReadGeometryVertexBuffer(dest);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -533,14 +566,14 @@ void OgreBinarySerializer::ReadGeometry(Mesh *mesh, VertexData *dest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OgreBinarySerializer::ReadGeometryVertexDeclaration(Mesh *mesh, VertexData *dest)
|
void OgreBinarySerializer::ReadGeometryVertexDeclaration(VertexData *dest)
|
||||||
{
|
{
|
||||||
if (!AtEnd())
|
if (!AtEnd())
|
||||||
{
|
{
|
||||||
uint16_t id = ReadHeader();
|
uint16_t id = ReadHeader();
|
||||||
while (!AtEnd() && id == M_GEOMETRY_VERTEX_ELEMENT)
|
while (!AtEnd() && id == M_GEOMETRY_VERTEX_ELEMENT)
|
||||||
{
|
{
|
||||||
ReadGeometryVertexElement(mesh, dest);
|
ReadGeometryVertexElement(dest);
|
||||||
|
|
||||||
if (!AtEnd())
|
if (!AtEnd())
|
||||||
id = ReadHeader();
|
id = ReadHeader();
|
||||||
|
@ -550,7 +583,7 @@ void OgreBinarySerializer::ReadGeometryVertexDeclaration(Mesh *mesh, VertexData
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OgreBinarySerializer::ReadGeometryVertexElement(Mesh *mesh, VertexData *dest)
|
void OgreBinarySerializer::ReadGeometryVertexElement(VertexData *dest)
|
||||||
{
|
{
|
||||||
VertexElement element;
|
VertexElement element;
|
||||||
element.source = Read<uint16_t>();
|
element.source = Read<uint16_t>();
|
||||||
|
@ -565,7 +598,7 @@ void OgreBinarySerializer::ReadGeometryVertexElement(Mesh *mesh, VertexData *des
|
||||||
dest->vertexElements.push_back(element);
|
dest->vertexElements.push_back(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OgreBinarySerializer::ReadGeometryVertexBuffer(Mesh *mesh, VertexData *dest)
|
void OgreBinarySerializer::ReadGeometryVertexBuffer(VertexData *dest)
|
||||||
{
|
{
|
||||||
uint16_t bindIndex = Read<uint16_t>();
|
uint16_t bindIndex = Read<uint16_t>();
|
||||||
uint16_t vertexSize = Read<uint16_t>();
|
uint16_t vertexSize = Read<uint16_t>();
|
||||||
|
@ -682,7 +715,7 @@ void OgreBinarySerializer::ReadAnimations(Mesh *mesh)
|
||||||
uint16_t id = ReadHeader();
|
uint16_t id = ReadHeader();
|
||||||
while (!AtEnd() && id == M_ANIMATION)
|
while (!AtEnd() && id == M_ANIMATION)
|
||||||
{
|
{
|
||||||
Animation2 *anim = new Animation2(mesh);
|
Animation *anim = new Animation(mesh);
|
||||||
anim->name = ReadLine();
|
anim->name = ReadLine();
|
||||||
anim->length = Read<float>();
|
anim->length = Read<float>();
|
||||||
|
|
||||||
|
@ -698,7 +731,7 @@ void OgreBinarySerializer::ReadAnimations(Mesh *mesh)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OgreBinarySerializer::ReadAnimation(Animation2 *anim)
|
void OgreBinarySerializer::ReadAnimation(Animation *anim)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (!AtEnd())
|
if (!AtEnd())
|
||||||
|
@ -731,7 +764,7 @@ void OgreBinarySerializer::ReadAnimation(Animation2 *anim)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OgreBinarySerializer::ReadAnimationKeyFrames(Animation2 *anim, VertexAnimationTrack *track)
|
void OgreBinarySerializer::ReadAnimationKeyFrames(Animation *anim, VertexAnimationTrack *track)
|
||||||
{
|
{
|
||||||
if (!AtEnd())
|
if (!AtEnd())
|
||||||
{
|
{
|
||||||
|
|
|
@ -51,7 +51,7 @@ namespace Ogre
|
||||||
{
|
{
|
||||||
class OgreBinarySerializer
|
class OgreBinarySerializer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static Mesh *ImportMesh(MemoryStreamReader *reader);
|
static Mesh *ImportMesh(MemoryStreamReader *reader);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -68,27 +68,28 @@ namespace Ogre
|
||||||
void ReadMeshSkeletonLink(Mesh *mesh);
|
void ReadMeshSkeletonLink(Mesh *mesh);
|
||||||
void ReadMeshBounds(Mesh *mesh);
|
void ReadMeshBounds(Mesh *mesh);
|
||||||
void ReadMeshExtremes(Mesh *mesh);
|
void ReadMeshExtremes(Mesh *mesh);
|
||||||
|
|
||||||
void ReadSubMesh(Mesh *mesh);
|
void ReadSubMesh(Mesh *mesh);
|
||||||
void ReadSubMeshNames(Mesh *mesh);
|
void ReadSubMeshNames(Mesh *mesh);
|
||||||
void ReadSubMeshOperation(SubMesh2 *submesh);
|
void ReadSubMeshOperation(SubMesh *submesh);
|
||||||
void ReadSubMeshTextureAlias(SubMesh2 *submesh);
|
void ReadSubMeshTextureAlias(SubMesh *submesh);
|
||||||
|
|
||||||
void ReadBoneAssignment(Mesh *dest);
|
|
||||||
void ReadBoneAssignment(SubMesh2 *dest);
|
|
||||||
|
|
||||||
void ReadGeometry(Mesh *mesh, VertexData *dest);
|
void ReadBoneAssignment(VertexData *dest);
|
||||||
void ReadGeometryVertexDeclaration(Mesh *mesh, VertexData *dest);
|
|
||||||
void ReadGeometryVertexElement(Mesh *mesh, VertexData *dest);
|
void ReadGeometry(VertexData *dest);
|
||||||
void ReadGeometryVertexBuffer(Mesh *mesh, VertexData *dest);
|
void ReadGeometryVertexDeclaration(VertexData *dest);
|
||||||
|
void ReadGeometryVertexElement(VertexData *dest);
|
||||||
|
void ReadGeometryVertexBuffer(VertexData *dest);
|
||||||
|
|
||||||
void ReadEdgeList(Mesh *mesh);
|
void ReadEdgeList(Mesh *mesh);
|
||||||
void ReadPoses(Mesh *mesh);
|
void ReadPoses(Mesh *mesh);
|
||||||
void ReadPoseVertices(Pose *pose);
|
void ReadPoseVertices(Pose *pose);
|
||||||
|
|
||||||
void ReadAnimations(Mesh *mesh);
|
void ReadAnimations(Mesh *mesh);
|
||||||
void ReadAnimation(Animation2 *anim);
|
void ReadAnimation(Animation *anim);
|
||||||
void ReadAnimationKeyFrames(Animation2 *anim, VertexAnimationTrack *track);
|
void ReadAnimationKeyFrames(Animation *anim, VertexAnimationTrack *track);
|
||||||
|
|
||||||
|
void NormalizeBoneWeights(VertexData *vertexData) const;
|
||||||
|
|
||||||
uint16_t ReadHeader(bool readLen = true);
|
uint16_t ReadHeader(bool readLen = true);
|
||||||
void RollbackHeader();
|
void RollbackHeader();
|
||||||
|
|
|
@ -42,15 +42,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
#include "AssimpPCH.h"
|
#include "AssimpPCH.h"
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <sstream>
|
|
||||||
#include <istream>
|
|
||||||
|
|
||||||
#include "OgreImporter.h"
|
#include "OgreImporter.h"
|
||||||
#include "OgreBinarySerializer.h"
|
#include "OgreBinarySerializer.h"
|
||||||
|
#include "OgreXmlSerializer.h"
|
||||||
#include "TinyFormatter.h"
|
|
||||||
#include "irrXMLWrapper.h"
|
|
||||||
|
|
||||||
static const aiImporterDesc desc = {
|
static const aiImporterDesc desc = {
|
||||||
"Ogre3D Mesh Importer",
|
"Ogre3D Mesh Importer",
|
||||||
|
@ -65,8 +59,6 @@ static const aiImporterDesc desc = {
|
||||||
"mesh mesh.xml"
|
"mesh mesh.xml"
|
||||||
};
|
};
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
namespace Assimp
|
namespace Assimp
|
||||||
{
|
{
|
||||||
namespace Ogre
|
namespace Ogre
|
||||||
|
@ -96,192 +88,54 @@ bool OgreImporter::CanRead(const std::string &pFile, Assimp::IOSystem *pIOHandle
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
/// @todo Read and validate first header chunk?
|
||||||
return EndsWith(pFile, ".mesh", false);
|
return EndsWith(pFile, ".mesh", false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OgreImporter::InternReadFile(const std::string &pFile, aiScene *pScene, Assimp::IOSystem *pIOHandler)
|
void OgreImporter::InternReadFile(const std::string &pFile, aiScene *pScene, Assimp::IOSystem *pIOHandler)
|
||||||
{
|
{
|
||||||
// -------------------- Initial file and XML operations --------------------
|
// Open source file
|
||||||
|
|
||||||
// Open
|
|
||||||
IOStream *f = pIOHandler->Open(pFile, "rb");
|
IOStream *f = pIOHandler->Open(pFile, "rb");
|
||||||
if (!f) {
|
if (!f) {
|
||||||
throw DeadlyImportError("Failed to open file " + pFile);
|
throw DeadlyImportError("Failed to open file " + pFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Binary .mesh import
|
// Binary .mesh import
|
||||||
if (EndsWith(pFile, ".mesh", false)) {
|
if (EndsWith(pFile, ".mesh", false))
|
||||||
// Read full data from file
|
{
|
||||||
/// @note MemoryStreamReader takes ownership of f.
|
/// @note MemoryStreamReader takes ownership of f.
|
||||||
MemoryStreamReader reader(f);
|
MemoryStreamReader reader(f);
|
||||||
|
|
||||||
// Import mesh
|
// Import mesh
|
||||||
boost::scoped_ptr<Mesh> mesh = OgreBinarySerializer::ImportMesh(&reader);
|
boost::scoped_ptr<Mesh> mesh = OgreBinarySerializer::ImportMesh(&reader);
|
||||||
|
|
||||||
// Import mesh referenced materials
|
// Import mesh referenced materials
|
||||||
ReadMaterials(pFile, pIOHandler, pScene, mesh.get());
|
ReadMaterials(pFile, pIOHandler, pScene, mesh.get());
|
||||||
|
|
||||||
// Convert to Assimp.
|
// Convert to Assimp
|
||||||
mesh->ConvertToAssimpScene(pScene);
|
mesh->ConvertToAssimpScene(pScene);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read
|
|
||||||
boost::scoped_ptr<IOStream> file(f);
|
|
||||||
boost::scoped_ptr<CIrrXML_IOStreamReader> xmlStream(new CIrrXML_IOStreamReader(file.get()));
|
|
||||||
boost::scoped_ptr<XmlReader> reader(irr::io::createIrrXMLReader(xmlStream.get()));
|
|
||||||
if (!reader) {
|
|
||||||
throw DeadlyImportError("Failed to create XML Reader for " + pFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
DefaultLogger::get()->debug("Opened a XML reader for " + pFile);
|
|
||||||
|
|
||||||
// Read root node
|
|
||||||
NextNode(reader.get());
|
|
||||||
if (!CurrentNodeNameEquals(reader.get(), "mesh")) {
|
|
||||||
throw DeadlyImportError("Root node is not <mesh> but <" + string(reader->getNodeName()) + "> in " + pFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Node names
|
|
||||||
const string nnSharedGeometry = "sharedgeometry";
|
|
||||||
const string nnVertexBuffer = "vertexbuffer";
|
|
||||||
const string nnSubMeshes = "submeshes";
|
|
||||||
const string nnSubMesh = "submesh";
|
|
||||||
const string nnSubMeshNames = "submeshnames";
|
|
||||||
const string nnSkeletonLink = "skeletonlink";
|
|
||||||
|
|
||||||
// -------------------- Shared Geometry --------------------
|
|
||||||
// This can be used to share geometry between submeshes
|
|
||||||
|
|
||||||
NextNode(reader.get());
|
|
||||||
if (CurrentNodeNameEquals(reader.get(), nnSharedGeometry))
|
|
||||||
{
|
|
||||||
DefaultLogger::get()->debug("Reading shared geometry");
|
|
||||||
unsigned int NumVertices = GetAttribute<unsigned int>(reader.get(), "vertexcount");
|
|
||||||
|
|
||||||
NextNode(reader.get());
|
|
||||||
while(CurrentNodeNameEquals(reader.get(), nnVertexBuffer)) {
|
|
||||||
ReadVertexBuffer(m_SharedGeometry, reader.get(), NumVertices);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------------- Sub Meshes --------------------
|
|
||||||
|
|
||||||
if (!CurrentNodeNameEquals(reader.get(), nnSubMeshes)) {
|
|
||||||
throw DeadlyImportError("Could not find <submeshes> node inside root <mesh> node");
|
|
||||||
}
|
|
||||||
|
|
||||||
vector<boost::shared_ptr<SubMesh> > subMeshes;
|
|
||||||
vector<aiMaterial*> materials;
|
|
||||||
|
|
||||||
NextNode(reader.get());
|
|
||||||
while(CurrentNodeNameEquals(reader.get(), nnSubMesh))
|
|
||||||
{
|
|
||||||
SubMesh* submesh = new SubMesh();
|
|
||||||
ReadSubMesh(subMeshes.size(), *submesh, reader.get());
|
|
||||||
|
|
||||||
/** @todo What is the correct way of handling empty ref here.
|
|
||||||
Does Assimp require there to be a valid material index for each mesh,
|
|
||||||
even if its a dummy material. */
|
|
||||||
aiMaterial* material = ReadMaterial(pFile, pIOHandler, submesh->MaterialName);
|
|
||||||
if (!material)
|
|
||||||
material = new aiMaterial();
|
|
||||||
if (material)
|
|
||||||
{
|
|
||||||
submesh->MaterialIndex = materials.size();
|
|
||||||
materials.push_back(material);
|
|
||||||
}
|
|
||||||
|
|
||||||
subMeshes.push_back(boost::shared_ptr<SubMesh>(submesh));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (subMeshes.empty()) {
|
|
||||||
throw DeadlyImportError("Could not find <submeshes> node inside root <mesh> node");
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is really a internal error if we failed to create dummy materials.
|
|
||||||
if (subMeshes.size() != materials.size()) {
|
|
||||||
throw DeadlyImportError("Internal Error: Material count does not match the submesh count");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip submesh names.
|
|
||||||
/// @todo Should these be read to scene etc. metadata?
|
|
||||||
if (CurrentNodeNameEquals(reader.get(), nnSubMeshNames))
|
|
||||||
{
|
|
||||||
NextNode(reader.get());
|
|
||||||
while(CurrentNodeNameEquals(reader.get(), nnSubMesh)) {
|
|
||||||
NextNode(reader.get());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------------- Skeleton --------------------
|
|
||||||
|
|
||||||
vector<Bone> Bones;
|
|
||||||
vector<Animation> Animations;
|
|
||||||
|
|
||||||
if (CurrentNodeNameEquals(reader.get(), nnSkeletonLink))
|
|
||||||
{
|
|
||||||
string skeletonFile = GetAttribute<string>(reader.get(), "name");
|
|
||||||
if (!skeletonFile.empty())
|
|
||||||
{
|
|
||||||
ReadSkeleton(pFile, pIOHandler, pScene, skeletonFile, Bones, Animations);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DefaultLogger::get()->debug("Found a unusual <" + nnSkeletonLink + "> with a empty file reference");
|
|
||||||
}
|
|
||||||
NextNode(reader.get());
|
|
||||||
}
|
}
|
||||||
|
// XML .mesh.xml import
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
DefaultLogger::get()->debug("Mesh has no assigned skeleton with <" + nnSkeletonLink + ">");
|
/// @note XmlReader does not take ownership of f, hence the scoped ptr.
|
||||||
|
boost::scoped_ptr<IOStream> scopedFile(f);
|
||||||
|
boost::scoped_ptr<CIrrXML_IOStreamReader> xmlStream(new CIrrXML_IOStreamReader(scopedFile.get()));
|
||||||
|
boost::scoped_ptr<XmlReader> reader(irr::io::createIrrXMLReader(xmlStream.get()));
|
||||||
|
|
||||||
|
// Import mesh
|
||||||
|
boost::scoped_ptr<MeshXml> mesh = OgreXmlSerializer::ImportMesh(reader.get());
|
||||||
|
|
||||||
|
// Import skeleton
|
||||||
|
OgreXmlSerializer::ImportSkeleton(pIOHandler, mesh);
|
||||||
|
|
||||||
|
// Import mesh referenced materials
|
||||||
|
ReadMaterials(pFile, pIOHandler, pScene, mesh.get());
|
||||||
|
|
||||||
|
// Convert to Assimp
|
||||||
|
mesh->ConvertToAssimpScene(pScene);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now there might be <boneassignments> for the shared geometry
|
|
||||||
if (CurrentNodeNameEquals(reader.get(), "boneassignments")) {
|
|
||||||
ReadBoneWeights(m_SharedGeometry, reader.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------------- Process Results --------------------
|
|
||||||
BOOST_FOREACH(boost::shared_ptr<SubMesh> submesh, subMeshes)
|
|
||||||
{
|
|
||||||
ProcessSubMesh(*submesh.get(), m_SharedGeometry);
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------------- Apply to aiScene --------------------
|
|
||||||
|
|
||||||
// Materials
|
|
||||||
pScene->mNumMaterials = 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];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Meshes
|
|
||||||
pScene->mMeshes = new aiMesh*[subMeshes.size()];
|
|
||||||
pScene->mNumMeshes = subMeshes.size();
|
|
||||||
|
|
||||||
for(size_t i=0, len=subMeshes.size(); i<len; ++i)
|
|
||||||
{
|
|
||||||
boost::shared_ptr<SubMesh> submesh = subMeshes[i];
|
|
||||||
pScene->mMeshes[i] = CreateAssimpSubMesh(pScene, *(submesh.get()), Bones);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the root node
|
|
||||||
pScene->mRootNode = new aiNode();
|
|
||||||
pScene->mRootNode->mMeshes = new unsigned int[subMeshes.size()];
|
|
||||||
pScene->mRootNode->mNumMeshes = subMeshes.size();
|
|
||||||
|
|
||||||
for(size_t i=0, len=subMeshes.size(); i<len; ++i) {
|
|
||||||
pScene->mRootNode->mMeshes[i] = static_cast<unsigned int>(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skeleton and animations
|
|
||||||
CreateAssimpSkeleton(pScene, Bones, Animations);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // Ogre
|
} // Ogre
|
||||||
|
|
|
@ -53,58 +53,10 @@ namespace Assimp
|
||||||
namespace Ogre
|
namespace Ogre
|
||||||
{
|
{
|
||||||
|
|
||||||
struct Face;
|
|
||||||
struct BoneWeight;
|
|
||||||
struct Bone;
|
|
||||||
struct Animation;
|
|
||||||
|
|
||||||
/// Ogre SubMesh
|
|
||||||
struct SubMesh
|
|
||||||
{
|
|
||||||
bool UseSharedGeometry;
|
|
||||||
bool Use32bitIndexes;
|
|
||||||
|
|
||||||
std::string Name;
|
|
||||||
std::string MaterialName;
|
|
||||||
|
|
||||||
bool HasGeometry;
|
|
||||||
bool HasPositions;
|
|
||||||
bool HasNormals;
|
|
||||||
bool HasTangents;
|
|
||||||
|
|
||||||
std::vector<Face> Faces;
|
|
||||||
std::vector<aiVector3D> Positions;
|
|
||||||
std::vector<aiVector3D> Normals;
|
|
||||||
std::vector<aiVector3D> Tangents;
|
|
||||||
|
|
||||||
/// Arbitrary number of texcoords, they are nearly always 2d, but Assimp has always 3d texcoords, n vectors(outer) with texcoords for each vertex(inner).
|
|
||||||
std::vector<std::vector<aiVector3D> > Uvs;
|
|
||||||
|
|
||||||
/// A list(inner) of bones for each vertex(outer).
|
|
||||||
std::vector<std::vector<BoneWeight> > Weights;
|
|
||||||
|
|
||||||
/// The Index in the Assimp material array from the material witch is attached to this submesh.
|
|
||||||
int MaterialIndex;
|
|
||||||
|
|
||||||
// The highest index of a bone from a bone weight, this is needed to create the Assimp bone struct. Converting from vertex-bones to bone-vertices.
|
|
||||||
unsigned int BonesUsed;
|
|
||||||
|
|
||||||
SubMesh() :
|
|
||||||
UseSharedGeometry(false),
|
|
||||||
Use32bitIndexes(false),
|
|
||||||
HasGeometry(false),
|
|
||||||
HasPositions(false),
|
|
||||||
HasNormals(false),
|
|
||||||
HasTangents(false),
|
|
||||||
MaterialIndex(-1),
|
|
||||||
BonesUsed(0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Importer for Ogre mesh, skeleton and material formats.
|
/** Importer for Ogre mesh, skeleton and material formats.
|
||||||
@todo Support vertex colors
|
@todo Support vertex colors.
|
||||||
@todo Support multiple TexCoords (this is already done??) */
|
@todo Support poses/animations from the mesh file.
|
||||||
|
Currently only skeleton file animations are supported. */
|
||||||
class OgreImporter : public BaseImporter
|
class OgreImporter : public BaseImporter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -121,42 +73,10 @@ public:
|
||||||
virtual void SetupProperties(const Importer *pImp);
|
virtual void SetupProperties(const Importer *pImp);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
//-------------------------------- OgreMesh.cpp -------------------------------
|
|
||||||
|
|
||||||
/// Helper Functions to read parts of the XML File.
|
|
||||||
void ReadSubMesh(const unsigned int submeshIndex, SubMesh &submesh, XmlReader *reader);
|
|
||||||
|
|
||||||
/// Reads a single Vertexbuffer and writes its data in the Submesh.
|
|
||||||
static void ReadVertexBuffer(SubMesh &submesh, XmlReader *reader, const unsigned int numVertices);
|
|
||||||
|
|
||||||
/// Reads bone weights are stores them into the given submesh.
|
|
||||||
static void ReadBoneWeights(SubMesh &submesh, XmlReader *reader);
|
|
||||||
|
|
||||||
/// After Loading a SubMehs some work needs to be done (make all Vertexes unique, normalize weights).
|
|
||||||
static void ProcessSubMesh(SubMesh &submesh, SubMesh &sharedGeometry);
|
|
||||||
|
|
||||||
/// Uses the bone data to convert a SubMesh into a aiMesh which will be created and returned.
|
|
||||||
aiMesh *CreateAssimpSubMesh(aiScene *pScene, const SubMesh &submesh, const std::vector<Bone> &bones) const;
|
|
||||||
|
|
||||||
//-------------------------------- OgreSkeleton.cpp -------------------------------
|
|
||||||
|
|
||||||
/// Writes the results in Bones and Animations, Filename is not const, because its call-by-value and the function will change it!
|
|
||||||
void ReadSkeleton(const std::string &pFile, Assimp::IOSystem *pIOHandler, const aiScene *pScene,
|
|
||||||
const std::string &skeletonFile, std::vector<Bone> &Bones, std::vector<Animation> &Animations) const;
|
|
||||||
|
|
||||||
/// Converts the animations in aiAnimations and puts them into the scene.
|
|
||||||
void PutAnimationsInScene(aiScene *pScene, const std::vector<Bone> &Bones, const std::vector<Animation> &Animations);
|
|
||||||
|
|
||||||
/// Creates the aiSkeleton in current scene.
|
|
||||||
void CreateAssimpSkeleton(aiScene *pScene, const std::vector<Bone> &bones, const std::vector<Animation> &animations);
|
|
||||||
|
|
||||||
/// Recursively creates a filled aiNode from a given root bone.
|
|
||||||
static aiNode* CreateNodeFromBone(int boneId, const std::vector<Bone> &bones, aiNode *parent);
|
|
||||||
|
|
||||||
//-------------------------------- OgreMaterial.cpp -------------------------------
|
|
||||||
|
|
||||||
/// Read materials referenced by the @c mesh to @c pScene.
|
/// 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, 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
|
/// Reads material
|
||||||
aiMaterial* ReadMaterial(const std::string &pFile, Assimp::IOSystem *pIOHandler, const std::string MaterialName);
|
aiMaterial* ReadMaterial(const std::string &pFile, Assimp::IOSystem *pIOHandler, const std::string MaterialName);
|
||||||
|
@ -169,104 +89,8 @@ private:
|
||||||
std::string m_userDefinedMaterialLibFile;
|
std::string m_userDefinedMaterialLibFile;
|
||||||
bool m_detectTextureTypeFromFilename;
|
bool m_detectTextureTypeFromFilename;
|
||||||
|
|
||||||
/// VertexBuffer for the sub meshes that use shader geometry.
|
|
||||||
SubMesh m_SharedGeometry;
|
|
||||||
|
|
||||||
std::map<aiTextureType, unsigned int> m_textures;
|
std::map<aiTextureType, unsigned int> m_textures;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Simplified face.
|
|
||||||
/** @todo Support other polygon types than just just triangles. Move to using aiFace. */
|
|
||||||
struct Face
|
|
||||||
{
|
|
||||||
unsigned int VertexIndices[3];
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Ogre Bone assignment
|
|
||||||
struct BoneAssignment
|
|
||||||
{
|
|
||||||
/// Bone ID from Ogre.
|
|
||||||
unsigned int BoneId;
|
|
||||||
// Bone name for Assimp.
|
|
||||||
std::string BoneName;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Ogre Bone weight
|
|
||||||
struct BoneWeight
|
|
||||||
{
|
|
||||||
/// Bone Id
|
|
||||||
unsigned int Id;
|
|
||||||
/// BoneWeight
|
|
||||||
float Value;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/// Ogre Bone
|
|
||||||
struct Bone
|
|
||||||
{
|
|
||||||
std::string Name;
|
|
||||||
|
|
||||||
int Id;
|
|
||||||
int ParentId;
|
|
||||||
|
|
||||||
aiVector3D Position;
|
|
||||||
aiVector3D RotationAxis;
|
|
||||||
float RotationAngle;
|
|
||||||
|
|
||||||
aiMatrix4x4 BoneToWorldSpace;
|
|
||||||
|
|
||||||
std::vector<int> Children;
|
|
||||||
|
|
||||||
Bone() :
|
|
||||||
Id(-1),
|
|
||||||
ParentId(-1),
|
|
||||||
RotationAngle(0.0f)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns if this bone is parented.
|
|
||||||
bool IsParented() const { return (ParentId != -1); }
|
|
||||||
|
|
||||||
/// This operator is needed to sort the bones by Id in a vector<Bone>.
|
|
||||||
bool operator<(const Bone &other) const { return (Id < other.Id); }
|
|
||||||
|
|
||||||
/// This operator is needed to find a bone by its name in a vector<Bone>
|
|
||||||
bool operator==(const std::string& other) const { return Name == other; }
|
|
||||||
bool operator==(const aiString& other) const { return Name == std::string(other.data); }
|
|
||||||
|
|
||||||
/// @note Implemented in OgreSkeleton.cpp
|
|
||||||
void CalculateBoneToWorldSpaceMatrix(std::vector<Bone>& Bones);
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Ogre animation key frame
|
|
||||||
/** Transformations for a frame. */
|
|
||||||
struct KeyFrame
|
|
||||||
{
|
|
||||||
float Time;
|
|
||||||
aiVector3D Position;
|
|
||||||
aiQuaternion Rotation;
|
|
||||||
aiVector3D Scaling;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Ogre animation track
|
|
||||||
/** Keyframes for one bone. */
|
|
||||||
struct Track
|
|
||||||
{
|
|
||||||
std::string BoneName;
|
|
||||||
std::vector<KeyFrame> Keyframes;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Ogre animation
|
|
||||||
struct Animation
|
|
||||||
{
|
|
||||||
/// Name
|
|
||||||
std::string Name;
|
|
||||||
/// Length
|
|
||||||
float Length;
|
|
||||||
/// Tracks
|
|
||||||
std::vector<Track> Tracks;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // Ogre
|
} // Ogre
|
||||||
} // Assimp
|
} // Assimp
|
||||||
|
|
||||||
|
|
|
@ -42,12 +42,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER
|
#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
#include "OgreImporter.h"
|
#include "OgreImporter.h"
|
||||||
#include "TinyFormatter.h"
|
#include "TinyFormatter.h"
|
||||||
|
|
||||||
|
#include "fast_atof.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
namespace Assimp
|
namespace Assimp
|
||||||
|
@ -66,7 +68,7 @@ void OgreImporter::ReadMaterials(const std::string &pFile, Assimp::IOSystem *pIO
|
||||||
// Create materials that can be found and parsed via the IOSystem.
|
// Create materials that can be found and parsed via the IOSystem.
|
||||||
for (size_t i=0, len=mesh->NumSubMeshes(); i<len; ++i)
|
for (size_t i=0, len=mesh->NumSubMeshes(); i<len; ++i)
|
||||||
{
|
{
|
||||||
SubMesh2 *submesh = mesh->SubMesh(i);
|
SubMesh *submesh = mesh->GetSubMesh(i);
|
||||||
if (submesh && !submesh->materialRef.empty())
|
if (submesh && !submesh->materialRef.empty())
|
||||||
{
|
{
|
||||||
aiMaterial *material = ReadMaterial(pFile, pIOHandler, submesh->materialRef);
|
aiMaterial *material = ReadMaterial(pFile, pIOHandler, submesh->materialRef);
|
||||||
|
@ -78,7 +80,33 @@ void OgreImporter::ReadMaterials(const std::string &pFile, Assimp::IOSystem *pIO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assign material to scene
|
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(i);
|
||||||
|
if (submesh && !submesh->materialRef.empty())
|
||||||
|
{
|
||||||
|
aiMaterial *material = ReadMaterial(pFile, pIOHandler, submesh->materialRef);
|
||||||
|
if (material)
|
||||||
|
{
|
||||||
|
submesh->materialIndex = materials.size();
|
||||||
|
materials.push_back(material);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AssignMaterials(pScene, materials);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OgreImporter::AssignMaterials(aiScene *pScene, std::vector<aiMaterial*> &materials)
|
||||||
|
{
|
||||||
pScene->mNumMaterials = materials.size();
|
pScene->mNumMaterials = materials.size();
|
||||||
if (pScene->mNumMaterials > 0)
|
if (pScene->mNumMaterials > 0)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,570 +0,0 @@
|
||||||
/*
|
|
||||||
Open Asset Import Library (assimp)
|
|
||||||
----------------------------------------------------------------------
|
|
||||||
|
|
||||||
Copyright (c) 2006-2012, assimp team
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use of this software in source and binary forms,
|
|
||||||
with or without modification, are permitted provided that the
|
|
||||||
following conditions are met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above
|
|
||||||
copyright notice, this list of conditions and the
|
|
||||||
following disclaimer.
|
|
||||||
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the
|
|
||||||
following disclaimer in the documentation and/or other
|
|
||||||
materials provided with the distribution.
|
|
||||||
|
|
||||||
* Neither the name of the assimp team, nor the names of its
|
|
||||||
contributors may be used to endorse or promote products
|
|
||||||
derived from this software without specific prior
|
|
||||||
written permission of the assimp team.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "AssimpPCH.h"
|
|
||||||
|
|
||||||
#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER
|
|
||||||
|
|
||||||
#include "OgreImporter.h"
|
|
||||||
#include "TinyFormatter.h"
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
namespace Assimp
|
|
||||||
{
|
|
||||||
namespace Ogre
|
|
||||||
{
|
|
||||||
|
|
||||||
void OgreImporter::ReadSubMesh(const unsigned int submeshIndex, SubMesh &submesh, XmlReader *reader)
|
|
||||||
{
|
|
||||||
if (reader->getAttributeValue("material")) {
|
|
||||||
submesh.MaterialName = GetAttribute<string>(reader, "material");
|
|
||||||
}
|
|
||||||
if (reader->getAttributeValue("use32bitindexes")) {
|
|
||||||
submesh.Use32bitIndexes = GetAttribute<bool>(reader, "use32bitindexes");
|
|
||||||
}
|
|
||||||
if (reader->getAttributeValue("usesharedvertices")) {
|
|
||||||
submesh.UseSharedGeometry = GetAttribute<bool>(reader, "usesharedvertices");
|
|
||||||
}
|
|
||||||
|
|
||||||
DefaultLogger::get()->debug(Formatter::format() << "Reading submesh " << submeshIndex);
|
|
||||||
DefaultLogger::get()->debug(Formatter::format() << " - Material '" << submesh.MaterialName << "'");
|
|
||||||
DefaultLogger::get()->debug(Formatter::format() << " - Shader geometry = " << (submesh.UseSharedGeometry ? "true" : "false") <<
|
|
||||||
", 32bit indexes = " << (submesh.Use32bitIndexes ? "true" : "false"));
|
|
||||||
|
|
||||||
//TODO: maybe we have alsways 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
|
|
||||||
|
|
||||||
NextNode(reader);
|
|
||||||
string currentNodeName = reader->getNodeName();
|
|
||||||
|
|
||||||
const string nnFaces = "faces";
|
|
||||||
const string nnFace = "face";
|
|
||||||
const string nnGeometry = "geometry";
|
|
||||||
const string nnBoneAssignments = "boneassignments";
|
|
||||||
const string nnVertexBuffer = "vertexbuffer";
|
|
||||||
|
|
||||||
bool quadWarned = false;
|
|
||||||
|
|
||||||
while(currentNodeName == nnFaces ||
|
|
||||||
currentNodeName == nnGeometry ||
|
|
||||||
currentNodeName == nnBoneAssignments)
|
|
||||||
{
|
|
||||||
if (currentNodeName == nnFaces)
|
|
||||||
{
|
|
||||||
unsigned int numFaces = GetAttribute<unsigned int>(reader, "count");
|
|
||||||
|
|
||||||
NextNode(reader);
|
|
||||||
currentNodeName = reader->getNodeName();
|
|
||||||
|
|
||||||
while(currentNodeName == nnFace)
|
|
||||||
{
|
|
||||||
Face NewFace;
|
|
||||||
NewFace.VertexIndices[0] = GetAttribute<int>(reader, "v1");
|
|
||||||
NewFace.VertexIndices[1] = GetAttribute<int>(reader, "v2");
|
|
||||||
NewFace.VertexIndices[2] = GetAttribute<int>(reader, "v3");
|
|
||||||
|
|
||||||
/// @todo Support quads
|
|
||||||
if (!quadWarned && reader->getAttributeValue("v4")) {
|
|
||||||
DefaultLogger::get()->warn("Submesh has quads, only triangles are supported at the moment!");
|
|
||||||
}
|
|
||||||
|
|
||||||
submesh.Faces.push_back(NewFace);
|
|
||||||
|
|
||||||
// Advance
|
|
||||||
NextNode(reader);
|
|
||||||
currentNodeName = reader->getNodeName();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (submesh.Faces.size() == numFaces)
|
|
||||||
{
|
|
||||||
DefaultLogger::get()->debug(Formatter::format() << " - Faces " << numFaces);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw DeadlyImportError(Formatter::format() << "Read only " << submesh.Faces.size() << " faces when should have read " << numFaces);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (currentNodeName == nnGeometry)
|
|
||||||
{
|
|
||||||
unsigned int numVertices = GetAttribute<int>(reader, "vertexcount");
|
|
||||||
|
|
||||||
NextNode(reader);
|
|
||||||
while(string(reader->getNodeName()) == nnVertexBuffer) {
|
|
||||||
ReadVertexBuffer(submesh, reader, numVertices);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (reader->getNodeName() == nnBoneAssignments)
|
|
||||||
{
|
|
||||||
ReadBoneWeights(submesh, reader);
|
|
||||||
}
|
|
||||||
|
|
||||||
currentNodeName = reader->getNodeName();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void OgreImporter::ReadVertexBuffer(SubMesh &submesh, XmlReader *reader, const unsigned int numVertices)
|
|
||||||
{
|
|
||||||
DefaultLogger::get()->debug(Formatter::format() << "Reading vertex buffer with " << numVertices << " vertices");
|
|
||||||
|
|
||||||
submesh.HasGeometry = true;
|
|
||||||
|
|
||||||
if (reader->getAttributeValue("positions") && GetAttribute<bool>(reader, "positions"))
|
|
||||||
{
|
|
||||||
submesh.HasPositions = true;
|
|
||||||
submesh.Positions.reserve(numVertices);
|
|
||||||
DefaultLogger::get()->debug(" - Has positions");
|
|
||||||
}
|
|
||||||
if (reader->getAttributeValue("normals") && GetAttribute<bool>(reader, "normals"))
|
|
||||||
{
|
|
||||||
submesh.HasNormals = true;
|
|
||||||
submesh.Normals.reserve(numVertices);
|
|
||||||
DefaultLogger::get()->debug(" - Has normals");
|
|
||||||
}
|
|
||||||
if (reader->getAttributeValue("tangents") && GetAttribute<bool>(reader, "tangents"))
|
|
||||||
{
|
|
||||||
submesh.HasTangents = true;
|
|
||||||
submesh.Tangents.reserve(numVertices);
|
|
||||||
DefaultLogger::get()->debug(" - Has tangents");
|
|
||||||
}
|
|
||||||
if (reader->getAttributeValue("texture_coords"))
|
|
||||||
{
|
|
||||||
submesh.Uvs.resize(GetAttribute<unsigned int>(reader, "texture_coords"));
|
|
||||||
for(size_t i=0, len=submesh.Uvs.size(); i<len; ++i) {
|
|
||||||
submesh.Uvs[i].reserve(numVertices);
|
|
||||||
}
|
|
||||||
DefaultLogger::get()->debug(Formatter::format() << " - Has " << submesh.Uvs.size() << " texture coords");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!submesh.HasPositions) {
|
|
||||||
throw DeadlyImportError("Vertex buffer does not contain positions!");
|
|
||||||
}
|
|
||||||
|
|
||||||
const string nnVertex = "vertex";
|
|
||||||
const string nnPosition = "position";
|
|
||||||
const string nnNormal = "normal";
|
|
||||||
const string nnTangent = "tangent";
|
|
||||||
const string nnBinormal = "binormal";
|
|
||||||
const string nnTexCoord = "texcoord";
|
|
||||||
const string nnColorDiffuse = "colour_diffuse";
|
|
||||||
const string nnColorSpecular = "colour_specular";
|
|
||||||
|
|
||||||
bool warnBinormal = true;
|
|
||||||
bool warnColorDiffuse = true;
|
|
||||||
bool warnColorSpecular = true;
|
|
||||||
|
|
||||||
NextNode(reader);
|
|
||||||
string currentNodeName = reader->getNodeName();
|
|
||||||
|
|
||||||
/// @todo Make this loop nicer.
|
|
||||||
while(currentNodeName == nnVertex ||
|
|
||||||
currentNodeName == nnPosition ||
|
|
||||||
currentNodeName == nnNormal ||
|
|
||||||
currentNodeName == nnTangent ||
|
|
||||||
currentNodeName == nnBinormal ||
|
|
||||||
currentNodeName == nnTexCoord ||
|
|
||||||
currentNodeName == nnColorDiffuse ||
|
|
||||||
currentNodeName == nnColorSpecular)
|
|
||||||
{
|
|
||||||
if (currentNodeName == nnVertex)
|
|
||||||
{
|
|
||||||
NextNode(reader);
|
|
||||||
currentNodeName = reader->getNodeName();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @todo Implement nnBinormal, nnColorDiffuse and nnColorSpecular
|
|
||||||
|
|
||||||
if (submesh.HasPositions && currentNodeName == nnPosition)
|
|
||||||
{
|
|
||||||
aiVector3D NewPos;
|
|
||||||
NewPos.x = GetAttribute<float>(reader, "x");
|
|
||||||
NewPos.y = GetAttribute<float>(reader, "y");
|
|
||||||
NewPos.z = GetAttribute<float>(reader, "z");
|
|
||||||
submesh.Positions.push_back(NewPos);
|
|
||||||
}
|
|
||||||
else if (submesh.HasNormals && currentNodeName == nnNormal)
|
|
||||||
{
|
|
||||||
aiVector3D NewNormal;
|
|
||||||
NewNormal.x = GetAttribute<float>(reader, "x");
|
|
||||||
NewNormal.y = GetAttribute<float>(reader, "y");
|
|
||||||
NewNormal.z = GetAttribute<float>(reader, "z");
|
|
||||||
submesh.Normals.push_back(NewNormal);
|
|
||||||
}
|
|
||||||
else if (submesh.HasTangents && currentNodeName == nnTangent)
|
|
||||||
{
|
|
||||||
aiVector3D NewTangent;
|
|
||||||
NewTangent.x = GetAttribute<float>(reader, "x");
|
|
||||||
NewTangent.y = GetAttribute<float>(reader, "y");
|
|
||||||
NewTangent.z = GetAttribute<float>(reader, "z");
|
|
||||||
submesh.Tangents.push_back(NewTangent);
|
|
||||||
}
|
|
||||||
else if (submesh.Uvs.size() > 0 && currentNodeName == nnTexCoord)
|
|
||||||
{
|
|
||||||
for(size_t i=0, len=submesh.Uvs.size(); i<len; ++i)
|
|
||||||
{
|
|
||||||
if (currentNodeName != nnTexCoord) {
|
|
||||||
throw DeadlyImportError("Vertex buffer declared more UVs than can be found in a vertex");
|
|
||||||
}
|
|
||||||
|
|
||||||
aiVector3D NewUv;
|
|
||||||
NewUv.x = GetAttribute<float>(reader, "u");
|
|
||||||
NewUv.y = GetAttribute<float>(reader, "v") * (-1)+1; //flip the uv vertikal, blender exports them so! (ahem... @todo ????)
|
|
||||||
submesh.Uvs[i].push_back(NewUv);
|
|
||||||
|
|
||||||
NextNode(reader);
|
|
||||||
currentNodeName = reader->getNodeName();
|
|
||||||
}
|
|
||||||
// Continue main loop as above already read next node
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/// @todo Remove this stuff once implemented. We only want to log warnings once per element.
|
|
||||||
bool warn = true;
|
|
||||||
if (currentNodeName == nnBinormal)
|
|
||||||
{
|
|
||||||
if (warnBinormal)
|
|
||||||
{
|
|
||||||
warnBinormal = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
warn = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (currentNodeName == nnColorDiffuse)
|
|
||||||
{
|
|
||||||
if (warnColorDiffuse)
|
|
||||||
{
|
|
||||||
warnColorDiffuse = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
warn = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (currentNodeName == nnColorSpecular)
|
|
||||||
{
|
|
||||||
if (warnColorSpecular)
|
|
||||||
{
|
|
||||||
warnColorSpecular = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
warn = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (warn) {
|
|
||||||
DefaultLogger::get()->warn(string("Vertex buffer attribute read not implemented for element: ") + currentNodeName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Advance
|
|
||||||
NextNode(reader);
|
|
||||||
currentNodeName = reader->getNodeName();
|
|
||||||
}
|
|
||||||
|
|
||||||
DefaultLogger::get()->debug(Formatter::format() <<
|
|
||||||
" - Positions " << submesh.Positions.size() <<
|
|
||||||
" Normals " << submesh.Normals.size() <<
|
|
||||||
" TexCoords " << submesh.Uvs.size() <<
|
|
||||||
" Tangents " << submesh.Tangents.size());
|
|
||||||
|
|
||||||
// Sanity checks
|
|
||||||
if (submesh.HasNormals && submesh.Normals.size() != numVertices) {
|
|
||||||
throw DeadlyImportError(Formatter::format() << "Read only " << submesh.Normals.size() << " normals when should have read " << numVertices);
|
|
||||||
}
|
|
||||||
if (submesh.HasTangents && submesh.Tangents.size() != numVertices) {
|
|
||||||
throw DeadlyImportError(Formatter::format() << "Read only " << submesh.Tangents.size() << " tangents when should have read " << numVertices);
|
|
||||||
}
|
|
||||||
for(unsigned int i=0; i<submesh.Uvs.size(); ++i)
|
|
||||||
{
|
|
||||||
if (submesh.Uvs[i].size() != numVertices) {
|
|
||||||
throw DeadlyImportError(Formatter::format() << "Read only " << submesh.Uvs[i].size()
|
|
||||||
<< " uvs for uv index " << i << " when should have read " << numVertices);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void OgreImporter::ReadBoneWeights(SubMesh &submesh, XmlReader *reader)
|
|
||||||
{
|
|
||||||
submesh.Weights.resize(submesh.Positions.size());
|
|
||||||
|
|
||||||
unsigned int numRead = 0;
|
|
||||||
const string nnVertexBoneAssignment = "vertexboneassignment";
|
|
||||||
|
|
||||||
NextNode(reader);
|
|
||||||
while(CurrentNodeNameEquals(reader, nnVertexBoneAssignment))
|
|
||||||
{
|
|
||||||
numRead++;
|
|
||||||
|
|
||||||
BoneWeight weight;
|
|
||||||
weight.Id = GetAttribute<int>(reader, "boneindex");
|
|
||||||
weight.Value = GetAttribute<float>(reader, "weight");
|
|
||||||
|
|
||||||
//calculate the number of bones used (this is the highest id +1 becuase bone ids start at 0)
|
|
||||||
/// @todo This can probably be refactored to something else.
|
|
||||||
submesh.BonesUsed = max(submesh.BonesUsed, weight.Id+1);
|
|
||||||
|
|
||||||
const unsigned int vertexId = GetAttribute<int>(reader, "vertexindex");
|
|
||||||
submesh.Weights[vertexId].push_back(weight);
|
|
||||||
|
|
||||||
NextNode(reader);
|
|
||||||
}
|
|
||||||
DefaultLogger::get()->debug(Formatter::format() << " - Bone weights " << numRead);
|
|
||||||
}
|
|
||||||
|
|
||||||
void OgreImporter::ProcessSubMesh(SubMesh &submesh, SubMesh &sharedGeometry)
|
|
||||||
{
|
|
||||||
// Make all vertexes unique. Required by Assimp.
|
|
||||||
vector<Face> uniqueFaceList(submesh.Faces.size());
|
|
||||||
unsigned int uniqueVertexCount = submesh.Faces.size() * 3;
|
|
||||||
|
|
||||||
vector<aiVector3D> uniquePositions(uniqueVertexCount);
|
|
||||||
vector<aiVector3D> uniqueNormals(uniqueVertexCount);
|
|
||||||
vector<aiVector3D> uniqueTangents(uniqueVertexCount);
|
|
||||||
|
|
||||||
vector<vector<BoneWeight> > uniqueWeights(uniqueVertexCount);
|
|
||||||
vector<vector<aiVector3D> > uniqueUvs(submesh.UseSharedGeometry ? sharedGeometry.Uvs.size() : submesh.Uvs.size());
|
|
||||||
|
|
||||||
for(size_t uvi=0; uvi<uniqueUvs.size(); ++uvi) {
|
|
||||||
uniqueUvs[uvi].resize(uniqueVertexCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Support for shared geometry.
|
|
||||||
We can use this loop to copy vertex informations from the shared data pool. In order to do so
|
|
||||||
we just use a reference to a submodel instead of our submodel itself */
|
|
||||||
SubMesh &vertexSource = (submesh.UseSharedGeometry ? sharedGeometry : submesh);
|
|
||||||
if (submesh.UseSharedGeometry)
|
|
||||||
{
|
|
||||||
submesh.HasPositions = sharedGeometry.HasPositions;
|
|
||||||
submesh.HasNormals = sharedGeometry.HasNormals;
|
|
||||||
submesh.HasTangents = sharedGeometry.HasTangents;
|
|
||||||
submesh.BonesUsed = sharedGeometry.BonesUsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i=0, flen=submesh.Faces.size(); i<flen; ++i)
|
|
||||||
{
|
|
||||||
const Face &face = submesh.Faces[i];
|
|
||||||
|
|
||||||
// We pre calculate the index values here,
|
|
||||||
// because we need them in all vertex attributes.
|
|
||||||
unsigned int v1 = face.VertexIndices[0];
|
|
||||||
unsigned int v2 = face.VertexIndices[1];
|
|
||||||
unsigned int v3 = face.VertexIndices[2];
|
|
||||||
|
|
||||||
size_t pos = i*3;
|
|
||||||
|
|
||||||
uniqueFaceList[i].VertexIndices[0] = pos;
|
|
||||||
uniqueFaceList[i].VertexIndices[1] = pos + 1;
|
|
||||||
uniqueFaceList[i].VertexIndices[2] = pos + 2;
|
|
||||||
|
|
||||||
uniquePositions[pos] = vertexSource.Positions[v1];
|
|
||||||
uniquePositions[pos+1] = vertexSource.Positions[v2];
|
|
||||||
uniquePositions[pos+2] = vertexSource.Positions[v3];
|
|
||||||
|
|
||||||
if (vertexSource.HasNormals)
|
|
||||||
{
|
|
||||||
uniqueNormals[pos ] = vertexSource.Normals[v1];
|
|
||||||
uniqueNormals[pos+1] = vertexSource.Normals[v2];
|
|
||||||
uniqueNormals[pos+2] = vertexSource.Normals[v3];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vertexSource.HasTangents)
|
|
||||||
{
|
|
||||||
uniqueTangents[pos] = vertexSource.Tangents[v1];
|
|
||||||
uniqueTangents[pos+1] = vertexSource.Tangents[v2];
|
|
||||||
uniqueTangents[pos+2] = vertexSource.Tangents[v3];
|
|
||||||
}
|
|
||||||
|
|
||||||
for(size_t uvi=0; uvi<uniqueUvs.size(); ++uvi)
|
|
||||||
{
|
|
||||||
const std::vector<aiVector3D> &uv = vertexSource.Uvs[uvi];
|
|
||||||
uniqueUvs[uvi][pos] = uv[v1];
|
|
||||||
uniqueUvs[uvi][pos+1] = uv[v2];
|
|
||||||
uniqueUvs[uvi][pos+2] = uv[v3];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!vertexSource.Weights.empty())
|
|
||||||
{
|
|
||||||
uniqueWeights[pos] = vertexSource.Weights[v1];
|
|
||||||
uniqueWeights[pos+1] = vertexSource.Weights[v2];
|
|
||||||
uniqueWeights[pos+2] = vertexSource.Weights[v3];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now we have the unique data, but want them in the SubMesh, so we swap all the containers.
|
|
||||||
// If we don't have one of them, we just swap empty containers, so everything is ok.
|
|
||||||
submesh.Faces.swap(uniqueFaceList);
|
|
||||||
submesh.Positions.swap(uniquePositions);
|
|
||||||
submesh.Normals.swap(uniqueNormals);
|
|
||||||
submesh.Tangents.swap(uniqueTangents);
|
|
||||||
submesh.Uvs.swap(uniqueUvs);
|
|
||||||
submesh.Weights.swap(uniqueWeights);
|
|
||||||
|
|
||||||
// Normalize bone weights
|
|
||||||
// For example the Blender exporter doesn't care about whether the sum of all bone
|
|
||||||
// weights for a single vertex equals 1 or not, so validate here.
|
|
||||||
for(size_t vertexId=0, wlen=submesh.Weights.size(); vertexId<wlen; ++vertexId)
|
|
||||||
{
|
|
||||||
std::vector<BoneWeight> &weights = submesh.Weights[vertexId];
|
|
||||||
|
|
||||||
float sum = 0.0f;
|
|
||||||
for(size_t boneId=0, blen=weights.size(); boneId<blen; ++boneId) {
|
|
||||||
sum += weights[boneId].Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
//check if the sum is too far away from 1
|
|
||||||
if ((sum < (1.0f - 0.05f)) || (sum > (1.0f + 0.05f)))
|
|
||||||
{
|
|
||||||
for(size_t boneId=0, blen=weights.size(); boneId<blen; ++boneId) {
|
|
||||||
weights[boneId].Value /= sum;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
aiMesh *OgreImporter::CreateAssimpSubMesh(aiScene *pScene, const SubMesh& submesh, const vector<Bone>& bones) const
|
|
||||||
{
|
|
||||||
const size_t sizeVector3D = sizeof(aiVector3D);
|
|
||||||
|
|
||||||
aiMesh *dest = new aiMesh();
|
|
||||||
|
|
||||||
// Material
|
|
||||||
if (submesh.MaterialIndex != -1)
|
|
||||||
dest->mMaterialIndex = submesh.MaterialIndex;
|
|
||||||
|
|
||||||
// Positions
|
|
||||||
dest->mVertices = new aiVector3D[submesh.Positions.size()];
|
|
||||||
dest->mNumVertices = submesh.Positions.size();
|
|
||||||
memcpy(dest->mVertices, &submesh.Positions[0], submesh.Positions.size() * sizeVector3D);
|
|
||||||
|
|
||||||
// Normals
|
|
||||||
if (submesh.HasNormals)
|
|
||||||
{
|
|
||||||
dest->mNormals = new aiVector3D[submesh.Normals.size()];
|
|
||||||
memcpy(dest->mNormals, &submesh.Normals[0], submesh.Normals.size() * sizeVector3D);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tangents
|
|
||||||
// Until we have support for bitangents, no tangents will be written
|
|
||||||
/// @todo Investigate why the above?
|
|
||||||
if (submesh.HasTangents)
|
|
||||||
{
|
|
||||||
DefaultLogger::get()->warn("Tangents found from Ogre mesh but writing to Assimp mesh not yet supported!");
|
|
||||||
//dest->mTangents = new aiVector3D[submesh.Tangents.size()];
|
|
||||||
//memcpy(dest->mTangents, &submesh.Tangents[0], submesh.Tangents.size() * sizeVector3D);
|
|
||||||
}
|
|
||||||
|
|
||||||
// UVs
|
|
||||||
for (size_t i=0, len=submesh.Uvs.size(); i<len; ++i)
|
|
||||||
{
|
|
||||||
dest->mNumUVComponents[i] = 2;
|
|
||||||
dest->mTextureCoords[i] = new aiVector3D[submesh.Uvs[i].size()];
|
|
||||||
memcpy(dest->mTextureCoords[i], &(submesh.Uvs[i][0]), submesh.Uvs[i].size() * sizeVector3D);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bone weights. Convert internal vertex-to-bone mapping to bone-to-vertex.
|
|
||||||
vector<vector<aiVertexWeight> > assimpWeights(submesh.BonesUsed);
|
|
||||||
for(size_t vertexId=0, len=submesh.Weights.size(); vertexId<len; ++vertexId)
|
|
||||||
{
|
|
||||||
const vector<BoneWeight> &vertexWeights = submesh.Weights[vertexId];
|
|
||||||
for (size_t boneId=0, len=vertexWeights.size(); boneId<len; ++boneId)
|
|
||||||
{
|
|
||||||
const BoneWeight &ogreWeight = vertexWeights[boneId];
|
|
||||||
assimpWeights[ogreWeight.Id].push_back(aiVertexWeight(vertexId, ogreWeight.Value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bones.
|
|
||||||
vector<aiBone*> assimpBones;
|
|
||||||
assimpBones.reserve(submesh.BonesUsed);
|
|
||||||
|
|
||||||
for(size_t boneId=0, len=submesh.BonesUsed; boneId<len; ++boneId)
|
|
||||||
{
|
|
||||||
const vector<aiVertexWeight> &boneWeights = assimpWeights[boneId];
|
|
||||||
if (boneWeights.size() == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// @note The bones list is sorted by id's, this was done in LoadSkeleton.
|
|
||||||
aiBone *assimpBone = new aiBone();
|
|
||||||
assimpBone->mName = bones[boneId].Name;
|
|
||||||
assimpBone->mOffsetMatrix = bones[boneId].BoneToWorldSpace;
|
|
||||||
assimpBone->mNumWeights = boneWeights.size();
|
|
||||||
assimpBone->mWeights = new aiVertexWeight[boneWeights.size()];
|
|
||||||
memcpy(assimpBone->mWeights, &boneWeights[0], boneWeights.size() * sizeof(aiVertexWeight));
|
|
||||||
|
|
||||||
assimpBones.push_back(assimpBone);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!assimpBones.empty())
|
|
||||||
{
|
|
||||||
dest->mBones = new aiBone*[assimpBones.size()];
|
|
||||||
dest->mNumBones = assimpBones.size();
|
|
||||||
|
|
||||||
for(size_t i=0, len=assimpBones.size(); i<len; ++i) {
|
|
||||||
dest->mBones[i] = assimpBones[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Faces
|
|
||||||
dest->mFaces = new aiFace[submesh.Faces.size()];
|
|
||||||
dest->mNumFaces = submesh.Faces.size();
|
|
||||||
|
|
||||||
for(size_t i=0, len=submesh.Faces.size(); i<len; ++i)
|
|
||||||
{
|
|
||||||
dest->mFaces[i].mNumIndices = 3;
|
|
||||||
dest->mFaces[i].mIndices = new unsigned int[3];
|
|
||||||
|
|
||||||
const Face &f = submesh.Faces[i];
|
|
||||||
dest->mFaces[i].mIndices[0] = f.VertexIndices[0];
|
|
||||||
dest->mFaces[i].mIndices[1] = f.VertexIndices[1];
|
|
||||||
dest->mFaces[i].mIndices[2] = f.VertexIndices[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
return dest;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // Ogre
|
|
||||||
} // Assimp
|
|
||||||
|
|
||||||
#endif // ASSIMP_BUILD_NO_OGRE_IMPORTER
|
|
|
@ -44,9 +44,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER
|
#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER
|
||||||
|
|
||||||
#include "ParsingUtils.h"
|
#include "ParsingUtils.h"
|
||||||
#include "irrXMLWrapper.h"
|
|
||||||
#include "fast_atof.h"
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
namespace Assimp
|
namespace Assimp
|
||||||
{
|
{
|
||||||
namespace Ogre
|
namespace Ogre
|
||||||
|
@ -116,119 +115,6 @@ static inline std::string &Trim(std::string &s, bool newlines = true)
|
||||||
return TrimLeft(TrimRight(s, newlines), newlines);
|
return TrimLeft(TrimRight(s, newlines), newlines);
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef irr::io::IrrXMLReader XmlReader;
|
|
||||||
|
|
||||||
static void ThrowAttibuteError(const XmlReader* reader, const std::string &name, const std::string &error = "")
|
|
||||||
{
|
|
||||||
if (!error.empty())
|
|
||||||
{
|
|
||||||
throw DeadlyImportError(error + " in node '" + std::string(reader->getNodeName()) + "' and attribute '" + name + "'");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw DeadlyImportError("Attribute '" + name + "' does not exist in node '" + std::string(reader->getNodeName()) + "'");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
inline T GetAttribute(const XmlReader* reader, const std::string &name);
|
|
||||||
|
|
||||||
template<>
|
|
||||||
inline int GetAttribute<int>(const XmlReader* reader, const std::string &name)
|
|
||||||
{
|
|
||||||
const char* value = reader->getAttributeValue(name.c_str());
|
|
||||||
if (value)
|
|
||||||
{
|
|
||||||
return atoi(value);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ThrowAttibuteError(reader, name);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<>
|
|
||||||
inline unsigned int GetAttribute<unsigned int>(const XmlReader* reader, const std::string &name)
|
|
||||||
{
|
|
||||||
const char* value = reader->getAttributeValue(name.c_str());
|
|
||||||
if (value)
|
|
||||||
{
|
|
||||||
return static_cast<unsigned int>(atoi(value)); ///< @todo Find a better way...
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ThrowAttibuteError(reader, name);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<>
|
|
||||||
inline float GetAttribute<float>(const XmlReader* reader, const std::string &name)
|
|
||||||
{
|
|
||||||
const char* value = reader->getAttributeValue(name.c_str());
|
|
||||||
if (value)
|
|
||||||
{
|
|
||||||
return fast_atof(value);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ThrowAttibuteError(reader, name);
|
|
||||||
return 0.f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<>
|
|
||||||
inline std::string GetAttribute<std::string>(const XmlReader* reader, const std::string &name)
|
|
||||||
{
|
|
||||||
const char* value = reader->getAttributeValue(name.c_str());
|
|
||||||
if (value)
|
|
||||||
{
|
|
||||||
return std::string(value);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ThrowAttibuteError(reader, name);
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<>
|
|
||||||
inline bool GetAttribute<bool>(const XmlReader* reader, const std::string &name)
|
|
||||||
{
|
|
||||||
std::string value = Ogre::ToLower(GetAttribute<std::string>(reader, name));
|
|
||||||
if (ASSIMP_stricmp(value, "true") == 0)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if (ASSIMP_stricmp(value, "false") == 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ThrowAttibuteError(reader, name, "Boolean value is expected to be 'true' or 'false', encountered '" + value + "'");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool NextNode(XmlReader* reader)
|
|
||||||
{
|
|
||||||
do
|
|
||||||
{
|
|
||||||
if (!reader->read()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while(reader->getNodeType() != irr::io::EXN_ELEMENT);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool CurrentNodeNameEquals(const XmlReader* reader, const std::string &name)
|
|
||||||
{
|
|
||||||
return (ASSIMP_stricmp(std::string(reader->getNodeName()), name) == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Skips a line from current @ss position until a newline. Returns the skipped part.
|
/// Skips a line from current @ss position until a newline. Returns the skipped part.
|
||||||
static inline std::string SkipLine(std::stringstream &ss)
|
static inline std::string SkipLine(std::stringstream &ss)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,446 +0,0 @@
|
||||||
/*
|
|
||||||
Open Asset Import Library (assimp)
|
|
||||||
----------------------------------------------------------------------
|
|
||||||
|
|
||||||
Copyright (c) 2006-2012, assimp team
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use of this software in source and binary forms,
|
|
||||||
with or without modification, are permitted provided that the
|
|
||||||
following conditions are met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above
|
|
||||||
copyright notice, this list of conditions and the
|
|
||||||
following disclaimer.
|
|
||||||
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the
|
|
||||||
following disclaimer in the documentation and/or other
|
|
||||||
materials provided with the distribution.
|
|
||||||
|
|
||||||
* Neither the name of the assimp team, nor the names of its
|
|
||||||
contributors may be used to endorse or promote products
|
|
||||||
derived from this software without specific prior
|
|
||||||
written permission of the assimp team.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "AssimpPCH.h"
|
|
||||||
|
|
||||||
#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER
|
|
||||||
|
|
||||||
#include "OgreImporter.h"
|
|
||||||
#include "TinyFormatter.h"
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
namespace Assimp
|
|
||||||
{
|
|
||||||
namespace Ogre
|
|
||||||
{
|
|
||||||
|
|
||||||
void OgreImporter::ReadSkeleton(const std::string &pFile, Assimp::IOSystem *pIOHandler, const aiScene *pScene,
|
|
||||||
const std::string &skeletonFile, vector<Bone> &Bones, vector<Animation> &Animations) const
|
|
||||||
{
|
|
||||||
string filename = skeletonFile;
|
|
||||||
if (EndsWith(filename, ".skeleton"))
|
|
||||||
{
|
|
||||||
DefaultLogger::get()->warn("Mesh is referencing a Ogre binary skeleton. Parsing binary Ogre assets is not supported at the moment. Trying to find .skeleton.xml file instead.");
|
|
||||||
filename += ".xml";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pIOHandler->Exists(filename))
|
|
||||||
{
|
|
||||||
DefaultLogger::get()->error("Failed to find skeleton file '" + filename + "', skeleton will be missing.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
boost::scoped_ptr<IOStream> file(pIOHandler->Open(filename));
|
|
||||||
if (!file.get()) {
|
|
||||||
throw DeadlyImportError("Failed to open skeleton file " + filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
boost::scoped_ptr<CIrrXML_IOStreamReader> stream(new CIrrXML_IOStreamReader(file.get()));
|
|
||||||
XmlReader* reader = irr::io::createIrrXMLReader(stream.get());
|
|
||||||
if (!reader) {
|
|
||||||
throw DeadlyImportError("Failed to create XML reader for skeleton file " + filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
DefaultLogger::get()->debug("Reading skeleton '" + filename + "'");
|
|
||||||
|
|
||||||
// Root
|
|
||||||
NextNode(reader);
|
|
||||||
if (!CurrentNodeNameEquals(reader, "skeleton")) {
|
|
||||||
throw DeadlyImportError("Root node is not <skeleton> but <" + string(reader->getNodeName()) + "> in " + filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bones
|
|
||||||
NextNode(reader);
|
|
||||||
if (!CurrentNodeNameEquals(reader, "bones")) {
|
|
||||||
throw DeadlyImportError("No <bones> node in skeleton " + skeletonFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
NextNode(reader);
|
|
||||||
while(CurrentNodeNameEquals(reader, "bone"))
|
|
||||||
{
|
|
||||||
/** @todo Fix this mandatory ordering. Some exporters might just write rotation first etc.
|
|
||||||
There is no technical reason this has to be so strict. */
|
|
||||||
|
|
||||||
Bone bone;
|
|
||||||
bone.Id = GetAttribute<int>(reader, "id");
|
|
||||||
bone.Name = GetAttribute<string>(reader, "name");
|
|
||||||
|
|
||||||
NextNode(reader);
|
|
||||||
if (!CurrentNodeNameEquals(reader, "position")) {
|
|
||||||
throw DeadlyImportError("Position is not first node in Bone!");
|
|
||||||
}
|
|
||||||
|
|
||||||
bone.Position.x = GetAttribute<float>(reader, "x");
|
|
||||||
bone.Position.y = GetAttribute<float>(reader, "y");
|
|
||||||
bone.Position.z = GetAttribute<float>(reader, "z");
|
|
||||||
|
|
||||||
NextNode(reader);
|
|
||||||
if (!CurrentNodeNameEquals(reader, "rotation")) {
|
|
||||||
throw DeadlyImportError("Rotation is not the second node in Bone!");
|
|
||||||
}
|
|
||||||
|
|
||||||
bone.RotationAngle = GetAttribute<float>(reader, "angle");
|
|
||||||
|
|
||||||
NextNode(reader);
|
|
||||||
if (!CurrentNodeNameEquals(reader, "axis")) {
|
|
||||||
throw DeadlyImportError("No axis specified for bone rotation!");
|
|
||||||
}
|
|
||||||
|
|
||||||
bone.RotationAxis.x = GetAttribute<float>(reader, "x");
|
|
||||||
bone.RotationAxis.y = GetAttribute<float>(reader, "y");
|
|
||||||
bone.RotationAxis.z = GetAttribute<float>(reader, "z");
|
|
||||||
|
|
||||||
Bones.push_back(bone);
|
|
||||||
|
|
||||||
NextNode(reader);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Order bones by Id
|
|
||||||
std::sort(Bones.begin(), Bones.end());
|
|
||||||
|
|
||||||
// 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=Bones.size(); i<len; ++i)
|
|
||||||
{
|
|
||||||
if (static_cast<int>(Bones[i].Id) != static_cast<int>(i)) {
|
|
||||||
throw DeadlyImportError("Bone Ids are not in sequence in " + skeletonFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DefaultLogger::get()->debug(Formatter::format() << " - Bones " << Bones.size());
|
|
||||||
|
|
||||||
// Bone hierarchy
|
|
||||||
if (!CurrentNodeNameEquals(reader, "bonehierarchy")) {
|
|
||||||
throw DeadlyImportError("No <bonehierarchy> node found after <bones> in " + skeletonFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
NextNode(reader);
|
|
||||||
while(CurrentNodeNameEquals(reader, "boneparent"))
|
|
||||||
{
|
|
||||||
string childName = GetAttribute<string>(reader, "bone");
|
|
||||||
string parentName = GetAttribute<string>(reader, "parent");
|
|
||||||
|
|
||||||
vector<Bone>::iterator iterChild = find(Bones.begin(), Bones.end(), childName);
|
|
||||||
vector<Bone>::iterator iterParent = find(Bones.begin(), Bones.end(), parentName);
|
|
||||||
|
|
||||||
if (iterChild != Bones.end() && iterParent != Bones.end())
|
|
||||||
{
|
|
||||||
iterChild->ParentId = iterParent->Id;
|
|
||||||
iterParent->Children.push_back(iterChild->Id);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DefaultLogger::get()->warn("Failed to find bones for parenting: Child " + childName + " Parent " + parentName);
|
|
||||||
}
|
|
||||||
|
|
||||||
NextNode(reader);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate bone matrices for root bones. Recursively does their children.
|
|
||||||
BOOST_FOREACH(Bone &theBone, Bones)
|
|
||||||
{
|
|
||||||
if (!theBone.IsParented()) {
|
|
||||||
theBone.CalculateBoneToWorldSpaceMatrix(Bones);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
aiVector3D zeroVec(0.f, 0.f, 0.f);
|
|
||||||
|
|
||||||
// Animations
|
|
||||||
if (CurrentNodeNameEquals(reader, "animations"))
|
|
||||||
{
|
|
||||||
DefaultLogger::get()->debug(" - Animations");
|
|
||||||
|
|
||||||
NextNode(reader);
|
|
||||||
while(CurrentNodeNameEquals(reader, "animation"))
|
|
||||||
{
|
|
||||||
Animation animation;
|
|
||||||
animation.Name = GetAttribute<string>(reader, "name");
|
|
||||||
animation.Length = GetAttribute<float>(reader, "length");
|
|
||||||
|
|
||||||
// Tracks
|
|
||||||
NextNode(reader);
|
|
||||||
if (!CurrentNodeNameEquals(reader, "tracks")) {
|
|
||||||
throw DeadlyImportError("No <tracks> node found in animation '" + animation.Name + "' in " + skeletonFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
NextNode(reader);
|
|
||||||
while(CurrentNodeNameEquals(reader, "track"))
|
|
||||||
{
|
|
||||||
Track track;
|
|
||||||
track.BoneName = GetAttribute<string>(reader, "bone");
|
|
||||||
|
|
||||||
// Keyframes
|
|
||||||
NextNode(reader);
|
|
||||||
if (!CurrentNodeNameEquals(reader, "keyframes")) {
|
|
||||||
throw DeadlyImportError("No <keyframes> node found in a track in animation '" + animation.Name + "' in " + skeletonFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
NextNode(reader);
|
|
||||||
while(CurrentNodeNameEquals(reader, "keyframe"))
|
|
||||||
{
|
|
||||||
KeyFrame keyFrame;
|
|
||||||
keyFrame.Time = GetAttribute<float>(reader, "time");
|
|
||||||
|
|
||||||
NextNode(reader);
|
|
||||||
while(CurrentNodeNameEquals(reader, "translate") || CurrentNodeNameEquals(reader, "rotate") || CurrentNodeNameEquals(reader, "scale"))
|
|
||||||
{
|
|
||||||
if (CurrentNodeNameEquals(reader, "translate"))
|
|
||||||
{
|
|
||||||
keyFrame.Position.x = GetAttribute<float>(reader, "x");
|
|
||||||
keyFrame.Position.y = GetAttribute<float>(reader, "y");
|
|
||||||
keyFrame.Position.z = GetAttribute<float>(reader, "z");
|
|
||||||
}
|
|
||||||
else if (CurrentNodeNameEquals(reader, "rotate"))
|
|
||||||
{
|
|
||||||
float angle = GetAttribute<float>(reader, "angle");
|
|
||||||
|
|
||||||
NextNode(reader);
|
|
||||||
if (!CurrentNodeNameEquals(reader, "axis")) {
|
|
||||||
throw DeadlyImportError("No axis for keyframe rotation in animation '" + animation.Name + "'");
|
|
||||||
}
|
|
||||||
|
|
||||||
aiVector3D axis;
|
|
||||||
axis.x = GetAttribute<float>(reader, "x");
|
|
||||||
axis.y = GetAttribute<float>(reader, "y");
|
|
||||||
axis.z = GetAttribute<float>(reader, "z");
|
|
||||||
|
|
||||||
if (axis.Equal(zeroVec))
|
|
||||||
{
|
|
||||||
axis.x = 1.0f;
|
|
||||||
if (angle != 0) {
|
|
||||||
DefaultLogger::get()->warn("Found invalid a key frame with a zero rotation axis in animation '" + animation.Name + "'");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
keyFrame.Rotation = aiQuaternion(axis, angle);
|
|
||||||
}
|
|
||||||
else if (CurrentNodeNameEquals(reader, "scale"))
|
|
||||||
{
|
|
||||||
keyFrame.Scaling.x = GetAttribute<float>(reader, "x");
|
|
||||||
keyFrame.Scaling.y = GetAttribute<float>(reader, "y");
|
|
||||||
keyFrame.Scaling.z = GetAttribute<float>(reader, "z");
|
|
||||||
}
|
|
||||||
NextNode(reader);
|
|
||||||
}
|
|
||||||
track.Keyframes.push_back(keyFrame);
|
|
||||||
}
|
|
||||||
animation.Tracks.push_back(track);
|
|
||||||
}
|
|
||||||
Animations.push_back(animation);
|
|
||||||
|
|
||||||
DefaultLogger::get()->debug(Formatter::format() << " " << animation.Name << " (" << animation.Length << " sec, " << animation.Tracks.size() << " tracks)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void OgreImporter::CreateAssimpSkeleton(aiScene *pScene, const std::vector<Bone> &bones, const std::vector<Animation> &animations)
|
|
||||||
{
|
|
||||||
if (bones.empty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pScene->mRootNode) {
|
|
||||||
throw DeadlyImportError("Creating Assimp skeleton: No root node created!");
|
|
||||||
}
|
|
||||||
if (pScene->mRootNode->mNumChildren > 0) {
|
|
||||||
throw DeadlyImportError("Creating Assimp skeleton: Root node already has children!");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bones
|
|
||||||
vector<aiNode*> rootBones;
|
|
||||||
BOOST_FOREACH(const Bone &bone, bones)
|
|
||||||
{
|
|
||||||
if (!bone.IsParented()) {
|
|
||||||
rootBones.push_back(CreateNodeFromBone(bone.Id, bones, pScene->mRootNode));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!rootBones.empty())
|
|
||||||
{
|
|
||||||
pScene->mRootNode->mChildren = new aiNode*[rootBones.size()];
|
|
||||||
pScene->mRootNode->mNumChildren = rootBones.size();
|
|
||||||
|
|
||||||
for(size_t i=0, len=rootBones.size(); i<len; ++i) {
|
|
||||||
pScene->mRootNode->mChildren[i] = rootBones[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Auf nicht vorhandene Animationskeys achten!
|
|
||||||
// @todo Pay attention to non-existing animation Keys (google translated from above german comment)
|
|
||||||
|
|
||||||
// Animations
|
|
||||||
if (!animations.empty())
|
|
||||||
{
|
|
||||||
pScene->mAnimations = new aiAnimation*[animations.size()];
|
|
||||||
pScene->mNumAnimations = animations.size();
|
|
||||||
|
|
||||||
for(size_t ai=0, alen=animations.size(); ai<alen; ++ai)
|
|
||||||
{
|
|
||||||
const Animation &aSource = animations[ai];
|
|
||||||
|
|
||||||
aiAnimation *animation = new aiAnimation();
|
|
||||||
animation->mName = aSource.Name;
|
|
||||||
animation->mDuration = aSource.Length;
|
|
||||||
animation->mTicksPerSecond = 1.0f;
|
|
||||||
|
|
||||||
// Tracks
|
|
||||||
animation->mChannels = new aiNodeAnim*[aSource.Tracks.size()];
|
|
||||||
animation->mNumChannels = aSource.Tracks.size();
|
|
||||||
|
|
||||||
for(size_t ti=0, tlen=aSource.Tracks.size(); ti<tlen; ++ti)
|
|
||||||
{
|
|
||||||
const Track &tSource = aSource.Tracks[ti];
|
|
||||||
|
|
||||||
aiNodeAnim *animationNode = new aiNodeAnim();
|
|
||||||
animationNode->mNodeName = tSource.BoneName;
|
|
||||||
|
|
||||||
// We need this, to access the bones default pose.
|
|
||||||
// Which we need to make keys absolute to the default bone pose.
|
|
||||||
vector<Bone>::const_iterator boneIter = find(bones.begin(), bones.end(), tSource.BoneName);
|
|
||||||
if (boneIter == bones.end())
|
|
||||||
{
|
|
||||||
for(size_t createdAnimationIndex=0; createdAnimationIndex<ai; createdAnimationIndex++) {
|
|
||||||
delete pScene->mAnimations[createdAnimationIndex];
|
|
||||||
}
|
|
||||||
delete [] pScene->mAnimations;
|
|
||||||
pScene->mAnimations = NULL;
|
|
||||||
pScene->mNumAnimations = 0;
|
|
||||||
|
|
||||||
DefaultLogger::get()->error("Failed to find bone for name " + tSource.BoneName + " when creating animation " + aSource.Name +
|
|
||||||
". This is a serious error, animations wont be imported.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
aiMatrix4x4 t0, t1;
|
|
||||||
aiMatrix4x4 defaultBonePose = aiMatrix4x4::Translation(boneIter->Position, t1) * aiMatrix4x4::Rotation(boneIter->RotationAngle, boneIter->RotationAxis, t0);
|
|
||||||
|
|
||||||
// Keyframes
|
|
||||||
unsigned int numKeyframes = tSource.Keyframes.size();
|
|
||||||
|
|
||||||
animationNode->mPositionKeys = new aiVectorKey[numKeyframes];
|
|
||||||
animationNode->mRotationKeys = new aiQuatKey[numKeyframes];
|
|
||||||
animationNode->mScalingKeys = new aiVectorKey[numKeyframes];
|
|
||||||
animationNode->mNumPositionKeys = numKeyframes;
|
|
||||||
animationNode->mNumRotationKeys = numKeyframes;
|
|
||||||
animationNode->mNumScalingKeys = numKeyframes;
|
|
||||||
|
|
||||||
//...and fill them
|
|
||||||
for(size_t kfi=0; kfi<numKeyframes; ++kfi)
|
|
||||||
{
|
|
||||||
const KeyFrame &kfSource = tSource.Keyframes[kfi];
|
|
||||||
|
|
||||||
// Create a matrix to transform a vector from the bones
|
|
||||||
// default pose to the bone bones in this animation key
|
|
||||||
aiMatrix4x4 t2, t3;
|
|
||||||
aiMatrix4x4 keyBonePose =
|
|
||||||
aiMatrix4x4::Translation(kfSource.Position, t3) *
|
|
||||||
aiMatrix4x4(kfSource.Rotation.GetMatrix()) *
|
|
||||||
aiMatrix4x4::Scaling(kfSource.Scaling, t2);
|
|
||||||
|
|
||||||
// Calculate the complete transformation from world space to bone space
|
|
||||||
aiMatrix4x4 CompleteTransform = defaultBonePose * keyBonePose;
|
|
||||||
|
|
||||||
aiVector3D kfPos; aiQuaternion kfRot; aiVector3D kfScale;
|
|
||||||
CompleteTransform.Decompose(kfScale, kfRot, kfPos);
|
|
||||||
|
|
||||||
animationNode->mPositionKeys[kfi].mTime = static_cast<double>(kfSource.Time);
|
|
||||||
animationNode->mRotationKeys[kfi].mTime = static_cast<double>(kfSource.Time);
|
|
||||||
animationNode->mScalingKeys[kfi].mTime = static_cast<double>(kfSource.Time);
|
|
||||||
|
|
||||||
animationNode->mPositionKeys[kfi].mValue = kfPos;
|
|
||||||
animationNode->mRotationKeys[kfi].mValue = kfRot;
|
|
||||||
animationNode->mScalingKeys[kfi].mValue = kfScale;
|
|
||||||
}
|
|
||||||
animation->mChannels[ti] = animationNode;
|
|
||||||
}
|
|
||||||
pScene->mAnimations[ai] = animation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
aiNode* OgreImporter::CreateNodeFromBone(int boneId, const std::vector<Bone> &bones, aiNode* parent)
|
|
||||||
{
|
|
||||||
aiMatrix4x4 t0,t1;
|
|
||||||
const Bone &source = bones[boneId];
|
|
||||||
|
|
||||||
aiNode* boneNode = new aiNode(source.Name);
|
|
||||||
boneNode->mParent = parent;
|
|
||||||
boneNode->mTransformation = aiMatrix4x4::Translation(source.Position, t0) * aiMatrix4x4::Rotation(source.RotationAngle, source.RotationAxis, t1);
|
|
||||||
|
|
||||||
if (!source.Children.empty())
|
|
||||||
{
|
|
||||||
boneNode->mChildren = new aiNode*[source.Children.size()];
|
|
||||||
boneNode->mNumChildren = source.Children.size();
|
|
||||||
|
|
||||||
for(size_t i=0, len=source.Children.size(); i<len; ++i) {
|
|
||||||
boneNode->mChildren[i] = CreateNodeFromBone(source.Children[i], bones, boneNode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return boneNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Bone::CalculateBoneToWorldSpaceMatrix(vector<Bone> &Bones)
|
|
||||||
{
|
|
||||||
aiMatrix4x4 t0, t1;
|
|
||||||
aiMatrix4x4 transform = aiMatrix4x4::Rotation(-RotationAngle, RotationAxis, t1) * aiMatrix4x4::Translation(-Position, t0);
|
|
||||||
|
|
||||||
if (!IsParented())
|
|
||||||
{
|
|
||||||
BoneToWorldSpace = transform;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
BoneToWorldSpace = transform * Bones[ParentId].BoneToWorldSpace;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recursively for all children now that the parent matrix has been calculated.
|
|
||||||
BOOST_FOREACH(int childId, Children)
|
|
||||||
{
|
|
||||||
Bones[childId].CalculateBoneToWorldSpaceMatrix(Bones);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // Ogre
|
|
||||||
} // Assimp
|
|
||||||
|
|
||||||
#endif // ASSIMP_BUILD_NO_OGRE_IMPORTER
|
|
|
@ -234,10 +234,68 @@ std::string VertexElement::SemanticToString(Semantic semantic)
|
||||||
return "Uknown_VertexElement::Semantic";
|
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 (VertexBoneAssignmentList::const_iterator iter=boneAssignments.begin(), end=boneAssignments.end();
|
||||||
|
iter!=end; ++iter)
|
||||||
|
{
|
||||||
|
if (iter->vertexIndex == currentIndex)
|
||||||
|
{
|
||||||
|
VertexBoneAssignment a = (*iter);
|
||||||
|
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[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(vi, iter->weight));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return weights;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::set<uint16_t> IVertexData::ReferencedBonesByWeights() const
|
||||||
|
{
|
||||||
|
std::set<uint16_t> referenced;
|
||||||
|
for (VertexBoneAssignmentList::const_iterator iter=boneAssignments.begin(), end=boneAssignments.end();
|
||||||
|
iter!=end; ++iter)
|
||||||
|
{
|
||||||
|
referenced.insert(iter->boneIndex);
|
||||||
|
}
|
||||||
|
return referenced;
|
||||||
|
}
|
||||||
|
|
||||||
// VertexData
|
// VertexData
|
||||||
|
|
||||||
VertexData::VertexData() :
|
VertexData::VertexData()
|
||||||
count(0)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -282,6 +340,32 @@ VertexElement *VertexData::GetVertexElement(VertexElement::Semantic semantic, ui
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// VertexDataXml
|
||||||
|
|
||||||
|
VertexDataXml::VertexDataXml()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
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::IndexData() :
|
IndexData::IndexData() :
|
||||||
|
@ -316,6 +400,7 @@ size_t IndexData::FaceSize() const
|
||||||
|
|
||||||
Mesh::Mesh() :
|
Mesh::Mesh() :
|
||||||
sharedVertexData(0),
|
sharedVertexData(0),
|
||||||
|
skeleton(0),
|
||||||
hasSkeletalAnimations(false)
|
hasSkeletalAnimations(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -327,6 +412,7 @@ Mesh::~Mesh()
|
||||||
|
|
||||||
void Mesh::Reset()
|
void Mesh::Reset()
|
||||||
{
|
{
|
||||||
|
OGRE_SAFE_DELETE(skeleton)
|
||||||
OGRE_SAFE_DELETE(sharedVertexData)
|
OGRE_SAFE_DELETE(sharedVertexData)
|
||||||
|
|
||||||
for(size_t i=0, len=subMeshes.size(); i<len; ++i) {
|
for(size_t i=0, len=subMeshes.size(); i<len; ++i) {
|
||||||
|
@ -348,7 +434,7 @@ size_t Mesh::NumSubMeshes() const
|
||||||
return subMeshes.size();
|
return subMeshes.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
SubMesh2 *Mesh::SubMesh(uint16_t index) const
|
SubMesh *Mesh::GetSubMesh(uint16_t index) const
|
||||||
{
|
{
|
||||||
for(size_t i=0; i<subMeshes.size(); ++i)
|
for(size_t i=0; i<subMeshes.size(); ++i)
|
||||||
if (subMeshes[i]->index == index)
|
if (subMeshes[i]->index == index)
|
||||||
|
@ -358,7 +444,7 @@ SubMesh2 *Mesh::SubMesh(uint16_t index) const
|
||||||
|
|
||||||
void Mesh::ConvertToAssimpScene(aiScene* dest)
|
void Mesh::ConvertToAssimpScene(aiScene* dest)
|
||||||
{
|
{
|
||||||
// Export meshes
|
// Setup
|
||||||
dest->mNumMeshes = NumSubMeshes();
|
dest->mNumMeshes = NumSubMeshes();
|
||||||
dest->mMeshes = new aiMesh*[dest->mNumMeshes];
|
dest->mMeshes = new aiMesh*[dest->mNumMeshes];
|
||||||
|
|
||||||
|
@ -367,36 +453,44 @@ void Mesh::ConvertToAssimpScene(aiScene* dest)
|
||||||
dest->mRootNode->mNumMeshes = dest->mNumMeshes;
|
dest->mRootNode->mNumMeshes = dest->mNumMeshes;
|
||||||
dest->mRootNode->mMeshes = new unsigned int[dest->mRootNode->mNumMeshes];
|
dest->mRootNode->mMeshes = new unsigned int[dest->mRootNode->mNumMeshes];
|
||||||
|
|
||||||
for(size_t i=0; i<dest->mNumMeshes; ++i) {
|
// Export meshes
|
||||||
|
for(size_t i=0; i<dest->mNumMeshes; ++i)
|
||||||
|
{
|
||||||
dest->mMeshes[i] = subMeshes[i]->ConvertToAssimpMesh(this);
|
dest->mMeshes[i] = subMeshes[i]->ConvertToAssimpMesh(this);
|
||||||
dest->mRootNode->mMeshes[i] = i;
|
dest->mRootNode->mMeshes[i] = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SubMesh2
|
// ISubMesh
|
||||||
|
|
||||||
SubMesh2::SubMesh2() :
|
ISubMesh::ISubMesh() :
|
||||||
index(0),
|
index(0),
|
||||||
vertexData(0),
|
materialIndex(-1),
|
||||||
indexData(new IndexData()),
|
|
||||||
usesSharedVertexData(false),
|
usesSharedVertexData(false),
|
||||||
operationType(OT_POINT_LIST),
|
operationType(OT_POINT_LIST)
|
||||||
materialIndex(-1)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
SubMesh2::~SubMesh2()
|
// SubMesh
|
||||||
|
|
||||||
|
SubMesh::SubMesh() :
|
||||||
|
vertexData(0),
|
||||||
|
indexData(new IndexData())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SubMesh::~SubMesh()
|
||||||
{
|
{
|
||||||
Reset();
|
Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SubMesh2::Reset()
|
void SubMesh::Reset()
|
||||||
{
|
{
|
||||||
OGRE_SAFE_DELETE(vertexData)
|
OGRE_SAFE_DELETE(vertexData)
|
||||||
OGRE_SAFE_DELETE(indexData)
|
OGRE_SAFE_DELETE(indexData)
|
||||||
}
|
}
|
||||||
|
|
||||||
aiMesh *SubMesh2::ConvertToAssimpMesh(Mesh *parent)
|
aiMesh *SubMesh::ConvertToAssimpMesh(Mesh *parent)
|
||||||
{
|
{
|
||||||
if (operationType != OT_TRIANGLE_LIST) {
|
if (operationType != OT_TRIANGLE_LIST) {
|
||||||
throw DeadlyImportError(Formatter::format() << "Only mesh operation type OT_TRIANGLE_LIST is supported. Found " << operationType);
|
throw DeadlyImportError(Formatter::format() << "Only mesh operation type OT_TRIANGLE_LIST is supported. Found " << operationType);
|
||||||
|
@ -456,6 +550,8 @@ aiMesh *SubMesh2::ConvertToAssimpMesh(Mesh *parent)
|
||||||
const size_t vWidthUv1 = (uv1Element ? src->VertexSize(uv1Element->source) : 0);
|
const size_t vWidthUv1 = (uv1Element ? src->VertexSize(uv1Element->source) : 0);
|
||||||
const size_t vWidthUv2 = (uv2Element ? src->VertexSize(uv2Element->source) : 0);
|
const size_t vWidthUv2 = (uv2Element ? src->VertexSize(uv2Element->source) : 0);
|
||||||
|
|
||||||
|
bool boneAssignments = src->HasBoneAssignments();
|
||||||
|
|
||||||
// Prepare normals
|
// Prepare normals
|
||||||
if (normals)
|
if (normals)
|
||||||
dest->mNormals = new aiVector3D[dest->mNumVertices];
|
dest->mNormals = new aiVector3D[dest->mNumVertices];
|
||||||
|
@ -530,6 +626,7 @@ aiMesh *SubMesh2::ConvertToAssimpMesh(Mesh *parent)
|
||||||
|
|
||||||
// Ogres vertex index to ref into the source buffers.
|
// Ogres vertex index to ref into the source buffers.
|
||||||
const size_t ogreVertexIndex = ogreFace.mIndices[v];
|
const size_t ogreVertexIndex = ogreFace.mIndices[v];
|
||||||
|
src->AddVertexMapping(ogreVertexIndex, newIndex);
|
||||||
|
|
||||||
// Position
|
// Position
|
||||||
positions->Seek((vWidthPosition * ogreVertexIndex) + positionsElement->offset, aiOrigin_SET);
|
positions->Seek((vWidthPosition * ogreVertexIndex) + positionsElement->offset, aiOrigin_SET);
|
||||||
|
@ -553,29 +650,505 @@ aiMesh *SubMesh2::ConvertToAssimpMesh(Mesh *parent)
|
||||||
uv2->Seek((vWidthUv2 * ogreVertexIndex) + uv2Element->offset, aiOrigin_SET);
|
uv2->Seek((vWidthUv2 * ogreVertexIndex) + uv2Element->offset, aiOrigin_SET);
|
||||||
uv2->Read(&uv2Dest[newIndex], sizeUv2, 1);
|
uv2->Read(&uv2Dest[newIndex], sizeUv2, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @todo Bones and bone weights.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bones and bone weights
|
||||||
|
if (parent->skeleton && boneAssignments)
|
||||||
|
{
|
||||||
|
AssimpVertexBoneWeightList weights = src->AssimpBoneWeights(dest->mNumVertices);
|
||||||
|
std::set<uint16_t> referencedBones = src->ReferencedBonesByWeights();
|
||||||
|
|
||||||
|
dest->mNumBones = 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;
|
return dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Animation2
|
// MeshXml
|
||||||
|
|
||||||
Animation2::Animation2(Mesh *_parentMesh) :
|
MeshXml::MeshXml() :
|
||||||
parentMesh(_parentMesh),
|
sharedVertexData(0),
|
||||||
|
skeleton(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
MeshXml::~MeshXml()
|
||||||
|
{
|
||||||
|
Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeshXml::Reset()
|
||||||
|
{
|
||||||
|
OGRE_SAFE_DELETE(skeleton)
|
||||||
|
OGRE_SAFE_DELETE(sharedVertexData)
|
||||||
|
|
||||||
|
for(size_t i=0, len=subMeshes.size(); i<len; ++i) {
|
||||||
|
OGRE_SAFE_DELETE(subMeshes[i])
|
||||||
|
}
|
||||||
|
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 = 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] = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export skeleton
|
||||||
|
if (skeleton)
|
||||||
|
{
|
||||||
|
// Bones
|
||||||
|
if (!skeleton->bones.empty())
|
||||||
|
{
|
||||||
|
BoneList rootBones = skeleton->RootBones();
|
||||||
|
dest->mRootNode->mNumChildren = 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 = 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() :
|
||||||
|
vertexData(0),
|
||||||
|
indexData(new IndexDataXml())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = 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] = newIndex;
|
||||||
|
|
||||||
|
// Ogres vertex index to ref into the source buffers.
|
||||||
|
const size_t ogreVertexIndex = ogreFace.mIndices[v];
|
||||||
|
src->AddVertexMapping(ogreVertexIndex, 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 = 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) :
|
||||||
|
parentSkeleton(parent),
|
||||||
|
parentMesh(0),
|
||||||
length(0.0f),
|
length(0.0f),
|
||||||
baseTime(-1.0f)
|
baseTime(-1.0f)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
VertexData *Animation2::AssociatedVertexData(VertexAnimationTrack *track) const
|
Animation::Animation(Mesh *parent) :
|
||||||
|
parentMesh(parent),
|
||||||
|
parentSkeleton(0),
|
||||||
|
length(0.0f),
|
||||||
|
baseTime(-1.0f)
|
||||||
{
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
VertexData *Animation::AssociatedVertexData(VertexAnimationTrack *track) const
|
||||||
|
{
|
||||||
|
if (!parentMesh)
|
||||||
|
return 0;
|
||||||
|
|
||||||
bool sharedGeom = (track->target == 0);
|
bool sharedGeom = (track->target == 0);
|
||||||
if (sharedGeom)
|
if (sharedGeom)
|
||||||
return parentMesh->sharedVertexData;
|
return parentMesh->sharedVertexData;
|
||||||
else
|
else
|
||||||
return parentMesh->SubMesh(track->target-1)->vertexData;
|
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 = 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()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Skeleton::~Skeleton()
|
||||||
|
{
|
||||||
|
Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Skeleton::Reset()
|
||||||
|
{
|
||||||
|
for(size_t i=0, len=bones.size(); i<len; ++i) {
|
||||||
|
OGRE_SAFE_DELETE(bones[i])
|
||||||
|
}
|
||||||
|
bones.clear();
|
||||||
|
for(size_t i=0, len=animations.size(); i<len; ++i) {
|
||||||
|
OGRE_SAFE_DELETE(animations[i])
|
||||||
|
}
|
||||||
|
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),
|
||||||
|
rotationAngle(0.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)
|
||||||
|
{
|
||||||
|
aiMatrix4x4 t0, t1;
|
||||||
|
aiMatrix4x4 transform = aiMatrix4x4::Rotation(-rotationAngle, rotation, t1) * aiMatrix4x4::Translation(-position, t0);
|
||||||
|
|
||||||
|
if (!IsParented())
|
||||||
|
worldMatrix = transform;
|
||||||
|
else
|
||||||
|
worldMatrix = transform * parent->worldMatrix;
|
||||||
|
|
||||||
|
aiMatrix4x4 t2, t3; /// @todo t0 and t1 could probably be reused here?
|
||||||
|
defaultPose = aiMatrix4x4::Translation(position, t2) * aiMatrix4x4::Rotation(rotationAngle, rotation, t3);
|
||||||
|
|
||||||
|
// Recursively for all children now that the parent matrix has been calculated.
|
||||||
|
for (size_t i=0, len=children.size(); i<len; ++i)
|
||||||
|
{
|
||||||
|
Bone *child = skeleton->BoneById(children[i]);
|
||||||
|
if (!child) {
|
||||||
|
throw DeadlyImportError(Formatter::format() << "CalculateWorldMatrixAndDefaultPose: Failed to find child bone " << children[i] << " for parent " << id << " " << name);
|
||||||
|
}
|
||||||
|
child->CalculateWorldMatrixAndDefaultPose(skeleton);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
aiNode *Bone::ConvertToAssimpNode(Skeleton *skeleton, aiNode *parentNode)
|
||||||
|
{
|
||||||
|
aiMatrix4x4 t0,t1;
|
||||||
|
|
||||||
|
// Bone node
|
||||||
|
aiNode* node = new aiNode(name);
|
||||||
|
node->mParent = parentNode;
|
||||||
|
node->mTransformation = defaultPose;
|
||||||
|
|
||||||
|
// Children
|
||||||
|
if (!children.empty())
|
||||||
|
{
|
||||||
|
node->mNumChildren = 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(Formatter::format() << "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 = boneWeights.size();
|
||||||
|
bone->mWeights = new aiVertexWeight[boneWeights.size()];
|
||||||
|
memcpy(bone->mWeights, &boneWeights[0], boneWeights.size() * sizeof(aiVertexWeight));
|
||||||
|
}
|
||||||
|
|
||||||
|
return bone;
|
||||||
|
}
|
||||||
|
|
||||||
|
// VertexAnimationTrack
|
||||||
|
|
||||||
|
VertexAnimationTrack::VertexAnimationTrack() :
|
||||||
|
target(0),
|
||||||
|
type(VAT_NONE)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = numKeyframes;
|
||||||
|
nodeAnim->mNumRotationKeys = numKeyframes;
|
||||||
|
nodeAnim->mNumScalingKeys = numKeyframes;
|
||||||
|
|
||||||
|
for(size_t kfi=0; kfi<numKeyframes; ++kfi)
|
||||||
|
{
|
||||||
|
const TransformKeyFrame &kfSource = transformKeyFrames[kfi];
|
||||||
|
|
||||||
|
// Create a matrix to transform a vector from the bones
|
||||||
|
// default pose to the bone bones in this animation key
|
||||||
|
aiMatrix4x4 t0, t1;
|
||||||
|
aiMatrix4x4 keyBonePose =
|
||||||
|
aiMatrix4x4::Translation(kfSource.position, t0) *
|
||||||
|
aiMatrix4x4(kfSource.rotation.GetMatrix()) *
|
||||||
|
aiMatrix4x4::Scaling(kfSource.scale, t1);
|
||||||
|
|
||||||
|
// Calculate the complete transformation from world space to bone space
|
||||||
|
aiMatrix4x4 finalTransform = bone->defaultPose * keyBonePose;
|
||||||
|
|
||||||
|
aiVector3D kfPos; aiQuaternion kfRot; aiVector3D kfScale;
|
||||||
|
finalTransform.Decompose(kfScale, kfRot, kfPos);
|
||||||
|
|
||||||
|
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 = kfPos;
|
||||||
|
nodeAnim->mRotationKeys[kfi].mValue = kfRot;
|
||||||
|
nodeAnim->mScalingKeys[kfi].mValue = kfScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nodeAnim;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // Ogre
|
} // Ogre
|
||||||
|
|
|
@ -57,7 +57,10 @@ namespace Ogre
|
||||||
|
|
||||||
// Forward decl
|
// Forward decl
|
||||||
class Mesh;
|
class Mesh;
|
||||||
class SubMesh2;
|
class MeshXml;
|
||||||
|
class SubMesh;
|
||||||
|
class SubMeshXml;
|
||||||
|
class Skeleton;
|
||||||
|
|
||||||
#define OGRE_SAFE_DELETE(p) delete p; p=0;
|
#define OGRE_SAFE_DELETE(p) delete p; p=0;
|
||||||
|
|
||||||
|
@ -156,11 +159,53 @@ public:
|
||||||
Type type;
|
Type type;
|
||||||
Semantic semantic;
|
Semantic semantic;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::vector<VertexElement> VertexElementList;
|
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
|
// Ogre Vertex Data
|
||||||
class VertexData
|
class VertexData : public IVertexData
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
VertexData();
|
VertexData();
|
||||||
|
@ -178,9 +223,6 @@ public:
|
||||||
/// Get vertex element for @c semantic for @c index.
|
/// Get vertex element for @c semantic for @c index.
|
||||||
VertexElement *GetVertexElement(VertexElement::Semantic semantic, uint16_t index = 0);
|
VertexElement *GetVertexElement(VertexElement::Semantic semantic, uint16_t index = 0);
|
||||||
|
|
||||||
/// Vertex count.
|
|
||||||
uint32_t count;
|
|
||||||
|
|
||||||
/// Vertex elements.
|
/// Vertex elements.
|
||||||
VertexElementList vertexElements;
|
VertexElementList vertexElements;
|
||||||
|
|
||||||
|
@ -243,6 +285,7 @@ public:
|
||||||
/// Vertex offset and normals.
|
/// Vertex offset and normals.
|
||||||
PoseVertexMap vertices;
|
PoseVertexMap vertices;
|
||||||
};
|
};
|
||||||
|
typedef std::vector<Pose*> PoseList;
|
||||||
|
|
||||||
/// Ogre Pose Key Frame Ref
|
/// Ogre Pose Key Frame Ref
|
||||||
struct PoseRef
|
struct PoseRef
|
||||||
|
@ -250,6 +293,7 @@ struct PoseRef
|
||||||
uint16_t index;
|
uint16_t index;
|
||||||
float influence;
|
float influence;
|
||||||
};
|
};
|
||||||
|
typedef std::vector<PoseRef> PoseRefList;
|
||||||
|
|
||||||
/// Ogre Pose Key Frame
|
/// Ogre Pose Key Frame
|
||||||
struct PoseKeyFrame
|
struct PoseKeyFrame
|
||||||
|
@ -257,8 +301,9 @@ struct PoseKeyFrame
|
||||||
/// Time position in the animation.
|
/// Time position in the animation.
|
||||||
float timePos;
|
float timePos;
|
||||||
|
|
||||||
std::vector<PoseRef> references;
|
PoseRefList references;
|
||||||
};
|
};
|
||||||
|
typedef std::vector<PoseKeyFrame> PoseKeyFrameList;
|
||||||
|
|
||||||
/// Ogre Morph Key Frame
|
/// Ogre Morph Key Frame
|
||||||
struct MorphKeyFrame
|
struct MorphKeyFrame
|
||||||
|
@ -268,6 +313,18 @@ struct MorphKeyFrame
|
||||||
|
|
||||||
MemoryStreamPtr buffer;
|
MemoryStreamPtr buffer;
|
||||||
};
|
};
|
||||||
|
typedef std::vector<MorphKeyFrame> MorphKeyFrameList;
|
||||||
|
|
||||||
|
/// Ogre animation key frame
|
||||||
|
struct TransformKeyFrame
|
||||||
|
{
|
||||||
|
float timePos;
|
||||||
|
|
||||||
|
aiQuaternion rotation;
|
||||||
|
aiVector3D position;
|
||||||
|
aiVector3D scale;
|
||||||
|
};
|
||||||
|
typedef std::vector<TransformKeyFrame> TransformKeyFrameList;
|
||||||
|
|
||||||
/// Ogre Animation Track
|
/// Ogre Animation Track
|
||||||
struct VertexAnimationTrack
|
struct VertexAnimationTrack
|
||||||
|
@ -279,59 +336,141 @@ struct VertexAnimationTrack
|
||||||
/// Morph animation is made up of many interpolated snapshot keyframes
|
/// Morph animation is made up of many interpolated snapshot keyframes
|
||||||
VAT_MORPH = 1,
|
VAT_MORPH = 1,
|
||||||
/// Pose animation is made up of a single delta pose keyframe
|
/// Pose animation is made up of a single delta pose keyframe
|
||||||
VAT_POSE = 2
|
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.
|
/// Vertex data target.
|
||||||
/** 0 == shared geometry
|
/** 0 == shared geometry
|
||||||
>0 == submesh index + 1 */
|
>0 == submesh index + 1 */
|
||||||
uint16_t target;
|
uint16_t target;
|
||||||
Type type;
|
|
||||||
|
|
||||||
std::vector<PoseKeyFrame> poseKeyFrames;
|
/// Only valid for VAT_TRANSFORM.
|
||||||
std::vector<MorphKeyFrame> morphKeyFrames;
|
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
|
/// Ogre Animation
|
||||||
/** @todo Port OgreImporter::Animation to this and rename this to Animation! */
|
class Animation
|
||||||
class Animation2
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Animation2(Mesh *_parentMesh);
|
Animation(Skeleton *parent);
|
||||||
|
Animation(Mesh *parent);
|
||||||
|
|
||||||
/// Returns the associated vertex data for a track in this animation.
|
/// 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;
|
VertexData *AssociatedVertexData(VertexAnimationTrack *track) const;
|
||||||
|
|
||||||
|
/// Convert to Assimp animation.
|
||||||
|
aiAnimation *ConvertToAssimpAnimation();
|
||||||
|
|
||||||
/// Parent mesh.
|
/// Parent mesh.
|
||||||
|
/** @note Set only when animation is read from a mesh. */
|
||||||
Mesh *parentMesh;
|
Mesh *parentMesh;
|
||||||
|
|
||||||
|
/// Parent skeleton.
|
||||||
|
/** @note Set only when animation is read from a skeleton. */
|
||||||
|
Skeleton *parentSkeleton;
|
||||||
|
|
||||||
/// Animation name.
|
/// Animation name.
|
||||||
std::string name;
|
std::string name;
|
||||||
|
|
||||||
/// Base animation name.
|
/// Base animation name.
|
||||||
std::string baseName;
|
std::string baseName;
|
||||||
|
|
||||||
/// Length in seconds.
|
/// Length in seconds.
|
||||||
float length;
|
float length;
|
||||||
|
|
||||||
/// Base animation key time.
|
/// Base animation key time.
|
||||||
float baseTime;
|
float baseTime;
|
||||||
|
|
||||||
/// Animation tracks.
|
/// Animation tracks.
|
||||||
std::vector<VertexAnimationTrack> tracks;
|
VertexAnimationTrackList tracks;
|
||||||
};
|
};
|
||||||
|
typedef std::vector<Animation*> AnimationList;
|
||||||
|
|
||||||
/// Ogre Vertex Bone Assignment
|
/// Ogre Bone
|
||||||
struct VertexBoneAssignment
|
class Bone
|
||||||
{
|
{
|
||||||
uint32_t vertexIndex;
|
public:
|
||||||
uint16_t boneIndex;
|
Bone();
|
||||||
float weight;
|
|
||||||
|
/// 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;
|
||||||
|
aiVector3D rotation;
|
||||||
|
float rotationAngle;
|
||||||
|
|
||||||
|
aiMatrix4x4 worldMatrix;
|
||||||
|
aiMatrix4x4 defaultPose;
|
||||||
|
};
|
||||||
|
typedef std::vector<Bone*> BoneList;
|
||||||
|
|
||||||
|
/// Ogre Skeleton
|
||||||
|
class Skeleton
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
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;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Ogre SubMesh
|
/// Ogre Sub Mesh interface, inherited by the binary and XML implementations.
|
||||||
/** @todo Port OgreImporter::SubMesh to this and rename this to SubMesh! */
|
class ISubMesh
|
||||||
class SubMesh2
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// @note Full list of Ogre types, not all of them are supported and exposed to Assimp.
|
/// @note Full list of Ogre types, not all of them are supported and exposed to Assimp.
|
||||||
|
@ -351,17 +490,7 @@ public:
|
||||||
OT_TRIANGLE_FAN = 6
|
OT_TRIANGLE_FAN = 6
|
||||||
};
|
};
|
||||||
|
|
||||||
SubMesh2();
|
ISubMesh();
|
||||||
~SubMesh2();
|
|
||||||
|
|
||||||
/// 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();
|
|
||||||
|
|
||||||
/// Covert to Assimp mesh.
|
|
||||||
aiMesh *ConvertToAssimpMesh(Mesh *parent);
|
|
||||||
|
|
||||||
/// SubMesh index.
|
/// SubMesh index.
|
||||||
unsigned int index;
|
unsigned int index;
|
||||||
|
@ -371,7 +500,7 @@ public:
|
||||||
|
|
||||||
/// Material used by this submesh.
|
/// Material used by this submesh.
|
||||||
std::string materialRef;
|
std::string materialRef;
|
||||||
|
|
||||||
/// Texture alias information.
|
/// Texture alias information.
|
||||||
std::string textureAliasName;
|
std::string textureAliasName;
|
||||||
std::string textureAliasRef;
|
std::string textureAliasRef;
|
||||||
|
@ -379,22 +508,37 @@ public:
|
||||||
/// Assimp scene material index used by this submesh.
|
/// Assimp scene material index used by this submesh.
|
||||||
/** -1 if no material or material could not be imported. */
|
/** -1 if no material or material could not be imported. */
|
||||||
int materialIndex;
|
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();
|
||||||
|
|
||||||
|
/// Covert to Assimp mesh.
|
||||||
|
aiMesh *ConvertToAssimpMesh(Mesh *parent);
|
||||||
|
|
||||||
/// Vertex data.
|
/// Vertex data.
|
||||||
VertexData *vertexData;
|
VertexData *vertexData;
|
||||||
|
|
||||||
/// Index data.
|
/// Index data.
|
||||||
IndexData *indexData;
|
IndexData *indexData;
|
||||||
|
|
||||||
/// If submesh uses shared geometry from parent mesh.
|
|
||||||
bool usesSharedVertexData;
|
|
||||||
|
|
||||||
/// Operation type.
|
|
||||||
OperationType operationType;
|
|
||||||
|
|
||||||
/// Bone assignments.
|
|
||||||
std::vector<VertexBoneAssignment> boneAssignments;
|
|
||||||
};
|
};
|
||||||
|
typedef std::vector<SubMesh*> SubMeshList;
|
||||||
|
|
||||||
/// Ogre Mesh
|
/// Ogre Mesh
|
||||||
class Mesh
|
class Mesh
|
||||||
|
@ -410,7 +554,7 @@ public:
|
||||||
size_t NumSubMeshes() const;
|
size_t NumSubMeshes() const;
|
||||||
|
|
||||||
/// Returns submesh for @c index.
|
/// Returns submesh for @c index.
|
||||||
SubMesh2 *SubMesh(uint16_t index) const;
|
SubMesh *GetSubMesh(uint16_t index) const;
|
||||||
|
|
||||||
/// Convert mesh to Assimp scene.
|
/// Convert mesh to Assimp scene.
|
||||||
void ConvertToAssimpScene(aiScene* dest);
|
void ConvertToAssimpScene(aiScene* dest);
|
||||||
|
@ -421,20 +565,98 @@ public:
|
||||||
/// Skeleton reference.
|
/// Skeleton reference.
|
||||||
std::string skeletonRef;
|
std::string skeletonRef;
|
||||||
|
|
||||||
|
/// Skeleton.
|
||||||
|
Skeleton *skeleton;
|
||||||
|
|
||||||
/// Vertex data
|
/// Vertex data
|
||||||
VertexData *sharedVertexData;
|
VertexData *sharedVertexData;
|
||||||
|
|
||||||
/// Sub meshes.
|
/// Sub meshes.
|
||||||
std::vector<SubMesh2*> subMeshes;
|
SubMeshList subMeshes;
|
||||||
|
|
||||||
/// Animations
|
/// Animations
|
||||||
std::vector<Animation2*> animations;
|
AnimationList animations;
|
||||||
|
|
||||||
/// Bone assignments.
|
|
||||||
std::vector<VertexBoneAssignment> boneAssignments;
|
|
||||||
|
|
||||||
/// Poses
|
/// Poses
|
||||||
std::vector<Pose*> poses;
|
PoseList poses;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Ogre XML Vertex Data
|
||||||
|
class VertexDataXml : public IVertexData
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
VertexDataXml();
|
||||||
|
|
||||||
|
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
|
} // Ogre
|
||||||
|
|
|
@ -38,5 +38,880 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** @todo Move XML related serialization from OgreImporter.cpp
|
#include "OgreXmlSerializer.h"
|
||||||
here in a similar fashion as OgreBinarySerializer. */
|
|
||||||
|
#include "irrXMLWrapper.h"
|
||||||
|
#include "TinyFormatter.h"
|
||||||
|
|
||||||
|
#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER
|
||||||
|
|
||||||
|
// Define as 1 to get verbose logging.
|
||||||
|
#define OGRE_XML_SERIALIZER_DEBUG 0
|
||||||
|
|
||||||
|
namespace Assimp
|
||||||
|
{
|
||||||
|
namespace Ogre
|
||||||
|
{
|
||||||
|
|
||||||
|
void ThrowAttibuteError(const XmlReader* reader, const std::string &name, const std::string &error = "")
|
||||||
|
{
|
||||||
|
if (!error.empty())
|
||||||
|
{
|
||||||
|
throw DeadlyImportError(error + " in node '" + std::string(reader->getNodeName()) + "' and attribute '" + name + "'");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw DeadlyImportError("Attribute '" + name + "' does not exist in node '" + std::string(reader->getNodeName()) + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
int32_t OgreXmlSerializer::ReadAttribute<int32_t>(const std::string &name) const
|
||||||
|
{
|
||||||
|
if (HasAttribute(name.c_str()))
|
||||||
|
{
|
||||||
|
return static_cast<int32_t>(m_reader->getAttributeValueAsInt(name.c_str()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ThrowAttibuteError(m_reader, name);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
uint32_t OgreXmlSerializer::ReadAttribute<uint32_t>(const std::string &name) const
|
||||||
|
{
|
||||||
|
if (HasAttribute(name.c_str()))
|
||||||
|
{
|
||||||
|
/** @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>(name);
|
||||||
|
if (temp >= 0)
|
||||||
|
{
|
||||||
|
return static_cast<uint32_t>(temp);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ThrowAttibuteError(m_reader, name, "Found a negative number value where expecting a uint32_t value");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ThrowAttibuteError(m_reader, name);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
uint16_t OgreXmlSerializer::ReadAttribute<uint16_t>(const std::string &name) const
|
||||||
|
{
|
||||||
|
if (HasAttribute(name.c_str()))
|
||||||
|
{
|
||||||
|
return static_cast<uint16_t>(ReadAttribute<uint32_t>(name));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ThrowAttibuteError(m_reader, name);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
float OgreXmlSerializer::ReadAttribute<float>(const std::string &name) const
|
||||||
|
{
|
||||||
|
if (HasAttribute(name.c_str()))
|
||||||
|
{
|
||||||
|
return m_reader->getAttributeValueAsFloat(name.c_str());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ThrowAttibuteError(m_reader, name);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
std::string OgreXmlSerializer::ReadAttribute<std::string>(const std::string &name) const
|
||||||
|
{
|
||||||
|
const char* value = m_reader->getAttributeValue(name.c_str());
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
return std::string(value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ThrowAttibuteError(m_reader, name);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
bool OgreXmlSerializer::ReadAttribute<bool>(const std::string &name) const
|
||||||
|
{
|
||||||
|
std::string value = Ogre::ToLower(ReadAttribute<std::string>(name));
|
||||||
|
if (ASSIMP_stricmp(value, "true") == 0)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (ASSIMP_stricmp(value, "false") == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ThrowAttibuteError(m_reader, name, "Boolean value is expected to be 'true' or 'false', encountered '" + value + "'");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OgreXmlSerializer::HasAttribute(const std::string &name) const
|
||||||
|
{
|
||||||
|
return (m_reader->getAttributeValue(name.c_str()) != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string &OgreXmlSerializer::NextNode()
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (!m_reader->read())
|
||||||
|
{
|
||||||
|
m_currentNodeName = "";
|
||||||
|
return m_currentNodeName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while(m_reader->getNodeType() != irr::io::EXN_ELEMENT);
|
||||||
|
|
||||||
|
CurrentNodeName(true);
|
||||||
|
#if (OGRE_XML_SERIALIZER_DEBUG == 1)
|
||||||
|
DefaultLogger::get()->debug("<" + m_currentNodeName + ">");
|
||||||
|
#endif
|
||||||
|
return m_currentNodeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OgreXmlSerializer::CurrentNodeNameEquals(const std::string &name) const
|
||||||
|
{
|
||||||
|
return (ASSIMP_stricmp(m_currentNodeName, name) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string OgreXmlSerializer::CurrentNodeName(bool forceRead)
|
||||||
|
{
|
||||||
|
if (forceRead)
|
||||||
|
m_currentNodeName = std::string(m_reader->getNodeName());
|
||||||
|
return m_currentNodeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string &OgreXmlSerializer::SkipCurrentNode()
|
||||||
|
{
|
||||||
|
#if (OGRE_XML_SERIALIZER_DEBUG == 1)
|
||||||
|
DefaultLogger::get()->debug("Skipping node <" + m_currentNodeName + ">");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
if (!m_reader->read())
|
||||||
|
{
|
||||||
|
m_currentNodeName = "";
|
||||||
|
return m_currentNodeName;
|
||||||
|
}
|
||||||
|
if (m_reader->getNodeType() != irr::io::EXN_ELEMENT_END)
|
||||||
|
continue;
|
||||||
|
else if (std::string(m_reader->getNodeName()) == m_currentNodeName)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return NextNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
// <mesh>
|
||||||
|
const std::string nnMesh = "mesh";
|
||||||
|
const std::string nnSharedGeometry = "sharedgeometry";
|
||||||
|
const std::string nnSubMeshes = "submeshes";
|
||||||
|
const std::string nnSubMesh = "submesh";
|
||||||
|
const std::string nnSubMeshNames = "submeshnames";
|
||||||
|
const std::string nnSkeletonLink = "skeletonlink";
|
||||||
|
const std::string nnLOD = "levelofdetail";
|
||||||
|
const std::string nnExtremes = "extremes";
|
||||||
|
const std::string nnPoses = "poses";
|
||||||
|
const std::string nnAnimations = "animations";
|
||||||
|
|
||||||
|
// <submesh>
|
||||||
|
const std::string nnFaces = "faces";
|
||||||
|
const std::string nnFace = "face";
|
||||||
|
const std::string nnGeometry = "geometry";
|
||||||
|
const std::string nnTextures = "textures";
|
||||||
|
|
||||||
|
// <mesh/submesh>
|
||||||
|
const std::string nnBoneAssignments = "boneassignments";
|
||||||
|
|
||||||
|
// <sharedgeometry/geometry>
|
||||||
|
const std::string nnVertexBuffer = "vertexbuffer";
|
||||||
|
|
||||||
|
// <vertexbuffer>
|
||||||
|
const std::string nnVertex = "vertex";
|
||||||
|
const std::string nnPosition = "position";
|
||||||
|
const std::string nnNormal = "normal";
|
||||||
|
const std::string nnTangent = "tangent";
|
||||||
|
const std::string nnBinormal = "binormal";
|
||||||
|
const std::string nnTexCoord = "texcoord";
|
||||||
|
const std::string nnColorDiffuse = "colour_diffuse";
|
||||||
|
const std::string nnColorSpecular = "colour_specular";
|
||||||
|
|
||||||
|
// <boneassignments>
|
||||||
|
const std::string nnVertexBoneAssignment = "vertexboneassignment";
|
||||||
|
|
||||||
|
MeshXml *OgreXmlSerializer::ImportMesh(XmlReader *reader)
|
||||||
|
{
|
||||||
|
OgreXmlSerializer serializer(reader);
|
||||||
|
|
||||||
|
MeshXml *mesh = new MeshXml();
|
||||||
|
serializer.ReadMesh(mesh);
|
||||||
|
return mesh;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OgreXmlSerializer::ImportSkeleton(Assimp::IOSystem *pIOHandler, MeshXml *mesh)
|
||||||
|
{
|
||||||
|
if (mesh->skeletonRef.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
/** @todo Also support referencing a binary skeleton from a XML mesh?
|
||||||
|
This will involves new interfacing to cross ref from MeshXml... */
|
||||||
|
|
||||||
|
std::string filename = mesh->skeletonRef;
|
||||||
|
if (EndsWith(filename, ".skeleton"))
|
||||||
|
{
|
||||||
|
DefaultLogger::get()->warn("Mesh is referencing a Ogre binary skeleton. Parsing binary Ogre assets is not supported at the moment. Trying to find .skeleton.xml file instead.");
|
||||||
|
filename += ".xml";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pIOHandler->Exists(filename))
|
||||||
|
{
|
||||||
|
DefaultLogger::get()->error("Failed to find skeleton file '" + filename + "', skeleton will be missing.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::scoped_ptr<IOStream> file(pIOHandler->Open(filename));
|
||||||
|
if (!file.get()) {
|
||||||
|
throw DeadlyImportError("Failed to open skeleton file " + filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::scoped_ptr<CIrrXML_IOStreamReader> stream(new CIrrXML_IOStreamReader(file.get()));
|
||||||
|
XmlReader* reader = irr::io::createIrrXMLReader(stream.get());
|
||||||
|
if (!reader) {
|
||||||
|
throw DeadlyImportError("Failed to create XML reader for skeleton file " + filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
Skeleton *skeleton = new Skeleton();
|
||||||
|
|
||||||
|
OgreXmlSerializer serializer(reader);
|
||||||
|
serializer.ReadSkeleton(skeleton);
|
||||||
|
|
||||||
|
mesh->skeleton = skeleton;
|
||||||
|
}
|
||||||
|
|
||||||
|
// <skeleton>
|
||||||
|
const std::string nnSkeleton = "skeleton";
|
||||||
|
const std::string nnBones = "bones";
|
||||||
|
const std::string nnBoneHierarchy = "bonehierarchy";
|
||||||
|
|
||||||
|
// <bones>
|
||||||
|
const std::string nnBone = "bone";
|
||||||
|
const std::string nnRotation = "rotation";
|
||||||
|
const std::string nnAxis = "axis";
|
||||||
|
|
||||||
|
// <bonehierarchy>
|
||||||
|
const std::string nnBoneParent = "boneparent";
|
||||||
|
|
||||||
|
// <animations>
|
||||||
|
const std::string nnAnimation = "animation";
|
||||||
|
const std::string nnTracks = "tracks";
|
||||||
|
|
||||||
|
// <tracks>
|
||||||
|
const std::string nnTrack = "track";
|
||||||
|
const std::string nnKeyFrames = "keyframes";
|
||||||
|
const std::string nnKeyFrame = "keyframe";
|
||||||
|
const std::string nnTranslate = "translate";
|
||||||
|
const std::string nnRotate = "rotate";
|
||||||
|
const std::string nnScale = "scale";
|
||||||
|
|
||||||
|
const std::string anX = "x";
|
||||||
|
const std::string anY = "y";
|
||||||
|
const std::string anZ = "z";
|
||||||
|
|
||||||
|
void OgreXmlSerializer::ReadSkeleton(Skeleton *skeleton)
|
||||||
|
{
|
||||||
|
if (NextNode() != nnSkeleton) {
|
||||||
|
throw DeadlyImportError("Root node is <" + m_currentNodeName + "> expecting <skeleton>");
|
||||||
|
}
|
||||||
|
|
||||||
|
DefaultLogger::get()->debug("Reading Skeleton");
|
||||||
|
|
||||||
|
NextNode();
|
||||||
|
|
||||||
|
// Root level nodes
|
||||||
|
while(m_currentNodeName == nnBones ||
|
||||||
|
m_currentNodeName == nnBoneHierarchy ||
|
||||||
|
m_currentNodeName == nnAnimations)
|
||||||
|
{
|
||||||
|
if (m_currentNodeName == nnBones)
|
||||||
|
ReadBones(skeleton);
|
||||||
|
else if (m_currentNodeName == nnBoneHierarchy)
|
||||||
|
ReadBoneHierarchy(skeleton);
|
||||||
|
else if (m_currentNodeName == nnAnimations)
|
||||||
|
ReadAnimations(skeleton);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OgreXmlSerializer::ReadAnimations(Skeleton *skeleton)
|
||||||
|
{
|
||||||
|
DefaultLogger::get()->debug(" - Animations");
|
||||||
|
|
||||||
|
NextNode();
|
||||||
|
while(m_currentNodeName == nnAnimation)
|
||||||
|
{
|
||||||
|
Animation *anim = new Animation(skeleton);
|
||||||
|
anim->name = ReadAttribute<std::string>("name");
|
||||||
|
anim->length = ReadAttribute<float>("length");
|
||||||
|
|
||||||
|
if (NextNode() != nnTracks) {
|
||||||
|
throw DeadlyImportError(Formatter::format() << "No <tracks> found in <animation> " << anim->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadAnimationTracks(anim);
|
||||||
|
skeleton->animations.push_back(anim);
|
||||||
|
|
||||||
|
DefaultLogger::get()->debug(Formatter::format() << " " << anim->name << " (" << anim->length << " sec, " << anim->tracks.size() << " tracks)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OgreXmlSerializer::ReadAnimationTracks(Animation *dest)
|
||||||
|
{
|
||||||
|
NextNode();
|
||||||
|
while(m_currentNodeName == nnTrack)
|
||||||
|
{
|
||||||
|
VertexAnimationTrack track;
|
||||||
|
track.type = VertexAnimationTrack::VAT_TRANSFORM;
|
||||||
|
track.boneName = ReadAttribute<std::string>("bone");
|
||||||
|
|
||||||
|
if (NextNode() != nnKeyFrames) {
|
||||||
|
throw DeadlyImportError(Formatter::format() << "No <keyframes> found in <track> " << dest->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadAnimationKeyFrames(dest, &track);
|
||||||
|
|
||||||
|
dest->tracks.push_back(track);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OgreXmlSerializer::ReadAnimationKeyFrames(Animation *anim, VertexAnimationTrack *dest)
|
||||||
|
{
|
||||||
|
const aiVector3D zeroVec(0.f, 0.f, 0.f);
|
||||||
|
|
||||||
|
NextNode();
|
||||||
|
while(m_currentNodeName == nnKeyFrame)
|
||||||
|
{
|
||||||
|
TransformKeyFrame keyframe;
|
||||||
|
keyframe.timePos = ReadAttribute<float>("time");
|
||||||
|
|
||||||
|
NextNode();
|
||||||
|
while(m_currentNodeName == nnTranslate || m_currentNodeName == nnRotate || m_currentNodeName == nnScale)
|
||||||
|
{
|
||||||
|
if (m_currentNodeName == nnTranslate)
|
||||||
|
{
|
||||||
|
keyframe.position.x = ReadAttribute<float>(anX);
|
||||||
|
keyframe.position.y = ReadAttribute<float>(anY);
|
||||||
|
keyframe.position.z = ReadAttribute<float>(anZ);
|
||||||
|
}
|
||||||
|
else if (m_currentNodeName == nnRotate)
|
||||||
|
{
|
||||||
|
float angle = ReadAttribute<float>("angle");
|
||||||
|
|
||||||
|
if (NextNode() != nnAxis) {
|
||||||
|
throw DeadlyImportError("No axis specified for keyframe rotation in animation " + anim->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
aiVector3D axis;
|
||||||
|
axis.x = ReadAttribute<float>(anX);
|
||||||
|
axis.y = ReadAttribute<float>(anY);
|
||||||
|
axis.z = ReadAttribute<float>(anZ);
|
||||||
|
if (axis.Equal(zeroVec))
|
||||||
|
{
|
||||||
|
axis.x = 1.0f;
|
||||||
|
if (angle != 0) {
|
||||||
|
DefaultLogger::get()->warn("Found invalid a key frame with a zero rotation axis in animation: " + anim->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
keyframe.rotation = aiQuaternion(axis, angle);
|
||||||
|
}
|
||||||
|
else if (m_currentNodeName == nnScale)
|
||||||
|
{
|
||||||
|
keyframe.scale.x = ReadAttribute<float>(anX);
|
||||||
|
keyframe.scale.y = ReadAttribute<float>(anY);
|
||||||
|
keyframe.scale.z = ReadAttribute<float>(anZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
NextNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
dest->transformKeyFrames.push_back(keyframe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OgreXmlSerializer::ReadBoneHierarchy(Skeleton *skeleton)
|
||||||
|
{
|
||||||
|
if (skeleton->bones.empty()) {
|
||||||
|
throw DeadlyImportError("Cannot read <bonehierarchy> for Skeleton without bones");
|
||||||
|
}
|
||||||
|
|
||||||
|
while(NextNode() == nnBoneParent)
|
||||||
|
{
|
||||||
|
const std::string name = ReadAttribute<std::string>("bone");
|
||||||
|
const std::string parentName = ReadAttribute<std::string>("parent");
|
||||||
|
|
||||||
|
Bone *bone = skeleton->BoneByName(name);
|
||||||
|
Bone *parent = skeleton->BoneByName(parentName);
|
||||||
|
|
||||||
|
if (bone && parent)
|
||||||
|
parent->AddChild(bone);
|
||||||
|
else
|
||||||
|
DefaultLogger::get()->warn("Failed to find bones for parenting: Child " + name + " for parent " + parentName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate bone matrices for root bones. Recursively calcutes 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BoneCompare(Bone *a, Bone *b)
|
||||||
|
{
|
||||||
|
return (a->id < b->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OgreXmlSerializer::ReadBones(Skeleton *skeleton)
|
||||||
|
{
|
||||||
|
DefaultLogger::get()->debug(" - Bones");
|
||||||
|
|
||||||
|
NextNode();
|
||||||
|
while(m_currentNodeName == nnBone)
|
||||||
|
{
|
||||||
|
Bone *bone = new Bone();
|
||||||
|
bone->id = ReadAttribute<uint16_t>("id");
|
||||||
|
bone->name = ReadAttribute<std::string>("name");
|
||||||
|
|
||||||
|
NextNode();
|
||||||
|
while(m_currentNodeName == nnPosition || m_currentNodeName == nnRotation)
|
||||||
|
{
|
||||||
|
if (m_currentNodeName == nnPosition)
|
||||||
|
{
|
||||||
|
bone->position.x = ReadAttribute<float>(anX);
|
||||||
|
bone->position.y = ReadAttribute<float>(anY);
|
||||||
|
bone->position.z = ReadAttribute<float>(anZ);
|
||||||
|
}
|
||||||
|
else if (m_currentNodeName == nnRotation)
|
||||||
|
{
|
||||||
|
bone->rotationAngle = ReadAttribute<float>("angle");
|
||||||
|
|
||||||
|
if (NextNode() != nnAxis) {
|
||||||
|
throw DeadlyImportError(Formatter::format() << "No axis specified for bone rotation in bone " << bone->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
bone->rotation.x = ReadAttribute<float>(anX);
|
||||||
|
bone->rotation.y = ReadAttribute<float>(anY);
|
||||||
|
bone->rotation.z = ReadAttribute<float>(anZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
NextNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
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];
|
||||||
|
DefaultLogger::get()->debug(Formatter::format() << " " << b->id << " " << b->name);
|
||||||
|
|
||||||
|
if (b->id != static_cast<uint16_t>(i)) {
|
||||||
|
throw DeadlyImportError(Formatter::format() << "Bone ids are not in sequence starting from 0. Missing index " << i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OgreXmlSerializer::ReadMesh(MeshXml *mesh)
|
||||||
|
{
|
||||||
|
if (NextNode() != nnMesh) {
|
||||||
|
throw DeadlyImportError("Root node is <" + m_currentNodeName + "> expecting <mesh>");
|
||||||
|
}
|
||||||
|
|
||||||
|
DefaultLogger::get()->debug("Reading Mesh");
|
||||||
|
|
||||||
|
NextNode();
|
||||||
|
|
||||||
|
// Root level nodes
|
||||||
|
while(m_currentNodeName == nnSharedGeometry ||
|
||||||
|
m_currentNodeName == nnSubMeshes ||
|
||||||
|
m_currentNodeName == nnSkeletonLink ||
|
||||||
|
m_currentNodeName == nnBoneAssignments ||
|
||||||
|
m_currentNodeName == nnLOD ||
|
||||||
|
m_currentNodeName == nnSubMeshNames ||
|
||||||
|
m_currentNodeName == nnExtremes ||
|
||||||
|
m_currentNodeName == nnPoses ||
|
||||||
|
m_currentNodeName == nnAnimations)
|
||||||
|
{
|
||||||
|
if (m_currentNodeName == nnSharedGeometry)
|
||||||
|
{
|
||||||
|
mesh->sharedVertexData = new VertexDataXml();
|
||||||
|
ReadGeometry(mesh->sharedVertexData);
|
||||||
|
}
|
||||||
|
else if (m_currentNodeName == nnSubMeshes)
|
||||||
|
{
|
||||||
|
NextNode();
|
||||||
|
while(m_currentNodeName == nnSubMesh) {
|
||||||
|
ReadSubMesh(mesh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (m_currentNodeName == nnBoneAssignments)
|
||||||
|
{
|
||||||
|
ReadBoneAssignments(mesh->sharedVertexData);
|
||||||
|
}
|
||||||
|
else if (m_currentNodeName == nnSkeletonLink)
|
||||||
|
{
|
||||||
|
mesh->skeletonRef = ReadAttribute<std::string>("name");
|
||||||
|
DefaultLogger::get()->debug("Read skeleton link " + mesh->skeletonRef);
|
||||||
|
NextNode();
|
||||||
|
}
|
||||||
|
// Assimp incompatible/ignored nodes
|
||||||
|
else
|
||||||
|
SkipCurrentNode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OgreXmlSerializer::ReadGeometry(VertexDataXml *dest)
|
||||||
|
{
|
||||||
|
dest->count = ReadAttribute<uint32_t>("vertexcount");
|
||||||
|
DefaultLogger::get()->debug(Formatter::format() << " - Reading geometry of " << dest->count << " vertices");
|
||||||
|
|
||||||
|
NextNode();
|
||||||
|
while(m_currentNodeName == nnVertexBuffer) {
|
||||||
|
ReadGeometryVertexBuffer(dest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OgreXmlSerializer::ReadGeometryVertexBuffer(VertexDataXml *dest)
|
||||||
|
{
|
||||||
|
bool positions = (HasAttribute("positions") && ReadAttribute<bool>("positions"));
|
||||||
|
bool normals = (HasAttribute("normals") && ReadAttribute<bool>("normals"));
|
||||||
|
bool tangents = (HasAttribute("tangents") && ReadAttribute<bool>("tangents"));
|
||||||
|
uint32_t uvs = (HasAttribute("texture_coords") ? ReadAttribute<uint32_t>("texture_coords") : 0);
|
||||||
|
|
||||||
|
if (!positions) {
|
||||||
|
throw DeadlyImportError("Vertex buffer does not contain positions!");
|
||||||
|
}
|
||||||
|
DefaultLogger::get()->debug(" - Contains positions");
|
||||||
|
dest->positions.reserve(dest->count);
|
||||||
|
|
||||||
|
if (normals)
|
||||||
|
{
|
||||||
|
DefaultLogger::get()->debug(" - Contains normals");
|
||||||
|
dest->normals.reserve(dest->count);
|
||||||
|
}
|
||||||
|
if (tangents)
|
||||||
|
{
|
||||||
|
DefaultLogger::get()->debug(" - Contains tangents");
|
||||||
|
dest->tangents.reserve(dest->count);
|
||||||
|
}
|
||||||
|
if (uvs > 0)
|
||||||
|
{
|
||||||
|
DefaultLogger::get()->debug(Formatter::format() << " - 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool warnBinormal = true;
|
||||||
|
bool warnColorDiffuse = true;
|
||||||
|
bool warnColorSpecular = true;
|
||||||
|
|
||||||
|
NextNode();
|
||||||
|
|
||||||
|
while(m_currentNodeName == nnVertex ||
|
||||||
|
m_currentNodeName == nnPosition ||
|
||||||
|
m_currentNodeName == nnNormal ||
|
||||||
|
m_currentNodeName == nnTangent ||
|
||||||
|
m_currentNodeName == nnBinormal ||
|
||||||
|
m_currentNodeName == nnTexCoord ||
|
||||||
|
m_currentNodeName == nnColorDiffuse ||
|
||||||
|
m_currentNodeName == nnColorSpecular)
|
||||||
|
{
|
||||||
|
if (m_currentNodeName == nnVertex) {
|
||||||
|
NextNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @todo Implement nnBinormal, nnColorDiffuse and nnColorSpecular
|
||||||
|
|
||||||
|
if (positions && m_currentNodeName == nnPosition)
|
||||||
|
{
|
||||||
|
aiVector3D pos;
|
||||||
|
pos.x = ReadAttribute<float>(anX);
|
||||||
|
pos.y = ReadAttribute<float>(anY);
|
||||||
|
pos.z = ReadAttribute<float>(anZ);
|
||||||
|
dest->positions.push_back(pos);
|
||||||
|
}
|
||||||
|
else if (normals && m_currentNodeName == nnNormal)
|
||||||
|
{
|
||||||
|
aiVector3D normal;
|
||||||
|
normal.x = ReadAttribute<float>(anX);
|
||||||
|
normal.y = ReadAttribute<float>(anY);
|
||||||
|
normal.z = ReadAttribute<float>(anZ);
|
||||||
|
dest->normals.push_back(normal);
|
||||||
|
}
|
||||||
|
else if (tangents && m_currentNodeName == nnTangent)
|
||||||
|
{
|
||||||
|
aiVector3D tangent;
|
||||||
|
tangent.x = ReadAttribute<float>(anX);
|
||||||
|
tangent.y = ReadAttribute<float>(anY);
|
||||||
|
tangent.z = ReadAttribute<float>(anZ);
|
||||||
|
dest->tangents.push_back(tangent);
|
||||||
|
}
|
||||||
|
else if (uvs > 0 && m_currentNodeName == nnTexCoord)
|
||||||
|
{
|
||||||
|
for(size_t i=0, len=dest->uvs.size(); i<len; ++i)
|
||||||
|
{
|
||||||
|
if (m_currentNodeName != nnTexCoord) {
|
||||||
|
throw DeadlyImportError("Vertex buffer declared more UVs than can be found in a vertex");
|
||||||
|
}
|
||||||
|
|
||||||
|
aiVector3D uv;
|
||||||
|
uv.x = ReadAttribute<float>("u");
|
||||||
|
uv.y = ReadAttribute<float>("v") * (-1)+1; //flip the uv vertikal, blender exports them so! (ahem... @todo ????)
|
||||||
|
dest->uvs[i].push_back(uv);
|
||||||
|
|
||||||
|
NextNode();
|
||||||
|
}
|
||||||
|
// Continue main loop as above already read next node
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/// @todo Remove this stuff once implemented. We only want to log warnings once per element.
|
||||||
|
bool warn = true;
|
||||||
|
if (m_currentNodeName == nnBinormal)
|
||||||
|
{
|
||||||
|
if (warnBinormal)
|
||||||
|
{
|
||||||
|
warnBinormal = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
warn = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (m_currentNodeName == nnColorDiffuse)
|
||||||
|
{
|
||||||
|
if (warnColorDiffuse)
|
||||||
|
{
|
||||||
|
warnColorDiffuse = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
warn = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (m_currentNodeName == nnColorSpecular)
|
||||||
|
{
|
||||||
|
if (warnColorSpecular)
|
||||||
|
{
|
||||||
|
warnColorSpecular = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
warn = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (warn) {
|
||||||
|
DefaultLogger::get()->warn("Vertex buffer attribute read not implemented for element: " + m_currentNodeName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Advance
|
||||||
|
NextNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanity checks
|
||||||
|
if (dest->positions.size() != dest->count) {
|
||||||
|
throw DeadlyImportError(Formatter::format() << "Read only " << dest->positions.size() << " positions when should have read " << dest->count);
|
||||||
|
}
|
||||||
|
if (normals && dest->normals.size() != dest->count) {
|
||||||
|
throw DeadlyImportError(Formatter::format() << "Read only " << dest->normals.size() << " normals when should have read " << dest->count);
|
||||||
|
}
|
||||||
|
if (tangents && dest->tangents.size() != dest->count) {
|
||||||
|
throw DeadlyImportError(Formatter::format() << "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(Formatter::format() << "Read only " << dest->uvs[i].size()
|
||||||
|
<< " uvs for uv index " << i << " when should have read " << dest->count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OgreXmlSerializer::ReadSubMesh(MeshXml *mesh)
|
||||||
|
{
|
||||||
|
static const std::string anMaterial = "material";
|
||||||
|
static const std::string anUseSharedVertices = "usesharedvertices";
|
||||||
|
static const std::string anCount = "count";
|
||||||
|
static const std::string anV1 = "v1";
|
||||||
|
static const std::string anV2 = "v2";
|
||||||
|
static const std::string anV3 = "v3";
|
||||||
|
static const std::string anV4 = "v4";
|
||||||
|
|
||||||
|
SubMeshXml* submesh = new SubMeshXml();
|
||||||
|
|
||||||
|
if (HasAttribute(anMaterial)) {
|
||||||
|
submesh->materialRef = ReadAttribute<std::string>(anMaterial);
|
||||||
|
}
|
||||||
|
if (HasAttribute(anUseSharedVertices)) {
|
||||||
|
submesh->usesSharedVertexData = ReadAttribute<bool>(anUseSharedVertices);
|
||||||
|
}
|
||||||
|
|
||||||
|
DefaultLogger::get()->debug(Formatter::format() << "Reading SubMesh " << mesh->subMeshes.size());
|
||||||
|
DefaultLogger::get()->debug(Formatter::format() << " - Material: '" << submesh->materialRef << "'");
|
||||||
|
DefaultLogger::get()->debug(Formatter::format() << " - Uses shared geometry: " << (submesh->usesSharedVertexData ? "true" : "false"));
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
NextNode();
|
||||||
|
while(m_currentNodeName == nnFaces ||
|
||||||
|
m_currentNodeName == nnGeometry ||
|
||||||
|
m_currentNodeName == nnTextures ||
|
||||||
|
m_currentNodeName == nnBoneAssignments)
|
||||||
|
{
|
||||||
|
if (m_currentNodeName == nnFaces)
|
||||||
|
{
|
||||||
|
submesh->indexData->faceCount = ReadAttribute<uint32_t>(anCount);
|
||||||
|
submesh->indexData->faces.reserve(submesh->indexData->faceCount);
|
||||||
|
|
||||||
|
NextNode();
|
||||||
|
while(m_currentNodeName == nnFace)
|
||||||
|
{
|
||||||
|
aiFace face;
|
||||||
|
face.mNumIndices = 3;
|
||||||
|
face.mIndices = new unsigned int[3];
|
||||||
|
face.mIndices[0] = ReadAttribute<uint32_t>(anV1);
|
||||||
|
face.mIndices[1] = ReadAttribute<uint32_t>(anV2);
|
||||||
|
face.mIndices[2] = ReadAttribute<uint32_t>(anV3);
|
||||||
|
|
||||||
|
/// @todo Support quads if Ogre even supports them in XML (I'm not sure but I doubt it)
|
||||||
|
if (!quadWarned && HasAttribute(anV4)) {
|
||||||
|
DefaultLogger::get()->warn("Submesh <face> has quads with <v4>, only triangles are supported at the moment!");
|
||||||
|
quadWarned = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
submesh->indexData->faces.push_back(face);
|
||||||
|
|
||||||
|
// Advance
|
||||||
|
NextNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (submesh->indexData->faces.size() == submesh->indexData->faceCount)
|
||||||
|
{
|
||||||
|
DefaultLogger::get()->debug(Formatter::format() << " - Faces " << submesh->indexData->faceCount);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw DeadlyImportError(Formatter::format() << "Read only " << submesh->indexData->faces.size() << " faces when should have read " << submesh->indexData->faceCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (m_currentNodeName == nnGeometry)
|
||||||
|
{
|
||||||
|
if (submesh->usesSharedVertexData) {
|
||||||
|
throw DeadlyImportError("Found <geometry> in <submesh> when use shared geometry is true. Invalid mesh file.");
|
||||||
|
}
|
||||||
|
|
||||||
|
submesh->vertexData = new VertexDataXml();
|
||||||
|
ReadGeometry(submesh->vertexData);
|
||||||
|
}
|
||||||
|
else if (m_currentNodeName == nnBoneAssignments)
|
||||||
|
{
|
||||||
|
ReadBoneAssignments(submesh->vertexData);
|
||||||
|
}
|
||||||
|
// Assimp incompatible/ignored nodes
|
||||||
|
else
|
||||||
|
SkipCurrentNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
submesh->index = mesh->subMeshes.size();
|
||||||
|
mesh->subMeshes.push_back(submesh);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OgreXmlSerializer::ReadBoneAssignments(VertexDataXml *dest)
|
||||||
|
{
|
||||||
|
if (!dest) {
|
||||||
|
throw DeadlyImportError("Cannot read bone assignments, vertex data is null.");
|
||||||
|
}
|
||||||
|
|
||||||
|
static const std::string anVertexIndex = "vertexindex";
|
||||||
|
static const std::string anBoneIndex = "boneindex";
|
||||||
|
static const std::string anWeight = "weight";
|
||||||
|
|
||||||
|
std::set<uint32_t> influencedVertices;
|
||||||
|
|
||||||
|
NextNode();
|
||||||
|
while(m_currentNodeName == nnVertexBoneAssignment)
|
||||||
|
{
|
||||||
|
VertexBoneAssignment ba;
|
||||||
|
ba.vertexIndex = ReadAttribute<uint32_t>(anVertexIndex);
|
||||||
|
ba.boneIndex = ReadAttribute<uint16_t>(anBoneIndex);
|
||||||
|
ba.weight = ReadAttribute<float>(anWeight);
|
||||||
|
dest->boneAssignments.push_back(ba);
|
||||||
|
|
||||||
|
influencedVertices.insert(ba.vertexIndex);
|
||||||
|
NextNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Normalize bone weights.
|
||||||
|
Some exporters wont care if the sum of all bone weights
|
||||||
|
for a single vertex equals 1 or not, so validate here. */
|
||||||
|
const float epsilon = 0.05f;
|
||||||
|
for(std::set<uint32_t>::const_iterator iter=influencedVertices.begin(), end=influencedVertices.end(); iter != end; ++iter)
|
||||||
|
{
|
||||||
|
const uint32_t vertexIndex = (*iter);
|
||||||
|
|
||||||
|
float sum = 0.0f;
|
||||||
|
for (VertexBoneAssignmentList::const_iterator baIter=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 (VertexBoneAssignmentList::iterator baIter=dest->boneAssignments.begin(), baEnd=dest->boneAssignments.end(); baIter != baEnd; ++baIter)
|
||||||
|
{
|
||||||
|
if (baIter->vertexIndex == vertexIndex)
|
||||||
|
baIter->weight /= sum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DefaultLogger::get()->debug(Formatter::format() << " - " << dest->boneAssignments.size() << " bone assignments");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // Ogre
|
||||||
|
} // Assimp
|
||||||
|
|
||||||
|
#endif // ASSIMP_BUILD_NO_OGRE_IMPORTER
|
||||||
|
|
|
@ -38,5 +38,70 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** @todo Move XML related serialization from OgreImporter.cpp
|
#ifndef AI_OGREXMLSERIALIZER_H_INC
|
||||||
here in a similar fashion as OgreBinarySerializer. */
|
#define AI_OGREXMLSERIALIZER_H_INC
|
||||||
|
|
||||||
|
#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER
|
||||||
|
|
||||||
|
#include "OgreStructs.h"
|
||||||
|
#include "OgreParsingUtils.h"
|
||||||
|
|
||||||
|
#include "irrXMLWrapper.h"
|
||||||
|
|
||||||
|
namespace Assimp
|
||||||
|
{
|
||||||
|
namespace Ogre
|
||||||
|
{
|
||||||
|
|
||||||
|
typedef irr::io::IrrXMLReader XmlReader;
|
||||||
|
|
||||||
|
class OgreXmlSerializer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static MeshXml *ImportMesh(XmlReader *reader);
|
||||||
|
static void ImportSkeleton(Assimp::IOSystem *pIOHandler, MeshXml *mesh);
|
||||||
|
|
||||||
|
private:
|
||||||
|
OgreXmlSerializer(XmlReader *reader) :
|
||||||
|
m_reader(reader)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mesh
|
||||||
|
void ReadMesh(MeshXml *mesh);
|
||||||
|
void ReadSubMesh(MeshXml *mesh);
|
||||||
|
|
||||||
|
void ReadGeometry(VertexDataXml *dest);
|
||||||
|
void ReadGeometryVertexBuffer(VertexDataXml *dest);
|
||||||
|
|
||||||
|
void ReadBoneAssignments(VertexDataXml *dest);
|
||||||
|
|
||||||
|
// Skeleton
|
||||||
|
void ReadSkeleton(Skeleton *skeleton);
|
||||||
|
|
||||||
|
void ReadBones(Skeleton *skeleton);
|
||||||
|
void ReadBoneHierarchy(Skeleton *skeleton);
|
||||||
|
|
||||||
|
void ReadAnimations(Skeleton *skeleton);
|
||||||
|
void ReadAnimationTracks(Animation *dest);
|
||||||
|
void ReadAnimationKeyFrames(Animation *anim, VertexAnimationTrack *dest);
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T ReadAttribute(const std::string &name) const;
|
||||||
|
bool HasAttribute(const std::string &name) const;
|
||||||
|
|
||||||
|
std::string &NextNode();
|
||||||
|
std::string &SkipCurrentNode();
|
||||||
|
|
||||||
|
bool CurrentNodeNameEquals(const std::string &name) const;
|
||||||
|
std::string CurrentNodeName(bool forceRead = false);
|
||||||
|
|
||||||
|
XmlReader *m_reader;
|
||||||
|
std::string m_currentNodeName;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // Ogre
|
||||||
|
} // Assimp
|
||||||
|
|
||||||
|
#endif // ASSIMP_BUILD_NO_OGRE_IMPORTER
|
||||||
|
#endif // AI_OGREXMLSERIALIZER_H_INC
|
||||||
|
|
Loading…
Reference in New Issue