Ogre Importer: Support for Shared Geometry, as exported by Blender2Ogre for Blender 2.6 (still experimental)
CleanUp for OgreImporter (splitted the big file in smaller ones) git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@1190 67173fc5-114c-0410-ac8e-9d2fd5bffc1fpull/5/head
parent
7a0c5a431e
commit
3ed356d3c4
|
@ -329,7 +329,9 @@ SET( Ogre_SRCS
|
|||
OgreImporter.hpp
|
||||
OgreXmlHelper.hpp
|
||||
OgreImporter.cpp
|
||||
OgreImporterMaterial.cpp
|
||||
OgreMaterial.cpp
|
||||
OgreMesh.cpp
|
||||
OgreSkeleton.cpp
|
||||
)
|
||||
SOURCE_GROUP( Ogre FILES ${Ogre_SRCS})
|
||||
|
||||
|
|
|
@ -48,16 +48,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
#include <sstream>
|
||||
using namespace std;
|
||||
|
||||
//#include "boost/format.hpp"
|
||||
//#include "boost/foreach.hpp"
|
||||
//using namespace boost;
|
||||
|
||||
#include "TinyFormatter.h"
|
||||
|
||||
#include "OgreImporter.hpp"
|
||||
#include "TinyFormatter.h"
|
||||
#include "irrXMLWrapper.h"
|
||||
|
||||
|
||||
namespace Assimp
|
||||
{
|
||||
namespace Ogre
|
||||
|
@ -106,8 +100,21 @@ void OgreImporter::InternReadFile(const std::string &pFile, aiScene *pScene, Ass
|
|||
throw DeadlyImportError("Root Node is not <mesh>! "+pFile+" "+MeshFile->getNodeName());
|
||||
}
|
||||
|
||||
//eventually load shared geometry
|
||||
XmlRead(MeshFile);//shared geometry is optional, so we need a reed for the next two if's
|
||||
if(MeshFile->getNodeName()==string("sharedgeometry"))
|
||||
{
|
||||
unsigned int NumVertices=GetAttribute<int>(MeshFile, "vertexcount");;
|
||||
|
||||
XmlRead(MeshFile);
|
||||
while(MeshFile->getNodeName()==string("vertexbuffer"))
|
||||
{
|
||||
ReadVertexBuffer(m_SharedGeometry, MeshFile, NumVertices);
|
||||
}
|
||||
}
|
||||
|
||||
//Go to the submeshs:
|
||||
if(!(XmlRead(MeshFile) && string(MeshFile->getNodeName())=="submeshes"))
|
||||
if(MeshFile->getNodeName()!=string("submeshes"))
|
||||
{
|
||||
throw DeadlyImportError("No <submeshes> node in <mesh> node! "+pFile);
|
||||
}
|
||||
|
@ -209,793 +216,6 @@ void OgreImporter::SetupProperties(const Importer* pImp)
|
|||
}
|
||||
|
||||
|
||||
void OgreImporter::ReadSubMesh(SubMesh &theSubMesh, XmlReader *Reader)
|
||||
{
|
||||
XmlRead(Reader);
|
||||
//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
|
||||
while(Reader->getNodeName()==string("faces") || string(Reader->getNodeName())=="geometry" || Reader->getNodeName()==string("boneassignments"))
|
||||
{
|
||||
if(string(Reader->getNodeName())=="faces")//Read the face list
|
||||
{
|
||||
//some info logging:
|
||||
unsigned int NumFaces=GetAttribute<int>(Reader, "count");
|
||||
ostringstream ss; ss <<"Submesh has " << NumFaces << " Faces.";
|
||||
DefaultLogger::get()->debug(ss.str());
|
||||
|
||||
while(XmlRead(Reader) && Reader->getNodeName()==string("face"))
|
||||
{
|
||||
Face NewFace;
|
||||
NewFace.VertexIndices[0]=GetAttribute<int>(Reader, "v1");
|
||||
NewFace.VertexIndices[1]=GetAttribute<int>(Reader, "v2");
|
||||
NewFace.VertexIndices[2]=GetAttribute<int>(Reader, "v3");
|
||||
if(Reader->getAttributeValue("v4"))//this should be supported in the future
|
||||
{
|
||||
throw DeadlyImportError("Submesh has quads, only traingles are supported!");
|
||||
}
|
||||
theSubMesh.FaceList.push_back(NewFace);
|
||||
}
|
||||
|
||||
}//end of faces
|
||||
else if(string(Reader->getNodeName())=="geometry")//Read the vertexdata
|
||||
{
|
||||
//some info logging:
|
||||
unsigned int NumVertices=GetAttribute<int>(Reader, "vertexcount");
|
||||
ostringstream ss; ss<<"VertexCount: " << NumVertices;
|
||||
DefaultLogger::get()->debug(ss.str());
|
||||
|
||||
//General Informations about vertices
|
||||
XmlRead(Reader);
|
||||
while(Reader->getNodeName()==string("vertexbuffer"))
|
||||
{
|
||||
ReadVertexBuffer(theSubMesh, Reader, NumVertices);
|
||||
}
|
||||
|
||||
//some error checking on the loaded data
|
||||
if(!theSubMesh.HasPositions)
|
||||
throw DeadlyImportError("No positions could be loaded!");
|
||||
|
||||
if(theSubMesh.HasNormals && theSubMesh.Normals.size() != NumVertices)
|
||||
throw DeadlyImportError("Wrong Number of Normals loaded!");
|
||||
|
||||
if(theSubMesh.HasTangents && theSubMesh.Tangents.size() != NumVertices)
|
||||
throw DeadlyImportError("Wrong Number of Tangents loaded!");
|
||||
|
||||
if(theSubMesh.NumUvs==1 && theSubMesh.Uvs.size() != NumVertices)
|
||||
throw DeadlyImportError("Wrong Number of Uvs loaded!");
|
||||
|
||||
}//end of "geometry
|
||||
|
||||
|
||||
else if(string(Reader->getNodeName())=="boneassignments")
|
||||
{
|
||||
theSubMesh.Weights.resize(theSubMesh.Positions.size());
|
||||
while(XmlRead(Reader) && Reader->getNodeName()==string("vertexboneassignment"))
|
||||
{
|
||||
Weight NewWeight;
|
||||
unsigned int VertexId=GetAttribute<int>(Reader, "vertexindex");
|
||||
NewWeight.BoneId=GetAttribute<int>(Reader, "boneindex");
|
||||
NewWeight.Value=GetAttribute<float>(Reader, "weight");
|
||||
theSubMesh.BonesUsed=max(theSubMesh.BonesUsed, NewWeight.BoneId+1);//calculate the number of bones used (this is the highest id +1 becuase bone ids start at 0)
|
||||
|
||||
theSubMesh.Weights[VertexId].push_back(NewWeight);
|
||||
|
||||
//XmlRead(Reader);//Once i had this line, and than i got only every second boneassignment, but my first test models had even boneassignment counts, so i thougt, everything would work. And yes, i HATE irrXML!!!
|
||||
}
|
||||
|
||||
}//end of boneassignments
|
||||
}
|
||||
DefaultLogger::get()->debug((Formatter::format(),
|
||||
"Positionen: ",theSubMesh.Positions.size(),
|
||||
" Normale: ",theSubMesh.Normals.size(),
|
||||
" TexCoords: ",theSubMesh.Uvs.size(),
|
||||
" Tantents: ",theSubMesh.Tangents.size()
|
||||
));
|
||||
DefaultLogger::get()->warn(Reader->getNodeName());
|
||||
|
||||
|
||||
|
||||
//---------------Make all Vertexes unique: (this is required by assimp)-----------------------
|
||||
vector<Face> UniqueFaceList(theSubMesh.FaceList.size());
|
||||
unsigned int UniqueVertexCount=theSubMesh.FaceList.size()*3;//*3 because each face consists of 3 vertexes, because we only support triangles^^
|
||||
vector<aiVector3D> UniquePositions(UniqueVertexCount);
|
||||
vector<aiVector3D> UniqueNormals(UniqueVertexCount);
|
||||
vector<aiVector3D> UniqueTangents(UniqueVertexCount);
|
||||
vector<aiVector3D> UniqueUvs(UniqueVertexCount);
|
||||
vector< vector<Weight> > UniqueWeights((theSubMesh.Weights.size() ? UniqueVertexCount : 0));
|
||||
|
||||
for(unsigned int i=0; i<theSubMesh.FaceList.size(); ++i)
|
||||
{
|
||||
//We precalculate the index vlaues her, because we need them in all vertex attributes
|
||||
unsigned int Vertex1=theSubMesh.FaceList[i].VertexIndices[0];
|
||||
unsigned int Vertex2=theSubMesh.FaceList[i].VertexIndices[1];
|
||||
unsigned int Vertex3=theSubMesh.FaceList[i].VertexIndices[2];
|
||||
|
||||
UniquePositions[3*i+0]=theSubMesh.Positions[Vertex1];
|
||||
UniquePositions[3*i+1]=theSubMesh.Positions[Vertex2];
|
||||
UniquePositions[3*i+2]=theSubMesh.Positions[Vertex3];
|
||||
|
||||
if(theSubMesh.HasNormals)
|
||||
{
|
||||
UniqueNormals[3*i+0]=theSubMesh.Normals[Vertex1];
|
||||
UniqueNormals[3*i+1]=theSubMesh.Normals[Vertex2];
|
||||
UniqueNormals[3*i+2]=theSubMesh.Normals[Vertex3];
|
||||
}
|
||||
|
||||
if(theSubMesh.HasTangents)
|
||||
{
|
||||
UniqueTangents[3*i+0]=theSubMesh.Tangents[Vertex1];
|
||||
UniqueTangents[3*i+1]=theSubMesh.Tangents[Vertex2];
|
||||
UniqueTangents[3*i+2]=theSubMesh.Tangents[Vertex3];
|
||||
}
|
||||
|
||||
if(1==theSubMesh.NumUvs)
|
||||
{
|
||||
UniqueUvs[3*i+0]=theSubMesh.Uvs[Vertex1];
|
||||
UniqueUvs[3*i+1]=theSubMesh.Uvs[Vertex2];
|
||||
UniqueUvs[3*i+2]=theSubMesh.Uvs[Vertex3];
|
||||
}
|
||||
|
||||
if(theSubMesh.Weights.size())
|
||||
{
|
||||
UniqueWeights[3*i+0]=theSubMesh.Weights[Vertex1];
|
||||
UniqueWeights[3*i+1]=theSubMesh.Weights[Vertex2];
|
||||
UniqueWeights[3*i+2]=theSubMesh.Weights[Vertex3];
|
||||
}
|
||||
|
||||
//The indexvalues a just continuous numbers (0, 1, 2, 3, 4, 5, 6...)
|
||||
UniqueFaceList[i].VertexIndices[0]=3*i+0;
|
||||
UniqueFaceList[i].VertexIndices[1]=3*i+1;
|
||||
UniqueFaceList[i].VertexIndices[2]=3*i+2;
|
||||
}
|
||||
//_________________________________________________________________________________________
|
||||
|
||||
//now we have the unique datas, 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
|
||||
theSubMesh.FaceList.swap(UniqueFaceList);
|
||||
theSubMesh.Positions.swap(UniquePositions);
|
||||
theSubMesh.Normals.swap(UniqueNormals);
|
||||
theSubMesh.Tangents.swap(UniqueTangents);
|
||||
theSubMesh.Uvs.swap(UniqueUvs);
|
||||
theSubMesh.Weights.swap(UniqueWeights);
|
||||
|
||||
//------------- normalize weights -----------------------------
|
||||
//The Blender exporter doesn't care about whether the sum of all boneweights for a single vertex equals 1 or not,
|
||||
//so we have to make this sure:
|
||||
for(unsigned int VertexId=0; VertexId<theSubMesh.Weights.size(); ++VertexId)//iterate over all vertices
|
||||
{
|
||||
float WeightSum=0.0f;
|
||||
for(unsigned int BoneId=0; BoneId<theSubMesh.Weights[VertexId].size(); ++BoneId)//iterate over all bones
|
||||
{
|
||||
WeightSum+=theSubMesh.Weights[VertexId][BoneId].Value;
|
||||
}
|
||||
|
||||
//check if the sum is too far away from 1
|
||||
if(WeightSum<1.0f-0.05f || WeightSum>1.0f+0.05f)
|
||||
{
|
||||
//normalize all weights:
|
||||
for(unsigned int BoneId=0; BoneId<theSubMesh.Weights[VertexId].size(); ++BoneId)//iterate over all bones
|
||||
{
|
||||
theSubMesh.Weights[VertexId][BoneId].Value/=WeightSum;
|
||||
}
|
||||
}
|
||||
}
|
||||
//_________________________________________________________
|
||||
}
|
||||
|
||||
|
||||
void OgreImporter::ReadVertexBuffer(SubMesh &theSubMesh, XmlReader *Reader, unsigned int NumVertices)
|
||||
{
|
||||
DefaultLogger::get()->debug("new Vertex Buffer");
|
||||
|
||||
bool ReadPositions=false;
|
||||
bool ReadNormals=false;
|
||||
bool ReadTangents=false;
|
||||
bool ReadUvs=false;
|
||||
|
||||
//-------------------- check, what we need to read: --------------------------------
|
||||
if(Reader->getAttributeValue("positions") && GetAttribute<bool>(Reader, "positions"))
|
||||
{
|
||||
ReadPositions=theSubMesh.HasPositions=true;
|
||||
theSubMesh.Positions.reserve(NumVertices);
|
||||
DefaultLogger::get()->debug("reading positions");
|
||||
}
|
||||
if(Reader->getAttributeValue("normals") && GetAttribute<bool>(Reader, "normals"))
|
||||
{
|
||||
ReadNormals=theSubMesh.HasNormals=true;
|
||||
theSubMesh.Normals.reserve(NumVertices);
|
||||
DefaultLogger::get()->debug("reading positions");
|
||||
}
|
||||
if(Reader->getAttributeValue("tangents") && GetAttribute<bool>(Reader, "tangents"))
|
||||
{
|
||||
ReadTangents=theSubMesh.HasTangents=true;
|
||||
theSubMesh.Tangents.reserve(NumVertices);
|
||||
DefaultLogger::get()->debug("reading positions");
|
||||
}
|
||||
|
||||
|
||||
//we can have 1 or 0 uv channels, and if the mesh has no uvs, it also doesn't have the attribute
|
||||
if(!Reader->getAttributeValue("texture_coords"))
|
||||
theSubMesh.NumUvs=0;
|
||||
else
|
||||
{
|
||||
ReadUvs=!!(theSubMesh.NumUvs=GetAttribute<int>(Reader, "texture_coords"));
|
||||
theSubMesh.Uvs.reserve(NumVertices);
|
||||
DefaultLogger::get()->debug("reading texture coords");
|
||||
}
|
||||
if(theSubMesh.NumUvs>1)
|
||||
{
|
||||
DefaultLogger::get()->warn("too many texcoords (just 1 supported!), just the first texcoords will be loaded!");
|
||||
theSubMesh.NumUvs=1;
|
||||
}
|
||||
//___________________________________________________________________
|
||||
|
||||
|
||||
//check if we will load anything
|
||||
if(!(ReadPositions || ReadNormals || ReadTangents || ReadUvs))
|
||||
DefaultLogger::get()->warn("vertexbuffer seams to be empty!");
|
||||
|
||||
|
||||
//read all the vertices:
|
||||
XmlRead(Reader);
|
||||
|
||||
/*it might happen, that we have more than one attribute per vertex (they are not splitted to different buffers)
|
||||
so the break condition is a bit tricky (well, IrrXML just sucks :( )*/
|
||||
while(Reader->getNodeName()==string("vertex")
|
||||
||Reader->getNodeName()==string("position")
|
||||
||Reader->getNodeName()==string("normal")
|
||||
||Reader->getNodeName()==string("tangent")
|
||||
||Reader->getNodeName()==string("texcoord"))
|
||||
{
|
||||
if(Reader->getNodeName()==string("vertex"))
|
||||
XmlRead(Reader);//Read an attribute tag
|
||||
|
||||
//Position
|
||||
if(ReadPositions && Reader->getNodeName()==string("position"))
|
||||
{
|
||||
aiVector3D NewPos;
|
||||
NewPos.x=GetAttribute<float>(Reader, "x");
|
||||
NewPos.y=GetAttribute<float>(Reader, "y");
|
||||
NewPos.z=GetAttribute<float>(Reader, "z");
|
||||
theSubMesh.Positions.push_back(NewPos);
|
||||
}
|
||||
|
||||
//Normal
|
||||
else if(ReadNormals && Reader->getNodeName()==string("normal"))
|
||||
{
|
||||
aiVector3D NewNormal;
|
||||
NewNormal.x=GetAttribute<float>(Reader, "x");
|
||||
NewNormal.y=GetAttribute<float>(Reader, "y");
|
||||
NewNormal.z=GetAttribute<float>(Reader, "z");
|
||||
theSubMesh.Normals.push_back(NewNormal);
|
||||
}
|
||||
|
||||
//Tangent
|
||||
else if(ReadTangents && Reader->getNodeName()==string("tangent"))
|
||||
{
|
||||
aiVector3D NewTangent;
|
||||
NewTangent.x=GetAttribute<float>(Reader, "x");
|
||||
NewTangent.y=GetAttribute<float>(Reader, "y");
|
||||
NewTangent.z=GetAttribute<float>(Reader, "z");
|
||||
theSubMesh.Tangents.push_back(NewTangent);
|
||||
}
|
||||
|
||||
//Uv:
|
||||
else if(ReadUvs && Reader->getNodeName()==string("texcoord"))
|
||||
{
|
||||
aiVector3D NewUv;
|
||||
NewUv.x=GetAttribute<float>(Reader, "u");
|
||||
NewUv.y=GetAttribute<float>(Reader, "v")*(-1)+1;//flip the uv vertikal, blender exports them so!
|
||||
theSubMesh.Uvs.push_back(NewUv);
|
||||
|
||||
//skip all the following texcoords:
|
||||
while(Reader->getNodeName()==string("texcoord"))
|
||||
XmlRead(Reader);
|
||||
continue;//don't read another line at the end of the loop
|
||||
}
|
||||
|
||||
//Attribute could not be read
|
||||
else
|
||||
{
|
||||
DefaultLogger::get()->warn(string("Attribute was not read: ")+Reader->getNodeName());
|
||||
}
|
||||
|
||||
XmlRead(Reader);//Read the Vertex tag
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
aiMesh* OgreImporter::CreateAssimpSubMesh(const SubMesh& theSubMesh, const vector<Bone>& Bones) const
|
||||
{
|
||||
const aiScene* const m_CurrentScene=this->m_CurrentScene;//make sure, that we can access but not change the scene
|
||||
(void)m_CurrentScene;
|
||||
|
||||
aiMesh* NewAiMesh=new aiMesh();
|
||||
|
||||
//Positions
|
||||
NewAiMesh->mVertices=new aiVector3D[theSubMesh.Positions.size()];
|
||||
memcpy(NewAiMesh->mVertices, &theSubMesh.Positions[0], theSubMesh.Positions.size()*sizeof(aiVector3D));
|
||||
NewAiMesh->mNumVertices=theSubMesh.Positions.size();
|
||||
|
||||
//Normals
|
||||
if(theSubMesh.HasNormals)
|
||||
{
|
||||
NewAiMesh->mNormals=new aiVector3D[theSubMesh.Normals.size()];
|
||||
memcpy(NewAiMesh->mNormals, &theSubMesh.Normals[0], theSubMesh.Normals.size()*sizeof(aiVector3D));
|
||||
}
|
||||
|
||||
|
||||
//until we have support for bitangents, no tangents will be written
|
||||
/*
|
||||
//Tangents
|
||||
if(theSubMesh.HasTangents)
|
||||
{
|
||||
NewAiMesh->mTangents=new aiVector3D[theSubMesh.Tangents.size()];
|
||||
memcpy(NewAiMesh->mTangents, &theSubMesh.Tangents[0], theSubMesh.Tangents.size()*sizeof(aiVector3D));
|
||||
}
|
||||
*/
|
||||
|
||||
//Uvs
|
||||
if(0!=theSubMesh.NumUvs)
|
||||
{
|
||||
NewAiMesh->mNumUVComponents[0]=2;
|
||||
NewAiMesh->mTextureCoords[0]= new aiVector3D[theSubMesh.Uvs.size()];
|
||||
memcpy(NewAiMesh->mTextureCoords[0], &theSubMesh.Uvs[0], theSubMesh.Uvs.size()*sizeof(aiVector3D));
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------- Bones --------------------------------------------
|
||||
|
||||
//Copy the weights in in Bone-Vertices Struktur
|
||||
//(we have them in a Vertex-Bones Structur, this is much easier for making them unique, which is required by assimp
|
||||
vector< vector<aiVertexWeight> > aiWeights(theSubMesh.BonesUsed);//now the outer list are the bones, and the inner vector the vertices
|
||||
for(unsigned int VertexId=0; VertexId<theSubMesh.Weights.size(); ++VertexId)//iterate over all vertices
|
||||
{
|
||||
for(unsigned int BoneId=0; BoneId<theSubMesh.Weights[VertexId].size(); ++BoneId)//iterate over all bones
|
||||
{
|
||||
aiVertexWeight NewWeight;
|
||||
NewWeight.mVertexId=VertexId;//the current Vertex, we can't use the Id form the submehs weights, because they are bone id's
|
||||
NewWeight.mWeight=theSubMesh.Weights[VertexId][BoneId].Value;
|
||||
aiWeights[theSubMesh.Weights[VertexId][BoneId].BoneId].push_back(NewWeight);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
vector<aiBone*> aiBones;
|
||||
aiBones.reserve(theSubMesh.BonesUsed);//the vector might be smaller, because there might be empty bones (bones that are not attached to any vertex)
|
||||
|
||||
//create all the bones and fill them with informations
|
||||
for(unsigned int i=0; i<theSubMesh.BonesUsed; ++i)
|
||||
{
|
||||
if(aiWeights[i].size()>0)
|
||||
{
|
||||
aiBone* NewBone=new aiBone();
|
||||
NewBone->mNumWeights=aiWeights[i].size();
|
||||
NewBone->mWeights=new aiVertexWeight[aiWeights[i].size()];
|
||||
memcpy(NewBone->mWeights, &(aiWeights[i][0]), sizeof(aiVertexWeight)*aiWeights[i].size());
|
||||
NewBone->mName=Bones[i].Name;//The bone list should be sorted after its id's, this was done in LoadSkeleton
|
||||
NewBone->mOffsetMatrix=Bones[i].BoneToWorldSpace;
|
||||
|
||||
aiBones.push_back(NewBone);
|
||||
}
|
||||
}
|
||||
NewAiMesh->mNumBones=aiBones.size();
|
||||
|
||||
// mBones must be NULL if mNumBones is non 0 or the validation fails.
|
||||
if (aiBones.size()) {
|
||||
NewAiMesh->mBones=new aiBone* [aiBones.size()];
|
||||
memcpy(NewAiMesh->mBones, &(aiBones[0]), aiBones.size()*sizeof(aiBone*));
|
||||
}
|
||||
|
||||
//______________________________________________________________________________________________________
|
||||
|
||||
|
||||
|
||||
//Faces
|
||||
NewAiMesh->mFaces=new aiFace[theSubMesh.FaceList.size()];
|
||||
for(unsigned int i=0; i<theSubMesh.FaceList.size(); ++i)
|
||||
{
|
||||
NewAiMesh->mFaces[i].mNumIndices=3;
|
||||
NewAiMesh->mFaces[i].mIndices=new unsigned int[3];
|
||||
|
||||
NewAiMesh->mFaces[i].mIndices[0]=theSubMesh.FaceList[i].VertexIndices[0];
|
||||
NewAiMesh->mFaces[i].mIndices[1]=theSubMesh.FaceList[i].VertexIndices[1];
|
||||
NewAiMesh->mFaces[i].mIndices[2]=theSubMesh.FaceList[i].VertexIndices[2];
|
||||
}
|
||||
NewAiMesh->mNumFaces=theSubMesh.FaceList.size();
|
||||
|
||||
//Link the material:
|
||||
NewAiMesh->mMaterialIndex=theSubMesh.MaterialIndex;//the index is set by the function who called ReadSubMesh
|
||||
|
||||
return NewAiMesh;
|
||||
}
|
||||
|
||||
|
||||
void OgreImporter::LoadSkeleton(std::string FileName, vector<Bone> &Bones, vector<Animation> &Animations) const
|
||||
{
|
||||
const aiScene* const m_CurrentScene=this->m_CurrentScene;//make sure, that we can access but not change the scene
|
||||
(void)m_CurrentScene;
|
||||
|
||||
|
||||
//most likely the skeleton file will only end with .skeleton
|
||||
//But this is a xml reader, so we need: .skeleton.xml
|
||||
FileName+=".xml";
|
||||
|
||||
DefaultLogger::get()->debug(string("Loading Skeleton: ")+FileName);
|
||||
|
||||
//Open the File:
|
||||
boost::scoped_ptr<IOStream> File(m_CurrentIOHandler->Open(FileName));
|
||||
if(NULL==File.get())
|
||||
throw DeadlyImportError("Failed to open skeleton file "+FileName+".");
|
||||
|
||||
//Read the Mesh File:
|
||||
boost::scoped_ptr<CIrrXML_IOStreamReader> mIOWrapper(new CIrrXML_IOStreamReader(File.get()));
|
||||
XmlReader* SkeletonFile = irr::io::createIrrXMLReader(mIOWrapper.get());
|
||||
if(!SkeletonFile)
|
||||
throw DeadlyImportError(string("Failed to create XML Reader for ")+FileName);
|
||||
|
||||
//Quick note: Whoever read this should know this one thing: irrXml fucking sucks!!!
|
||||
|
||||
XmlRead(SkeletonFile);
|
||||
if(string("skeleton")!=SkeletonFile->getNodeName())
|
||||
throw DeadlyImportError("No <skeleton> node in SkeletonFile: "+FileName);
|
||||
|
||||
|
||||
|
||||
//------------------------------------load bones-----------------------------------------
|
||||
XmlRead(SkeletonFile);
|
||||
if(string("bones")!=SkeletonFile->getNodeName())
|
||||
throw DeadlyImportError("No bones node in skeleton "+FileName);
|
||||
|
||||
XmlRead(SkeletonFile);
|
||||
|
||||
while(string("bone")==SkeletonFile->getNodeName())
|
||||
{
|
||||
//TODO: Maybe we can have bone ids for the errrors, but normaly, they should never appear, so what....
|
||||
|
||||
//read a new bone:
|
||||
Bone NewBone;
|
||||
NewBone.Id=GetAttribute<int>(SkeletonFile, "id");
|
||||
NewBone.Name=GetAttribute<string>(SkeletonFile, "name");
|
||||
|
||||
//load the position:
|
||||
XmlRead(SkeletonFile);
|
||||
if(string("position")!=SkeletonFile->getNodeName())
|
||||
throw DeadlyImportError("Position is not first node in Bone!");
|
||||
NewBone.Position.x=GetAttribute<float>(SkeletonFile, "x");
|
||||
NewBone.Position.y=GetAttribute<float>(SkeletonFile, "y");
|
||||
NewBone.Position.z=GetAttribute<float>(SkeletonFile, "z");
|
||||
|
||||
//Rotation:
|
||||
XmlRead(SkeletonFile);
|
||||
if(string("rotation")!=SkeletonFile->getNodeName())
|
||||
throw DeadlyImportError("Rotation is not the second node in Bone!");
|
||||
NewBone.RotationAngle=GetAttribute<float>(SkeletonFile, "angle");
|
||||
XmlRead(SkeletonFile);
|
||||
if(string("axis")!=SkeletonFile->getNodeName())
|
||||
throw DeadlyImportError("No axis specified for bone rotation!");
|
||||
NewBone.RotationAxis.x=GetAttribute<float>(SkeletonFile, "x");
|
||||
NewBone.RotationAxis.y=GetAttribute<float>(SkeletonFile, "y");
|
||||
NewBone.RotationAxis.z=GetAttribute<float>(SkeletonFile, "z");
|
||||
|
||||
//append the newly loaded bone to the bone list
|
||||
Bones.push_back(NewBone);
|
||||
|
||||
//Proceed to the next bone:
|
||||
XmlRead(SkeletonFile);
|
||||
}
|
||||
//The bones in the file a not neccesarly ordered by there id's so we do it now:
|
||||
std::sort(Bones.begin(), Bones.end());
|
||||
|
||||
//now the id of each bone should be equal to its position in the vector:
|
||||
//so we do a simple check:
|
||||
{
|
||||
bool IdsOk=true;
|
||||
for(int i=0; i<static_cast<signed int>(Bones.size()); ++i)//i is signed, because all Id's are also signed!
|
||||
{
|
||||
if(Bones[i].Id!=i)
|
||||
IdsOk=false;
|
||||
}
|
||||
if(!IdsOk)
|
||||
throw DeadlyImportError("Bone Ids are not valid!"+FileName);
|
||||
}
|
||||
DefaultLogger::get()->debug((Formatter::format(),"Number of bones: ",Bones.size()));
|
||||
//________________________________________________________________________________
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//----------------------------load bonehierarchy--------------------------------
|
||||
if(string("bonehierarchy")!=SkeletonFile->getNodeName())
|
||||
throw DeadlyImportError("no bonehierarchy node in "+FileName);
|
||||
|
||||
DefaultLogger::get()->debug("loading bonehierarchy...");
|
||||
XmlRead(SkeletonFile);
|
||||
while(string("boneparent")==SkeletonFile->getNodeName())
|
||||
{
|
||||
string Child, Parent;
|
||||
Child=GetAttribute<string>(SkeletonFile, "bone");
|
||||
Parent=GetAttribute<string>(SkeletonFile, "parent");
|
||||
|
||||
unsigned int ChildId, ParentId;
|
||||
ChildId=find(Bones.begin(), Bones.end(), Child)->Id;
|
||||
ParentId=find(Bones.begin(), Bones.end(), Parent)->Id;
|
||||
|
||||
Bones[ChildId].ParentId=ParentId;
|
||||
Bones[ParentId].Children.push_back(ChildId);
|
||||
|
||||
XmlRead(SkeletonFile);//i once forget this line, which led to an endless loop, did i mentioned, that irrxml sucks??
|
||||
}
|
||||
//_____________________________________________________________________________
|
||||
|
||||
|
||||
//--------- Calculate the WorldToBoneSpace Matrix recursivly for all bones: ------------------
|
||||
BOOST_FOREACH(Bone theBone, Bones)
|
||||
{
|
||||
if(-1==theBone.ParentId) //the bone is a root bone
|
||||
{
|
||||
theBone.CalculateBoneToWorldSpaceMatrix(Bones);
|
||||
}
|
||||
}
|
||||
//_______________________________________________________________________
|
||||
|
||||
|
||||
//---------------------------load animations-----------------------------
|
||||
if(string("animations")==SkeletonFile->getNodeName())//animations are optional values
|
||||
{
|
||||
DefaultLogger::get()->debug("Loading Animations");
|
||||
XmlRead(SkeletonFile);
|
||||
while(string("animation")==SkeletonFile->getNodeName())
|
||||
{
|
||||
Animation NewAnimation;
|
||||
NewAnimation.Name=GetAttribute<string>(SkeletonFile, "name");
|
||||
NewAnimation.Length=GetAttribute<float>(SkeletonFile, "length");
|
||||
|
||||
//Load all Tracks
|
||||
XmlRead(SkeletonFile);
|
||||
if(string("tracks")!=SkeletonFile->getNodeName())
|
||||
throw DeadlyImportError("no tracks node in animation");
|
||||
XmlRead(SkeletonFile);
|
||||
while(string("track")==SkeletonFile->getNodeName())
|
||||
{
|
||||
Track NewTrack;
|
||||
NewTrack.BoneName=GetAttribute<string>(SkeletonFile, "bone");
|
||||
|
||||
//Load all keyframes;
|
||||
XmlRead(SkeletonFile);
|
||||
if(string("keyframes")!=SkeletonFile->getNodeName())
|
||||
throw DeadlyImportError("no keyframes node!");
|
||||
XmlRead(SkeletonFile);
|
||||
while(string("keyframe")==SkeletonFile->getNodeName())
|
||||
{
|
||||
Keyframe NewKeyframe;
|
||||
NewKeyframe.Time=GetAttribute<float>(SkeletonFile, "time");
|
||||
|
||||
//loop over the attributes:
|
||||
|
||||
while(true)
|
||||
{
|
||||
XmlRead(SkeletonFile);
|
||||
|
||||
//If any property doesn't show up, it will keep its initialization value
|
||||
|
||||
//Position:
|
||||
if(string("translate")==SkeletonFile->getNodeName())
|
||||
{
|
||||
NewKeyframe.Position.x=GetAttribute<float>(SkeletonFile, "x");
|
||||
NewKeyframe.Position.y=GetAttribute<float>(SkeletonFile, "y");
|
||||
NewKeyframe.Position.z=GetAttribute<float>(SkeletonFile, "z");
|
||||
}
|
||||
|
||||
//Rotation:
|
||||
else if(string("rotate")!=SkeletonFile->getNodeName())
|
||||
{
|
||||
float RotationAngle=GetAttribute<float>(SkeletonFile, "angle");
|
||||
aiVector3D RotationAxis;
|
||||
XmlRead(SkeletonFile);
|
||||
if(string("axis")!=SkeletonFile->getNodeName())
|
||||
throw DeadlyImportError("No axis for keyframe rotation!");
|
||||
RotationAxis.x=GetAttribute<float>(SkeletonFile, "x");
|
||||
RotationAxis.y=GetAttribute<float>(SkeletonFile, "y");
|
||||
RotationAxis.z=GetAttribute<float>(SkeletonFile, "z");
|
||||
NewKeyframe.Rotation=aiQuaternion(RotationAxis, RotationAngle);
|
||||
}
|
||||
|
||||
//Scaling:
|
||||
else if(string("scale")==SkeletonFile->getNodeName())
|
||||
{
|
||||
NewKeyframe.Scaling.x=GetAttribute<float>(SkeletonFile, "x");
|
||||
NewKeyframe.Scaling.y=GetAttribute<float>(SkeletonFile, "y");
|
||||
NewKeyframe.Scaling.z=GetAttribute<float>(SkeletonFile, "z");
|
||||
}
|
||||
|
||||
//we suppose, that we read all attributes and this is a new keyframe or the end of the animation
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
NewTrack.Keyframes.push_back(NewKeyframe);
|
||||
XmlRead(SkeletonFile);
|
||||
}
|
||||
|
||||
|
||||
NewAnimation.Tracks.push_back(NewTrack);
|
||||
}
|
||||
|
||||
Animations.push_back(NewAnimation);
|
||||
}
|
||||
}
|
||||
//_____________________________________________________________________________
|
||||
|
||||
}
|
||||
|
||||
|
||||
void OgreImporter::CreateAssimpSkeleton(const std::vector<Bone> &Bones, const std::vector<Animation> &/*Animations*/)
|
||||
{
|
||||
if(!m_CurrentScene->mRootNode)
|
||||
throw DeadlyImportError("No root node exists!!");
|
||||
if(0!=m_CurrentScene->mRootNode->mNumChildren)
|
||||
throw DeadlyImportError("Root Node already has childnodes!");
|
||||
|
||||
|
||||
//Createt the assimp bone hierarchy
|
||||
DefaultLogger::get()->debug("Root Bones");
|
||||
vector<aiNode*> RootBoneNodes;
|
||||
BOOST_FOREACH(Bone theBone, Bones)
|
||||
{
|
||||
if(-1==theBone.ParentId) //the bone is a root bone
|
||||
{
|
||||
DefaultLogger::get()->debug(theBone.Name);
|
||||
RootBoneNodes.push_back(CreateAiNodeFromBone(theBone.Id, Bones, m_CurrentScene->mRootNode));//which will recursily add all other nodes
|
||||
}
|
||||
}
|
||||
|
||||
if (RootBoneNodes.size()) {
|
||||
m_CurrentScene->mRootNode->mNumChildren=RootBoneNodes.size();
|
||||
m_CurrentScene->mRootNode->mChildren=new aiNode*[RootBoneNodes.size()];
|
||||
memcpy(m_CurrentScene->mRootNode->mChildren, &RootBoneNodes[0], sizeof(aiNode*)*RootBoneNodes.size());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OgreImporter::PutAnimationsInScene(const std::vector<Bone> &Bones, const std::vector<Animation> &Animations)
|
||||
{
|
||||
//-----------------Create the Assimp Animations --------------------
|
||||
if(Animations.size()>0)//Maybe the model had only a skeleton and no animations. (If it also has no skeleton, this function would'nt have been called
|
||||
{
|
||||
m_CurrentScene->mNumAnimations=Animations.size();
|
||||
m_CurrentScene->mAnimations=new aiAnimation*[Animations.size()];
|
||||
for(unsigned int i=0; i<Animations.size(); ++i)//create all animations
|
||||
{
|
||||
aiAnimation* NewAnimation=new aiAnimation();
|
||||
NewAnimation->mName=Animations[i].Name;
|
||||
NewAnimation->mDuration=Animations[i].Length;
|
||||
NewAnimation->mTicksPerSecond=1.0f;
|
||||
|
||||
//Create all tracks in this animation
|
||||
NewAnimation->mNumChannels=Animations[i].Tracks.size();
|
||||
NewAnimation->mChannels=new aiNodeAnim*[Animations[i].Tracks.size()];
|
||||
for(unsigned int j=0; j<Animations[i].Tracks.size(); ++j)
|
||||
{
|
||||
aiNodeAnim* NewNodeAnim=new aiNodeAnim();
|
||||
NewNodeAnim->mNodeName=Animations[i].Tracks[j].BoneName;
|
||||
|
||||
//we need this, to acces the bones default pose, which we need to make keys absolute
|
||||
vector<Bone>::const_iterator CurBone=find(Bones.begin(), Bones.end(), NewNodeAnim->mNodeName);
|
||||
aiMatrix4x4 t0, t1;
|
||||
aiMatrix4x4 DefBonePose=//The default bone pose doesnt have a scaling value
|
||||
aiMatrix4x4::Rotation(CurBone->RotationAngle, CurBone->RotationAxis, t0)
|
||||
* aiMatrix4x4::Translation(CurBone->Position, t1);
|
||||
|
||||
//Create the keyframe arrays...
|
||||
unsigned int KeyframeCount=Animations[i].Tracks[j].Keyframes.size();
|
||||
NewNodeAnim->mNumPositionKeys=KeyframeCount;
|
||||
NewNodeAnim->mPositionKeys=new aiVectorKey[KeyframeCount];
|
||||
NewNodeAnim->mNumRotationKeys=KeyframeCount;
|
||||
NewNodeAnim->mRotationKeys=new aiQuatKey[KeyframeCount];
|
||||
NewNodeAnim->mNumScalingKeys=KeyframeCount;
|
||||
NewNodeAnim->mScalingKeys=new aiVectorKey[KeyframeCount];
|
||||
|
||||
//...and fill them
|
||||
for(unsigned int k=0; k<KeyframeCount; ++k)
|
||||
{
|
||||
aiMatrix4x4 t2, t3;
|
||||
|
||||
//Create a matrix to transfrom a vector from the bones default pose to the bone bones in this animation key
|
||||
aiMatrix4x4 PoseToKey=aiMatrix4x4::Scaling(Animations[i].Tracks[j].Keyframes[k].Scaling, t2) //scale
|
||||
* aiMatrix4x4(Animations[i].Tracks[j].Keyframes[k].Rotation.GetMatrix()) //rot
|
||||
* aiMatrix4x4::Translation(Animations[i].Tracks[j].Keyframes[k].Position, t3); //pos
|
||||
|
||||
|
||||
//calculate the complete transformation from world space to bone space
|
||||
aiMatrix4x4 CompleteTransform=DefBonePose * PoseToKey;
|
||||
|
||||
aiVector3D Pos;
|
||||
aiQuaternion Rot;
|
||||
aiVector3D Scale;
|
||||
|
||||
CompleteTransform.Decompose(Scale, Rot, Pos);
|
||||
|
||||
|
||||
NewNodeAnim->mPositionKeys[k].mTime=Animations[i].Tracks[j].Keyframes[k].Time;
|
||||
NewNodeAnim->mPositionKeys[k].mValue=Pos;
|
||||
|
||||
NewNodeAnim->mRotationKeys[k].mTime=Animations[i].Tracks[j].Keyframes[k].Time;
|
||||
NewNodeAnim->mRotationKeys[k].mValue=Rot;
|
||||
|
||||
NewNodeAnim->mScalingKeys[k].mTime=Animations[i].Tracks[j].Keyframes[k].Time;
|
||||
NewNodeAnim->mScalingKeys[k].mValue=Scale;
|
||||
}
|
||||
|
||||
NewAnimation->mChannels[j]=NewNodeAnim;
|
||||
}
|
||||
|
||||
m_CurrentScene->mAnimations[i]=NewAnimation;
|
||||
}
|
||||
}
|
||||
//TODO: Auf nicht vorhandene Animationskeys achten!
|
||||
//#pragma warning (s.o.)
|
||||
//__________________________________________________________________
|
||||
}
|
||||
|
||||
|
||||
aiNode* OgreImporter::CreateAiNodeFromBone(int BoneId, const std::vector<Bone> &Bones, aiNode* ParentNode)
|
||||
{
|
||||
//----Create the node for this bone and set its values-----
|
||||
aiNode* NewNode=new aiNode(Bones[BoneId].Name);
|
||||
NewNode->mParent=ParentNode;
|
||||
|
||||
aiMatrix4x4 t0,t1;
|
||||
//create a matrix from the transformation values of the ogre bone
|
||||
NewNode->mTransformation=aiMatrix4x4::Rotation(Bones[BoneId].RotationAngle, Bones[BoneId].RotationAxis, t1)
|
||||
* aiMatrix4x4::Translation(Bones[BoneId].Position, t0)
|
||||
|
||||
;
|
||||
//__________________________________________________________
|
||||
|
||||
|
||||
//---------- recursivly create all children Nodes: ----------
|
||||
NewNode->mNumChildren=Bones[BoneId].Children.size();
|
||||
NewNode->mChildren=new aiNode*[Bones[BoneId].Children.size()];
|
||||
for(unsigned int i=0; i<Bones[BoneId].Children.size(); ++i)
|
||||
{
|
||||
NewNode->mChildren[i]=CreateAiNodeFromBone(Bones[BoneId].Children[i], Bones, NewNode);
|
||||
}
|
||||
//____________________________________________________
|
||||
|
||||
|
||||
return NewNode;
|
||||
}
|
||||
|
||||
|
||||
void Bone::CalculateBoneToWorldSpaceMatrix(vector<Bone> &Bones)
|
||||
{
|
||||
//Calculate the matrix for this bone:
|
||||
|
||||
aiMatrix4x4 t0,t1;
|
||||
aiMatrix4x4 Transf=aiMatrix4x4::Translation(-Position, t0)
|
||||
* aiMatrix4x4::Rotation(-RotationAngle, RotationAxis, t1)
|
||||
;
|
||||
if(-1==ParentId)
|
||||
{
|
||||
BoneToWorldSpace=Transf;
|
||||
}
|
||||
else
|
||||
{
|
||||
BoneToWorldSpace=Transf*Bones[ParentId].BoneToWorldSpace;
|
||||
}
|
||||
|
||||
//and recursivly for all children:
|
||||
BOOST_FOREACH(int theChildren, Children)
|
||||
{
|
||||
Bones[theChildren].CalculateBoneToWorldSpaceMatrix(Bones);
|
||||
}
|
||||
}
|
||||
|
||||
}//namespace Ogre
|
||||
}//namespace Assimp
|
||||
|
||||
|
|
|
@ -12,7 +12,6 @@ namespace Ogre
|
|||
|
||||
|
||||
//Forward declarations:
|
||||
struct SubMesh;
|
||||
struct Face;
|
||||
struct Weight;
|
||||
struct Bone;
|
||||
|
@ -20,6 +19,27 @@ struct Animation;
|
|||
struct Track;
|
||||
struct Keyframe;
|
||||
|
||||
///A submesh from Ogre
|
||||
struct SubMesh
|
||||
{
|
||||
std::string Name;
|
||||
std::string MaterialName;
|
||||
std::vector<Face> FaceList;
|
||||
|
||||
std::vector<aiVector3D> Positions; bool HasPositions;
|
||||
std::vector<aiVector3D> Normals; bool HasNormals;
|
||||
std::vector<aiVector3D> Tangents; bool HasTangents;
|
||||
std::vector<aiVector3D> Uvs; unsigned int NumUvs;//nearly always 2d, but assimp has always 3d texcoords
|
||||
|
||||
std::vector< std::vector<Weight> > Weights;//a list of bones for each vertex
|
||||
int MaterialIndex;///< The Index in the Assimp Materialarray from the material witch is attached to this submesh
|
||||
unsigned int BonesUsed;//the highest index of a bone from a bone weight, this is needed to create the assimp bone structur (converting from Vertex-Bones to Bone-Vertices)
|
||||
|
||||
SubMesh(): HasPositions(false), HasNormals(false), HasTangents(false),
|
||||
NumUvs(0), MaterialIndex(-1), BonesUsed(0) {}//initialize everything
|
||||
};
|
||||
|
||||
|
||||
///The Main Ogre Importer Class
|
||||
class OgreImporter : public BaseImporter
|
||||
{
|
||||
|
@ -59,26 +79,7 @@ private:
|
|||
std::string m_MaterialLibFilename;
|
||||
IOSystem* m_CurrentIOHandler;
|
||||
aiScene *m_CurrentScene;
|
||||
};
|
||||
|
||||
///A submesh from Ogre
|
||||
struct SubMesh
|
||||
{
|
||||
std::string Name;
|
||||
std::string MaterialName;
|
||||
std::vector<Face> FaceList;
|
||||
|
||||
std::vector<aiVector3D> Positions; bool HasPositions;
|
||||
std::vector<aiVector3D> Normals; bool HasNormals;
|
||||
std::vector<aiVector3D> Tangents; bool HasTangents;
|
||||
std::vector<aiVector3D> Uvs; unsigned int NumUvs;//nearly always 2d, but assimp has always 3d texcoords
|
||||
|
||||
std::vector< std::vector<Weight> > Weights;//a list of bones for each vertex
|
||||
int MaterialIndex;///< The Index in the Assimp Materialarray from the material witch is attached to this submesh
|
||||
unsigned int BonesUsed;//the highest index of a bone from a bone weight, this is needed to create the assimp bone structur (converting from Vertex-Bones to Bone-Vertices)
|
||||
|
||||
SubMesh(): HasPositions(false), HasNormals(false), HasTangents(false),
|
||||
NumUvs(0), MaterialIndex(-1), BonesUsed(0) {}//initialize everything
|
||||
SubMesh m_SharedGeometry;///< we will just use the vertexbuffers of the submesh
|
||||
};
|
||||
|
||||
///For the moment just triangles, no other polygon types!
|
||||
|
|
|
@ -172,7 +172,10 @@ aiMaterial* OgreImporter::LoadMaterial(const std::string MaterialName) const
|
|||
ss >> Line;
|
||||
|
||||
if(Line!="{")
|
||||
throw DeadlyImportError("empty material!");
|
||||
{
|
||||
DefaultLogger::get()->warn("empyt material!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while(Line!="}")//read until the end of the material
|
||||
{
|
||||
|
@ -273,7 +276,10 @@ void OgreImporter::ReadTechnique(stringstream &ss, aiMaterial* NewMaterial)
|
|||
string Line;
|
||||
ss >> Line;
|
||||
if(Line!="{")
|
||||
throw DeadlyImportError("empty technique!");
|
||||
{
|
||||
DefaultLogger::get()->warn("empty technique!");
|
||||
return;
|
||||
}
|
||||
while(Line!="}")//read until the end of the technique
|
||||
{
|
||||
ss >> Line;
|
||||
|
@ -281,7 +287,10 @@ void OgreImporter::ReadTechnique(stringstream &ss, aiMaterial* NewMaterial)
|
|||
{
|
||||
ss >> Line;
|
||||
if(Line!="{")
|
||||
throw DeadlyImportError("empty pass!");
|
||||
{
|
||||
DefaultLogger::get()->warn("empty pass!");
|
||||
return;
|
||||
}
|
||||
while(Line!="}")//read until the end of the pass
|
||||
{
|
||||
ss >> Line;
|
|
@ -0,0 +1,485 @@
|
|||
/*
|
||||
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.hpp"
|
||||
#include "TinyFormatter.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace Assimp
|
||||
{
|
||||
namespace Ogre
|
||||
{
|
||||
|
||||
|
||||
void OgreImporter::ReadSubMesh(SubMesh &theSubMesh, XmlReader *Reader)
|
||||
{
|
||||
//see, if we use shared vertices
|
||||
bool bSharedData=false;
|
||||
if(Reader->getAttributeValue("usesharedvertices"))
|
||||
bSharedData=GetAttribute<bool>(Reader, "usesharedvertices");
|
||||
|
||||
XmlRead(Reader);
|
||||
//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
|
||||
while( Reader->getNodeName()==string("faces")
|
||||
|| Reader->getNodeName()==string("geometry")
|
||||
|| Reader->getNodeName()==string("boneassignments"))
|
||||
{
|
||||
if(string(Reader->getNodeName())=="faces")//Read the face list
|
||||
{
|
||||
//some info logging:
|
||||
unsigned int NumFaces=GetAttribute<int>(Reader, "count");
|
||||
ostringstream ss; ss <<"Submesh has " << NumFaces << " Faces.";
|
||||
DefaultLogger::get()->debug(ss.str());
|
||||
|
||||
while(XmlRead(Reader) && Reader->getNodeName()==string("face"))
|
||||
{
|
||||
Face NewFace;
|
||||
NewFace.VertexIndices[0]=GetAttribute<int>(Reader, "v1");
|
||||
NewFace.VertexIndices[1]=GetAttribute<int>(Reader, "v2");
|
||||
NewFace.VertexIndices[2]=GetAttribute<int>(Reader, "v3");
|
||||
if(Reader->getAttributeValue("v4"))//this should be supported in the future
|
||||
{
|
||||
DefaultLogger::get()->warn("Submesh has quads, only traingles are supported!");
|
||||
//throw DeadlyImportError("Submesh has quads, only traingles are supported!");
|
||||
}
|
||||
theSubMesh.FaceList.push_back(NewFace);
|
||||
}
|
||||
|
||||
}//end of faces
|
||||
else if(string(Reader->getNodeName())=="geometry")//Read the vertexdata
|
||||
{
|
||||
//some info logging:
|
||||
unsigned int NumVertices=GetAttribute<int>(Reader, "vertexcount");
|
||||
ostringstream ss; ss<<"VertexCount: " << NumVertices;
|
||||
DefaultLogger::get()->debug(ss.str());
|
||||
|
||||
//General Informations about vertices
|
||||
XmlRead(Reader);
|
||||
while(Reader->getNodeName()==string("vertexbuffer"))
|
||||
{
|
||||
ReadVertexBuffer(theSubMesh, Reader, NumVertices);
|
||||
}
|
||||
|
||||
//some error checking on the loaded data
|
||||
if(!theSubMesh.HasPositions)
|
||||
throw DeadlyImportError("No positions could be loaded!");
|
||||
|
||||
if(theSubMesh.HasNormals && theSubMesh.Normals.size() != NumVertices)
|
||||
throw DeadlyImportError("Wrong Number of Normals loaded!");
|
||||
|
||||
if(theSubMesh.HasTangents && theSubMesh.Tangents.size() != NumVertices)
|
||||
throw DeadlyImportError("Wrong Number of Tangents loaded!");
|
||||
|
||||
if(theSubMesh.NumUvs==1 && theSubMesh.Uvs.size() != NumVertices)
|
||||
throw DeadlyImportError("Wrong Number of Uvs loaded!");
|
||||
|
||||
}//end of "geometry
|
||||
|
||||
|
||||
else if(string(Reader->getNodeName())=="boneassignments")
|
||||
{
|
||||
theSubMesh.Weights.resize(theSubMesh.Positions.size());
|
||||
while(XmlRead(Reader) && Reader->getNodeName()==string("vertexboneassignment"))
|
||||
{
|
||||
Weight NewWeight;
|
||||
unsigned int VertexId=GetAttribute<int>(Reader, "vertexindex");
|
||||
NewWeight.BoneId=GetAttribute<int>(Reader, "boneindex");
|
||||
NewWeight.Value=GetAttribute<float>(Reader, "weight");
|
||||
theSubMesh.BonesUsed=max(theSubMesh.BonesUsed, NewWeight.BoneId+1);//calculate the number of bones used (this is the highest id +1 becuase bone ids start at 0)
|
||||
|
||||
theSubMesh.Weights[VertexId].push_back(NewWeight);
|
||||
|
||||
//Once i had this line, and than i got only every second boneassignment,
|
||||
//but my first test models had even boneassignment counts, so i thougt, everything would work. And yes, i HATE irrXML!!!
|
||||
//XmlRead(Reader);
|
||||
}
|
||||
|
||||
}//end of boneassignments
|
||||
}
|
||||
DefaultLogger::get()->debug((Formatter::format(),
|
||||
"Positionen: ",theSubMesh.Positions.size(),
|
||||
" Normale: ",theSubMesh.Normals.size(),
|
||||
" TexCoords: ",theSubMesh.Uvs.size(),
|
||||
" Tantents: ",theSubMesh.Tangents.size()
|
||||
));
|
||||
DefaultLogger::get()->warn(Reader->getNodeName());
|
||||
|
||||
|
||||
|
||||
//---------------Make all Vertexes unique: (this is required by assimp)-----------------------
|
||||
vector<Face> UniqueFaceList(theSubMesh.FaceList.size());
|
||||
unsigned int UniqueVertexCount=theSubMesh.FaceList.size()*3;//*3 because each face consists of 3 vertexes, because we only support triangles^^
|
||||
vector<aiVector3D> UniquePositions(UniqueVertexCount);
|
||||
vector<aiVector3D> UniqueNormals(UniqueVertexCount);
|
||||
vector<aiVector3D> UniqueTangents(UniqueVertexCount);
|
||||
vector<aiVector3D> UniqueUvs(UniqueVertexCount);
|
||||
vector< vector<Weight> > UniqueWeights((theSubMesh.Weights.size() ? UniqueVertexCount : 0));
|
||||
|
||||
//Support for shared data:
|
||||
/*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= bSharedData ? m_SharedGeometry : theSubMesh;
|
||||
|
||||
if(VertexSource.NumUvs > 0)
|
||||
{
|
||||
DefaultLogger::get()->error("Not all Uvs will be made unique!");
|
||||
}
|
||||
|
||||
for(unsigned int i=0; i<theSubMesh.FaceList.size(); ++i)
|
||||
{
|
||||
//We precalculate the index vlaues her, because we need them in all vertex attributes
|
||||
unsigned int Vertex1=theSubMesh.FaceList[i].VertexIndices[0];
|
||||
unsigned int Vertex2=theSubMesh.FaceList[i].VertexIndices[1];
|
||||
unsigned int Vertex3=theSubMesh.FaceList[i].VertexIndices[2];
|
||||
|
||||
UniquePositions[3*i+0]=VertexSource.Positions[Vertex1];
|
||||
UniquePositions[3*i+1]=VertexSource.Positions[Vertex2];
|
||||
UniquePositions[3*i+2]=VertexSource.Positions[Vertex3];
|
||||
|
||||
if(VertexSource.HasNormals)
|
||||
{
|
||||
UniqueNormals[3*i+0]=VertexSource.Normals[Vertex1];
|
||||
UniqueNormals[3*i+1]=VertexSource.Normals[Vertex2];
|
||||
UniqueNormals[3*i+2]=VertexSource.Normals[Vertex3];
|
||||
}
|
||||
|
||||
if(VertexSource.HasTangents)
|
||||
{
|
||||
UniqueTangents[3*i+0]=VertexSource.Tangents[Vertex1];
|
||||
UniqueTangents[3*i+1]=VertexSource.Tangents[Vertex2];
|
||||
UniqueTangents[3*i+2]=VertexSource.Tangents[Vertex3];
|
||||
}
|
||||
|
||||
if(VertexSource.NumUvs > 0)
|
||||
{
|
||||
UniqueUvs[3*i+0]=VertexSource.Uvs[Vertex1];
|
||||
UniqueUvs[3*i+1]=VertexSource.Uvs[Vertex2];
|
||||
UniqueUvs[3*i+2]=VertexSource.Uvs[Vertex3];
|
||||
}
|
||||
|
||||
if(VertexSource.Weights.size())
|
||||
{
|
||||
//I don't think, that bone assinements can be shared, but who knows?
|
||||
UniqueWeights[3*i+0]=VertexSource.Weights[Vertex1];
|
||||
UniqueWeights[3*i+1]=VertexSource.Weights[Vertex2];
|
||||
UniqueWeights[3*i+2]=VertexSource.Weights[Vertex3];
|
||||
}
|
||||
|
||||
//The indexvalues a just continuous numbers (0, 1, 2, 3, 4, 5, 6...)
|
||||
UniqueFaceList[i].VertexIndices[0]=3*i+0;
|
||||
UniqueFaceList[i].VertexIndices[1]=3*i+1;
|
||||
UniqueFaceList[i].VertexIndices[2]=3*i+2;
|
||||
}
|
||||
//_________________________________________________________________________________________
|
||||
|
||||
//now we have the unique datas, 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
|
||||
theSubMesh.FaceList.swap(UniqueFaceList);
|
||||
theSubMesh.Positions.swap(UniquePositions);
|
||||
theSubMesh.Normals.swap(UniqueNormals);
|
||||
theSubMesh.Tangents.swap(UniqueTangents);
|
||||
theSubMesh.Uvs.swap(UniqueUvs);
|
||||
theSubMesh.Weights.swap(UniqueWeights);
|
||||
|
||||
//------------- normalize weights -----------------------------
|
||||
//The Blender exporter doesn't care about whether the sum of all boneweights for a single vertex equals 1 or not,
|
||||
//so we have to make this sure:
|
||||
for(unsigned int VertexId=0; VertexId<theSubMesh.Weights.size(); ++VertexId)//iterate over all vertices
|
||||
{
|
||||
float WeightSum=0.0f;
|
||||
for(unsigned int BoneId=0; BoneId<theSubMesh.Weights[VertexId].size(); ++BoneId)//iterate over all bones
|
||||
{
|
||||
WeightSum+=theSubMesh.Weights[VertexId][BoneId].Value;
|
||||
}
|
||||
|
||||
//check if the sum is too far away from 1
|
||||
if(WeightSum<1.0f-0.05f || WeightSum>1.0f+0.05f)
|
||||
{
|
||||
//normalize all weights:
|
||||
for(unsigned int BoneId=0; BoneId<theSubMesh.Weights[VertexId].size(); ++BoneId)//iterate over all bones
|
||||
{
|
||||
theSubMesh.Weights[VertexId][BoneId].Value/=WeightSum;
|
||||
}
|
||||
}
|
||||
}
|
||||
//_________________________________________________________
|
||||
}
|
||||
|
||||
|
||||
void OgreImporter::ReadVertexBuffer(SubMesh &theSubMesh, XmlReader *Reader, unsigned int NumVertices)
|
||||
{
|
||||
DefaultLogger::get()->debug("new Vertex Buffer");
|
||||
|
||||
bool ReadPositions=false;
|
||||
bool ReadNormals=false;
|
||||
bool ReadTangents=false;
|
||||
bool ReadUvs=false;
|
||||
|
||||
//-------------------- check, what we need to read: --------------------------------
|
||||
if(Reader->getAttributeValue("positions") && GetAttribute<bool>(Reader, "positions"))
|
||||
{
|
||||
ReadPositions=theSubMesh.HasPositions=true;
|
||||
theSubMesh.Positions.reserve(NumVertices);
|
||||
DefaultLogger::get()->debug("reading positions");
|
||||
}
|
||||
if(Reader->getAttributeValue("normals") && GetAttribute<bool>(Reader, "normals"))
|
||||
{
|
||||
ReadNormals=theSubMesh.HasNormals=true;
|
||||
theSubMesh.Normals.reserve(NumVertices);
|
||||
DefaultLogger::get()->debug("reading normals");
|
||||
}
|
||||
if(Reader->getAttributeValue("tangents") && GetAttribute<bool>(Reader, "tangents"))
|
||||
{
|
||||
ReadTangents=theSubMesh.HasTangents=true;
|
||||
theSubMesh.Tangents.reserve(NumVertices);
|
||||
DefaultLogger::get()->debug("reading tangents");
|
||||
}
|
||||
|
||||
|
||||
//we can have 1 or 0 uv channels, and if the mesh has no uvs, it also doesn't have the attribute
|
||||
if(!Reader->getAttributeValue("texture_coords"))
|
||||
theSubMesh.NumUvs=0;
|
||||
else
|
||||
{
|
||||
ReadUvs=!!(theSubMesh.NumUvs=GetAttribute<int>(Reader, "texture_coords"));
|
||||
theSubMesh.Uvs.reserve(NumVertices);
|
||||
DefaultLogger::get()->debug("reading texture coords");
|
||||
}
|
||||
if(theSubMesh.NumUvs>1)
|
||||
{
|
||||
DefaultLogger::get()->warn("too many texcoords (just 1 supported!), just the first texcoords will be loaded!");
|
||||
theSubMesh.NumUvs=1;
|
||||
}
|
||||
//___________________________________________________________________
|
||||
|
||||
|
||||
//check if we will load anything
|
||||
if(!(ReadPositions || ReadNormals || ReadTangents || ReadUvs))
|
||||
DefaultLogger::get()->warn("vertexbuffer seams to be empty!");
|
||||
|
||||
|
||||
//read all the vertices:
|
||||
XmlRead(Reader);
|
||||
|
||||
/*it might happen, that we have more than one attribute per vertex (they are not splitted to different buffers)
|
||||
so the break condition is a bit tricky (well, IrrXML just sucks :( )*/
|
||||
while(Reader->getNodeName()==string("vertex")
|
||||
||Reader->getNodeName()==string("position")
|
||||
||Reader->getNodeName()==string("normal")
|
||||
||Reader->getNodeName()==string("tangent")
|
||||
||Reader->getNodeName()==string("texcoord"))
|
||||
{
|
||||
if(Reader->getNodeName()==string("vertex"))
|
||||
XmlRead(Reader);//Read an attribute tag
|
||||
|
||||
//Position
|
||||
if(ReadPositions && Reader->getNodeName()==string("position"))
|
||||
{
|
||||
aiVector3D NewPos;
|
||||
NewPos.x=GetAttribute<float>(Reader, "x");
|
||||
NewPos.y=GetAttribute<float>(Reader, "y");
|
||||
NewPos.z=GetAttribute<float>(Reader, "z");
|
||||
theSubMesh.Positions.push_back(NewPos);
|
||||
}
|
||||
|
||||
//Normal
|
||||
else if(ReadNormals && Reader->getNodeName()==string("normal"))
|
||||
{
|
||||
aiVector3D NewNormal;
|
||||
NewNormal.x=GetAttribute<float>(Reader, "x");
|
||||
NewNormal.y=GetAttribute<float>(Reader, "y");
|
||||
NewNormal.z=GetAttribute<float>(Reader, "z");
|
||||
theSubMesh.Normals.push_back(NewNormal);
|
||||
}
|
||||
|
||||
//Tangent
|
||||
else if(ReadTangents && Reader->getNodeName()==string("tangent"))
|
||||
{
|
||||
aiVector3D NewTangent;
|
||||
NewTangent.x=GetAttribute<float>(Reader, "x");
|
||||
NewTangent.y=GetAttribute<float>(Reader, "y");
|
||||
NewTangent.z=GetAttribute<float>(Reader, "z");
|
||||
theSubMesh.Tangents.push_back(NewTangent);
|
||||
}
|
||||
|
||||
//Uv:
|
||||
else if(ReadUvs && Reader->getNodeName()==string("texcoord"))
|
||||
{
|
||||
aiVector3D NewUv;
|
||||
NewUv.x=GetAttribute<float>(Reader, "u");
|
||||
NewUv.y=GetAttribute<float>(Reader, "v")*(-1)+1;//flip the uv vertikal, blender exports them so!
|
||||
theSubMesh.Uvs.push_back(NewUv);
|
||||
|
||||
//skip all the following texcoords:
|
||||
while(Reader->getNodeName()==string("texcoord"))
|
||||
XmlRead(Reader);
|
||||
continue;//don't read another line at the end of the loop
|
||||
}
|
||||
|
||||
//Attribute could not be read
|
||||
else
|
||||
{
|
||||
DefaultLogger::get()->warn(string("Attribute was not read: ")+Reader->getNodeName());
|
||||
}
|
||||
|
||||
XmlRead(Reader);//Read the Vertex tag
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
aiMesh* OgreImporter::CreateAssimpSubMesh(const SubMesh& theSubMesh, const vector<Bone>& Bones) const
|
||||
{
|
||||
const aiScene* const m_CurrentScene=this->m_CurrentScene;//make sure, that we can access but not change the scene
|
||||
(void)m_CurrentScene;
|
||||
|
||||
aiMesh* NewAiMesh=new aiMesh();
|
||||
|
||||
//Positions
|
||||
NewAiMesh->mVertices=new aiVector3D[theSubMesh.Positions.size()];
|
||||
memcpy(NewAiMesh->mVertices, &theSubMesh.Positions[0], theSubMesh.Positions.size()*sizeof(aiVector3D));
|
||||
NewAiMesh->mNumVertices=theSubMesh.Positions.size();
|
||||
|
||||
//Normals
|
||||
if(theSubMesh.HasNormals)
|
||||
{
|
||||
NewAiMesh->mNormals=new aiVector3D[theSubMesh.Normals.size()];
|
||||
memcpy(NewAiMesh->mNormals, &theSubMesh.Normals[0], theSubMesh.Normals.size()*sizeof(aiVector3D));
|
||||
}
|
||||
|
||||
|
||||
//until we have support for bitangents, no tangents will be written
|
||||
/*
|
||||
//Tangents
|
||||
if(theSubMesh.HasTangents)
|
||||
{
|
||||
NewAiMesh->mTangents=new aiVector3D[theSubMesh.Tangents.size()];
|
||||
memcpy(NewAiMesh->mTangents, &theSubMesh.Tangents[0], theSubMesh.Tangents.size()*sizeof(aiVector3D));
|
||||
}
|
||||
*/
|
||||
|
||||
//Uvs
|
||||
if(0!=theSubMesh.NumUvs)
|
||||
{
|
||||
NewAiMesh->mNumUVComponents[0]=2;
|
||||
NewAiMesh->mTextureCoords[0]= new aiVector3D[theSubMesh.Uvs.size()];
|
||||
memcpy(NewAiMesh->mTextureCoords[0], &theSubMesh.Uvs[0], theSubMesh.Uvs.size()*sizeof(aiVector3D));
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------- Bones --------------------------------------------
|
||||
|
||||
//Copy the weights in in Bone-Vertices Struktur
|
||||
//(we have them in a Vertex-Bones Structur, this is much easier for making them unique, which is required by assimp
|
||||
vector< vector<aiVertexWeight> > aiWeights(theSubMesh.BonesUsed);//now the outer list are the bones, and the inner vector the vertices
|
||||
for(unsigned int VertexId=0; VertexId<theSubMesh.Weights.size(); ++VertexId)//iterate over all vertices
|
||||
{
|
||||
for(unsigned int BoneId=0; BoneId<theSubMesh.Weights[VertexId].size(); ++BoneId)//iterate over all bones
|
||||
{
|
||||
aiVertexWeight NewWeight;
|
||||
NewWeight.mVertexId=VertexId;//the current Vertex, we can't use the Id form the submehs weights, because they are bone id's
|
||||
NewWeight.mWeight=theSubMesh.Weights[VertexId][BoneId].Value;
|
||||
aiWeights[theSubMesh.Weights[VertexId][BoneId].BoneId].push_back(NewWeight);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
vector<aiBone*> aiBones;
|
||||
aiBones.reserve(theSubMesh.BonesUsed);//the vector might be smaller, because there might be empty bones (bones that are not attached to any vertex)
|
||||
|
||||
//create all the bones and fill them with informations
|
||||
for(unsigned int i=0; i<theSubMesh.BonesUsed; ++i)
|
||||
{
|
||||
if(aiWeights[i].size()>0)
|
||||
{
|
||||
aiBone* NewBone=new aiBone();
|
||||
NewBone->mNumWeights=aiWeights[i].size();
|
||||
NewBone->mWeights=new aiVertexWeight[aiWeights[i].size()];
|
||||
memcpy(NewBone->mWeights, &(aiWeights[i][0]), sizeof(aiVertexWeight)*aiWeights[i].size());
|
||||
NewBone->mName=Bones[i].Name;//The bone list should be sorted after its id's, this was done in LoadSkeleton
|
||||
NewBone->mOffsetMatrix=Bones[i].BoneToWorldSpace;
|
||||
|
||||
aiBones.push_back(NewBone);
|
||||
}
|
||||
}
|
||||
NewAiMesh->mNumBones=aiBones.size();
|
||||
|
||||
// mBones must be NULL if mNumBones is non 0 or the validation fails.
|
||||
if (aiBones.size()) {
|
||||
NewAiMesh->mBones=new aiBone* [aiBones.size()];
|
||||
memcpy(NewAiMesh->mBones, &(aiBones[0]), aiBones.size()*sizeof(aiBone*));
|
||||
}
|
||||
|
||||
//______________________________________________________________________________________________________
|
||||
|
||||
|
||||
|
||||
//Faces
|
||||
NewAiMesh->mFaces=new aiFace[theSubMesh.FaceList.size()];
|
||||
for(unsigned int i=0; i<theSubMesh.FaceList.size(); ++i)
|
||||
{
|
||||
NewAiMesh->mFaces[i].mNumIndices=3;
|
||||
NewAiMesh->mFaces[i].mIndices=new unsigned int[3];
|
||||
|
||||
NewAiMesh->mFaces[i].mIndices[0]=theSubMesh.FaceList[i].VertexIndices[0];
|
||||
NewAiMesh->mFaces[i].mIndices[1]=theSubMesh.FaceList[i].VertexIndices[1];
|
||||
NewAiMesh->mFaces[i].mIndices[2]=theSubMesh.FaceList[i].VertexIndices[2];
|
||||
}
|
||||
NewAiMesh->mNumFaces=theSubMesh.FaceList.size();
|
||||
|
||||
//Link the material:
|
||||
NewAiMesh->mMaterialIndex=theSubMesh.MaterialIndex;//the index is set by the function who called ReadSubMesh
|
||||
|
||||
return NewAiMesh;
|
||||
}
|
||||
|
||||
|
||||
}//namespace Ogre
|
||||
}//namespace Assimp
|
||||
|
||||
#endif // !! ASSIMP_BUILD_NO_OGRE_IMPORTER
|
|
@ -0,0 +1,445 @@
|
|||
/*
|
||||
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.hpp"
|
||||
#include "TinyFormatter.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace Assimp
|
||||
{
|
||||
namespace Ogre
|
||||
{
|
||||
|
||||
|
||||
|
||||
void OgreImporter::LoadSkeleton(std::string FileName, vector<Bone> &Bones, vector<Animation> &Animations) const
|
||||
{
|
||||
const aiScene* const m_CurrentScene=this->m_CurrentScene;//make sure, that we can access but not change the scene
|
||||
(void)m_CurrentScene;
|
||||
|
||||
|
||||
//most likely the skeleton file will only end with .skeleton
|
||||
//But this is a xml reader, so we need: .skeleton.xml
|
||||
FileName+=".xml";
|
||||
|
||||
DefaultLogger::get()->debug(string("Loading Skeleton: ")+FileName);
|
||||
|
||||
//Open the File:
|
||||
boost::scoped_ptr<IOStream> File(m_CurrentIOHandler->Open(FileName));
|
||||
if(NULL==File.get())
|
||||
throw DeadlyImportError("Failed to open skeleton file "+FileName+".");
|
||||
|
||||
//Read the Mesh File:
|
||||
boost::scoped_ptr<CIrrXML_IOStreamReader> mIOWrapper(new CIrrXML_IOStreamReader(File.get()));
|
||||
XmlReader* SkeletonFile = irr::io::createIrrXMLReader(mIOWrapper.get());
|
||||
if(!SkeletonFile)
|
||||
throw DeadlyImportError(string("Failed to create XML Reader for ")+FileName);
|
||||
|
||||
//Quick note: Whoever read this should know this one thing: irrXml fucking sucks!!!
|
||||
|
||||
XmlRead(SkeletonFile);
|
||||
if(string("skeleton")!=SkeletonFile->getNodeName())
|
||||
throw DeadlyImportError("No <skeleton> node in SkeletonFile: "+FileName);
|
||||
|
||||
|
||||
|
||||
//------------------------------------load bones-----------------------------------------
|
||||
XmlRead(SkeletonFile);
|
||||
if(string("bones")!=SkeletonFile->getNodeName())
|
||||
throw DeadlyImportError("No bones node in skeleton "+FileName);
|
||||
|
||||
XmlRead(SkeletonFile);
|
||||
|
||||
while(string("bone")==SkeletonFile->getNodeName())
|
||||
{
|
||||
//TODO: Maybe we can have bone ids for the errrors, but normaly, they should never appear, so what....
|
||||
|
||||
//read a new bone:
|
||||
Bone NewBone;
|
||||
NewBone.Id=GetAttribute<int>(SkeletonFile, "id");
|
||||
NewBone.Name=GetAttribute<string>(SkeletonFile, "name");
|
||||
|
||||
//load the position:
|
||||
XmlRead(SkeletonFile);
|
||||
if(string("position")!=SkeletonFile->getNodeName())
|
||||
throw DeadlyImportError("Position is not first node in Bone!");
|
||||
NewBone.Position.x=GetAttribute<float>(SkeletonFile, "x");
|
||||
NewBone.Position.y=GetAttribute<float>(SkeletonFile, "y");
|
||||
NewBone.Position.z=GetAttribute<float>(SkeletonFile, "z");
|
||||
|
||||
//Rotation:
|
||||
XmlRead(SkeletonFile);
|
||||
if(string("rotation")!=SkeletonFile->getNodeName())
|
||||
throw DeadlyImportError("Rotation is not the second node in Bone!");
|
||||
NewBone.RotationAngle=GetAttribute<float>(SkeletonFile, "angle");
|
||||
XmlRead(SkeletonFile);
|
||||
if(string("axis")!=SkeletonFile->getNodeName())
|
||||
throw DeadlyImportError("No axis specified for bone rotation!");
|
||||
NewBone.RotationAxis.x=GetAttribute<float>(SkeletonFile, "x");
|
||||
NewBone.RotationAxis.y=GetAttribute<float>(SkeletonFile, "y");
|
||||
NewBone.RotationAxis.z=GetAttribute<float>(SkeletonFile, "z");
|
||||
|
||||
//append the newly loaded bone to the bone list
|
||||
Bones.push_back(NewBone);
|
||||
|
||||
//Proceed to the next bone:
|
||||
XmlRead(SkeletonFile);
|
||||
}
|
||||
//The bones in the file a not neccesarly ordered by there id's so we do it now:
|
||||
std::sort(Bones.begin(), Bones.end());
|
||||
|
||||
//now the id of each bone should be equal to its position in the vector:
|
||||
//so we do a simple check:
|
||||
{
|
||||
bool IdsOk=true;
|
||||
for(int i=0; i<static_cast<signed int>(Bones.size()); ++i)//i is signed, because all Id's are also signed!
|
||||
{
|
||||
if(Bones[i].Id!=i)
|
||||
IdsOk=false;
|
||||
}
|
||||
if(!IdsOk)
|
||||
throw DeadlyImportError("Bone Ids are not valid!"+FileName);
|
||||
}
|
||||
DefaultLogger::get()->debug((Formatter::format(),"Number of bones: ",Bones.size()));
|
||||
//________________________________________________________________________________
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//----------------------------load bonehierarchy--------------------------------
|
||||
if(string("bonehierarchy")!=SkeletonFile->getNodeName())
|
||||
throw DeadlyImportError("no bonehierarchy node in "+FileName);
|
||||
|
||||
DefaultLogger::get()->debug("loading bonehierarchy...");
|
||||
XmlRead(SkeletonFile);
|
||||
while(string("boneparent")==SkeletonFile->getNodeName())
|
||||
{
|
||||
string Child, Parent;
|
||||
Child=GetAttribute<string>(SkeletonFile, "bone");
|
||||
Parent=GetAttribute<string>(SkeletonFile, "parent");
|
||||
|
||||
unsigned int ChildId, ParentId;
|
||||
ChildId=find(Bones.begin(), Bones.end(), Child)->Id;
|
||||
ParentId=find(Bones.begin(), Bones.end(), Parent)->Id;
|
||||
|
||||
Bones[ChildId].ParentId=ParentId;
|
||||
Bones[ParentId].Children.push_back(ChildId);
|
||||
|
||||
XmlRead(SkeletonFile);//i once forget this line, which led to an endless loop, did i mentioned, that irrxml sucks??
|
||||
}
|
||||
//_____________________________________________________________________________
|
||||
|
||||
|
||||
//--------- Calculate the WorldToBoneSpace Matrix recursivly for all bones: ------------------
|
||||
BOOST_FOREACH(Bone theBone, Bones)
|
||||
{
|
||||
if(-1==theBone.ParentId) //the bone is a root bone
|
||||
{
|
||||
theBone.CalculateBoneToWorldSpaceMatrix(Bones);
|
||||
}
|
||||
}
|
||||
//_______________________________________________________________________
|
||||
|
||||
|
||||
//---------------------------load animations-----------------------------
|
||||
if(string("animations")==SkeletonFile->getNodeName())//animations are optional values
|
||||
{
|
||||
DefaultLogger::get()->debug("Loading Animations");
|
||||
XmlRead(SkeletonFile);
|
||||
while(string("animation")==SkeletonFile->getNodeName())
|
||||
{
|
||||
Animation NewAnimation;
|
||||
NewAnimation.Name=GetAttribute<string>(SkeletonFile, "name");
|
||||
NewAnimation.Length=GetAttribute<float>(SkeletonFile, "length");
|
||||
|
||||
//Load all Tracks
|
||||
XmlRead(SkeletonFile);
|
||||
if(string("tracks")!=SkeletonFile->getNodeName())
|
||||
throw DeadlyImportError("no tracks node in animation");
|
||||
XmlRead(SkeletonFile);
|
||||
while(string("track")==SkeletonFile->getNodeName())
|
||||
{
|
||||
Track NewTrack;
|
||||
NewTrack.BoneName=GetAttribute<string>(SkeletonFile, "bone");
|
||||
|
||||
//Load all keyframes;
|
||||
XmlRead(SkeletonFile);
|
||||
if(string("keyframes")!=SkeletonFile->getNodeName())
|
||||
throw DeadlyImportError("no keyframes node!");
|
||||
XmlRead(SkeletonFile);
|
||||
while(string("keyframe")==SkeletonFile->getNodeName())
|
||||
{
|
||||
Keyframe NewKeyframe;
|
||||
NewKeyframe.Time=GetAttribute<float>(SkeletonFile, "time");
|
||||
|
||||
//loop over the attributes:
|
||||
|
||||
while(true)
|
||||
{
|
||||
XmlRead(SkeletonFile);
|
||||
|
||||
//If any property doesn't show up, it will keep its initialization value
|
||||
|
||||
//Position:
|
||||
if(string("translate")==SkeletonFile->getNodeName())
|
||||
{
|
||||
NewKeyframe.Position.x=GetAttribute<float>(SkeletonFile, "x");
|
||||
NewKeyframe.Position.y=GetAttribute<float>(SkeletonFile, "y");
|
||||
NewKeyframe.Position.z=GetAttribute<float>(SkeletonFile, "z");
|
||||
}
|
||||
|
||||
//Rotation:
|
||||
else if(string("rotate")!=SkeletonFile->getNodeName())
|
||||
{
|
||||
float RotationAngle=GetAttribute<float>(SkeletonFile, "angle");
|
||||
aiVector3D RotationAxis;
|
||||
XmlRead(SkeletonFile);
|
||||
if(string("axis")!=SkeletonFile->getNodeName())
|
||||
throw DeadlyImportError("No axis for keyframe rotation!");
|
||||
RotationAxis.x=GetAttribute<float>(SkeletonFile, "x");
|
||||
RotationAxis.y=GetAttribute<float>(SkeletonFile, "y");
|
||||
RotationAxis.z=GetAttribute<float>(SkeletonFile, "z");
|
||||
NewKeyframe.Rotation=aiQuaternion(RotationAxis, RotationAngle);
|
||||
}
|
||||
|
||||
//Scaling:
|
||||
else if(string("scale")==SkeletonFile->getNodeName())
|
||||
{
|
||||
NewKeyframe.Scaling.x=GetAttribute<float>(SkeletonFile, "x");
|
||||
NewKeyframe.Scaling.y=GetAttribute<float>(SkeletonFile, "y");
|
||||
NewKeyframe.Scaling.z=GetAttribute<float>(SkeletonFile, "z");
|
||||
}
|
||||
|
||||
//we suppose, that we read all attributes and this is a new keyframe or the end of the animation
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
NewTrack.Keyframes.push_back(NewKeyframe);
|
||||
XmlRead(SkeletonFile);
|
||||
}
|
||||
|
||||
|
||||
NewAnimation.Tracks.push_back(NewTrack);
|
||||
}
|
||||
|
||||
Animations.push_back(NewAnimation);
|
||||
}
|
||||
}
|
||||
//_____________________________________________________________________________
|
||||
|
||||
}
|
||||
|
||||
|
||||
void OgreImporter::CreateAssimpSkeleton(const std::vector<Bone> &Bones, const std::vector<Animation> &/*Animations*/)
|
||||
{
|
||||
if(!m_CurrentScene->mRootNode)
|
||||
throw DeadlyImportError("No root node exists!!");
|
||||
if(0!=m_CurrentScene->mRootNode->mNumChildren)
|
||||
throw DeadlyImportError("Root Node already has childnodes!");
|
||||
|
||||
|
||||
//Createt the assimp bone hierarchy
|
||||
DefaultLogger::get()->debug("Root Bones");
|
||||
vector<aiNode*> RootBoneNodes;
|
||||
BOOST_FOREACH(Bone theBone, Bones)
|
||||
{
|
||||
if(-1==theBone.ParentId) //the bone is a root bone
|
||||
{
|
||||
DefaultLogger::get()->debug(theBone.Name);
|
||||
RootBoneNodes.push_back(CreateAiNodeFromBone(theBone.Id, Bones, m_CurrentScene->mRootNode));//which will recursily add all other nodes
|
||||
}
|
||||
}
|
||||
|
||||
if (RootBoneNodes.size()) {
|
||||
m_CurrentScene->mRootNode->mNumChildren=RootBoneNodes.size();
|
||||
m_CurrentScene->mRootNode->mChildren=new aiNode*[RootBoneNodes.size()];
|
||||
memcpy(m_CurrentScene->mRootNode->mChildren, &RootBoneNodes[0], sizeof(aiNode*)*RootBoneNodes.size());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OgreImporter::PutAnimationsInScene(const std::vector<Bone> &Bones, const std::vector<Animation> &Animations)
|
||||
{
|
||||
//-----------------Create the Assimp Animations --------------------
|
||||
if(Animations.size()>0)//Maybe the model had only a skeleton and no animations. (If it also has no skeleton, this function would'nt have been called
|
||||
{
|
||||
m_CurrentScene->mNumAnimations=Animations.size();
|
||||
m_CurrentScene->mAnimations=new aiAnimation*[Animations.size()];
|
||||
for(unsigned int i=0; i<Animations.size(); ++i)//create all animations
|
||||
{
|
||||
aiAnimation* NewAnimation=new aiAnimation();
|
||||
NewAnimation->mName=Animations[i].Name;
|
||||
NewAnimation->mDuration=Animations[i].Length;
|
||||
NewAnimation->mTicksPerSecond=1.0f;
|
||||
|
||||
//Create all tracks in this animation
|
||||
NewAnimation->mNumChannels=Animations[i].Tracks.size();
|
||||
NewAnimation->mChannels=new aiNodeAnim*[Animations[i].Tracks.size()];
|
||||
for(unsigned int j=0; j<Animations[i].Tracks.size(); ++j)
|
||||
{
|
||||
aiNodeAnim* NewNodeAnim=new aiNodeAnim();
|
||||
NewNodeAnim->mNodeName=Animations[i].Tracks[j].BoneName;
|
||||
|
||||
//we need this, to acces the bones default pose, which we need to make keys absolute
|
||||
vector<Bone>::const_iterator CurBone=find(Bones.begin(), Bones.end(), NewNodeAnim->mNodeName);
|
||||
aiMatrix4x4 t0, t1;
|
||||
aiMatrix4x4 DefBonePose=//The default bone pose doesnt have a scaling value
|
||||
aiMatrix4x4::Rotation(CurBone->RotationAngle, CurBone->RotationAxis, t0)
|
||||
* aiMatrix4x4::Translation(CurBone->Position, t1);
|
||||
|
||||
//Create the keyframe arrays...
|
||||
unsigned int KeyframeCount=Animations[i].Tracks[j].Keyframes.size();
|
||||
NewNodeAnim->mNumPositionKeys=KeyframeCount;
|
||||
NewNodeAnim->mPositionKeys=new aiVectorKey[KeyframeCount];
|
||||
NewNodeAnim->mNumRotationKeys=KeyframeCount;
|
||||
NewNodeAnim->mRotationKeys=new aiQuatKey[KeyframeCount];
|
||||
NewNodeAnim->mNumScalingKeys=KeyframeCount;
|
||||
NewNodeAnim->mScalingKeys=new aiVectorKey[KeyframeCount];
|
||||
|
||||
//...and fill them
|
||||
for(unsigned int k=0; k<KeyframeCount; ++k)
|
||||
{
|
||||
aiMatrix4x4 t2, t3;
|
||||
|
||||
//Create a matrix to transfrom a vector from the bones default pose to the bone bones in this animation key
|
||||
aiMatrix4x4 PoseToKey=aiMatrix4x4::Scaling(Animations[i].Tracks[j].Keyframes[k].Scaling, t2) //scale
|
||||
* aiMatrix4x4(Animations[i].Tracks[j].Keyframes[k].Rotation.GetMatrix()) //rot
|
||||
* aiMatrix4x4::Translation(Animations[i].Tracks[j].Keyframes[k].Position, t3); //pos
|
||||
|
||||
|
||||
//calculate the complete transformation from world space to bone space
|
||||
aiMatrix4x4 CompleteTransform=DefBonePose * PoseToKey;
|
||||
|
||||
aiVector3D Pos;
|
||||
aiQuaternion Rot;
|
||||
aiVector3D Scale;
|
||||
|
||||
CompleteTransform.Decompose(Scale, Rot, Pos);
|
||||
|
||||
|
||||
NewNodeAnim->mPositionKeys[k].mTime=Animations[i].Tracks[j].Keyframes[k].Time;
|
||||
NewNodeAnim->mPositionKeys[k].mValue=Pos;
|
||||
|
||||
NewNodeAnim->mRotationKeys[k].mTime=Animations[i].Tracks[j].Keyframes[k].Time;
|
||||
NewNodeAnim->mRotationKeys[k].mValue=Rot;
|
||||
|
||||
NewNodeAnim->mScalingKeys[k].mTime=Animations[i].Tracks[j].Keyframes[k].Time;
|
||||
NewNodeAnim->mScalingKeys[k].mValue=Scale;
|
||||
}
|
||||
|
||||
NewAnimation->mChannels[j]=NewNodeAnim;
|
||||
}
|
||||
|
||||
m_CurrentScene->mAnimations[i]=NewAnimation;
|
||||
}
|
||||
}
|
||||
//TODO: Auf nicht vorhandene Animationskeys achten!
|
||||
//#pragma warning (s.o.)
|
||||
//__________________________________________________________________
|
||||
}
|
||||
|
||||
|
||||
aiNode* OgreImporter::CreateAiNodeFromBone(int BoneId, const std::vector<Bone> &Bones, aiNode* ParentNode)
|
||||
{
|
||||
//----Create the node for this bone and set its values-----
|
||||
aiNode* NewNode=new aiNode(Bones[BoneId].Name);
|
||||
NewNode->mParent=ParentNode;
|
||||
|
||||
aiMatrix4x4 t0,t1;
|
||||
//create a matrix from the transformation values of the ogre bone
|
||||
NewNode->mTransformation=aiMatrix4x4::Rotation(Bones[BoneId].RotationAngle, Bones[BoneId].RotationAxis, t1)
|
||||
* aiMatrix4x4::Translation(Bones[BoneId].Position, t0)
|
||||
|
||||
;
|
||||
//__________________________________________________________
|
||||
|
||||
|
||||
//---------- recursivly create all children Nodes: ----------
|
||||
NewNode->mNumChildren=Bones[BoneId].Children.size();
|
||||
NewNode->mChildren=new aiNode*[Bones[BoneId].Children.size()];
|
||||
for(unsigned int i=0; i<Bones[BoneId].Children.size(); ++i)
|
||||
{
|
||||
NewNode->mChildren[i]=CreateAiNodeFromBone(Bones[BoneId].Children[i], Bones, NewNode);
|
||||
}
|
||||
//____________________________________________________
|
||||
|
||||
|
||||
return NewNode;
|
||||
}
|
||||
|
||||
|
||||
void Bone::CalculateBoneToWorldSpaceMatrix(vector<Bone> &Bones)
|
||||
{
|
||||
//Calculate the matrix for this bone:
|
||||
|
||||
aiMatrix4x4 t0,t1;
|
||||
aiMatrix4x4 Transf=aiMatrix4x4::Translation(-Position, t0)
|
||||
* aiMatrix4x4::Rotation(-RotationAngle, RotationAxis, t1)
|
||||
;
|
||||
if(-1==ParentId)
|
||||
{
|
||||
BoneToWorldSpace=Transf;
|
||||
}
|
||||
else
|
||||
{
|
||||
BoneToWorldSpace=Transf*Bones[ParentId].BoneToWorldSpace;
|
||||
}
|
||||
|
||||
//and recursivly for all children:
|
||||
BOOST_FOREACH(int theChildren, Children)
|
||||
{
|
||||
Bones[theChildren].CalculateBoneToWorldSpaceMatrix(Bones);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}//namespace Ogre
|
||||
}//namespace Assimp
|
||||
|
||||
#endif // !! ASSIMP_BUILD_NO_OGRE_IMPORTER
|
Loading…
Reference in New Issue