OgreImporter: Cleanup and bugfixes to OgreSkeleton.cpp. This was actually so badly broken that it did nothing if the mesh referenced a binary skeleton. Now logs a warning for this case and tries to read from .skeleton.xml like the original author intended it to work. The assimp skeleton is still broken, I will fix that later on when I (eventually) get to that part of the code.
parent
45715df263
commit
f5c7b283bc
|
@ -39,6 +39,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "AssimpPCH.h"
|
#include "AssimpPCH.h"
|
||||||
|
|
||||||
#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER
|
#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@ -71,14 +72,10 @@ namespace Ogre
|
||||||
bool OgreImporter::CanRead(const std::string &pFile, Assimp::IOSystem *pIOHandler, bool checkSig) const
|
bool OgreImporter::CanRead(const std::string &pFile, Assimp::IOSystem *pIOHandler, bool checkSig) const
|
||||||
{
|
{
|
||||||
if (!checkSig)
|
if (!checkSig)
|
||||||
{
|
return EndsWith(pFile, ".mesh.xml", false);
|
||||||
string ext = "mesh.xml";
|
|
||||||
int len = ext.length();
|
const char* tokens[] = { "<mesh>" };
|
||||||
string fileExt = ToLower(pFile.substr(pFile.length()-len, len));
|
return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1);
|
||||||
return (ASSIMP_stricmp(fileExt, ext) == 0);
|
|
||||||
}
|
|
||||||
const char* tokens[] = {"<mesh>"};
|
|
||||||
return BaseImporter::SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OgreImporter::InternReadFile(const std::string &pFile, aiScene *pScene, Assimp::IOSystem *pIOHandler)
|
void OgreImporter::InternReadFile(const std::string &pFile, aiScene *pScene, Assimp::IOSystem *pIOHandler)
|
||||||
|
@ -101,7 +98,7 @@ void OgreImporter::InternReadFile(const std::string &pFile, aiScene *pScene, Ass
|
||||||
// Read root node
|
// Read root node
|
||||||
NextNode(reader.get());
|
NextNode(reader.get());
|
||||||
if (!CurrentNodeNameEquals(reader, "mesh"))
|
if (!CurrentNodeNameEquals(reader, "mesh"))
|
||||||
throw DeadlyImportError("Root node is not <mesh> but <" + string(reader->getNodeName()) + ">");
|
throw DeadlyImportError("Root node is not <mesh> but <" + string(reader->getNodeName()) + "> in " + pFile);
|
||||||
|
|
||||||
// Node names
|
// Node names
|
||||||
const string nnSharedGeometry = "sharedgeometry";
|
const string nnSharedGeometry = "sharedgeometry";
|
||||||
|
@ -242,8 +239,7 @@ void OgreImporter::SetupProperties(const Importer* pImp)
|
||||||
m_detectTextureTypeFromFilename = pImp->GetPropertyBool(AI_CONFIG_IMPORT_OGRE_TEXTURETYPE_FROM_FILENAME, false);
|
m_detectTextureTypeFromFilename = pImp->GetPropertyBool(AI_CONFIG_IMPORT_OGRE_TEXTURETYPE_FROM_FILENAME, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // Ogre
|
||||||
|
} // Assimp
|
||||||
|
|
||||||
}//namespace Ogre
|
#endif // ASSIMP_BUILD_NO_OGRE_IMPORTER
|
||||||
}//namespace Assimp
|
|
||||||
|
|
||||||
#endif // !! ASSIMP_BUILD_NO_OGRE_IMPORTER
|
|
||||||
|
|
|
@ -155,7 +155,7 @@ struct BoneAssignment
|
||||||
/// Ogre Bone weight
|
/// Ogre Bone weight
|
||||||
struct BoneWeight
|
struct BoneWeight
|
||||||
{
|
{
|
||||||
/// Bone ID
|
/// Bone Id
|
||||||
unsigned int Id;
|
unsigned int Id;
|
||||||
/// BoneWeight
|
/// BoneWeight
|
||||||
float Value;
|
float Value;
|
||||||
|
@ -187,8 +187,11 @@ struct Bone
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This operator is needed to sort the bones after Id's
|
/// Returns if this bone is parented.
|
||||||
bool operator<(const Bone &other) const { return Id < other.Id; }
|
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>
|
/// 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 std::string& other) const { return Name == other; }
|
||||||
|
|
|
@ -38,11 +38,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
|
||||||
This file contains material related code. This is
|
|
||||||
spilitted up from the main file OgreImporter.cpp
|
|
||||||
to make it shorter easier to maintain.
|
|
||||||
*/
|
|
||||||
#include "AssimpPCH.h"
|
#include "AssimpPCH.h"
|
||||||
|
|
||||||
#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER
|
#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER
|
||||||
|
|
|
@ -548,8 +548,7 @@ aiMesh* OgreImporter::CreateAssimpSubMesh(aiScene *pScene, const SubMesh& submes
|
||||||
return NewAiMesh;
|
return NewAiMesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // Ogre
|
||||||
|
} // Assimp
|
||||||
|
|
||||||
}//namespace Ogre
|
#endif // ASSIMP_BUILD_NO_OGRE_IMPORTER
|
||||||
}//namespace Assimp
|
|
||||||
|
|
||||||
#endif // !! ASSIMP_BUILD_NO_OGRE_IMPORTER
|
|
||||||
|
|
|
@ -118,10 +118,25 @@ static inline std::string ToLower(std::string s)
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
/// Returns if @c s ends with @c suffix. If @c caseSensitive is false, both strings will be lower cased before matching.
|
||||||
// From http://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring
|
static inline bool EndsWith(const std::string &s, const std::string &suffix, bool caseSensitive = true)
|
||||||
|
{
|
||||||
|
if (s.empty() || suffix.empty())
|
||||||
|
return false;
|
||||||
|
else if (s.length() < suffix.length())
|
||||||
|
return false;
|
||||||
|
|
||||||
// trim from start
|
if (!caseSensitive)
|
||||||
|
return EndsWith(ToLower(s), ToLower(suffix), true);
|
||||||
|
|
||||||
|
size_t len = suffix.length();
|
||||||
|
std::string sSuffix = s.substr(s.length()-len, len);
|
||||||
|
return (ASSIMP_stricmp(sSuffix, suffix) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Below trim functions adapted from http://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring
|
||||||
|
|
||||||
|
/// Trim from start
|
||||||
static inline std::string &TrimLeft(std::string &s, bool newlines = true)
|
static inline std::string &TrimLeft(std::string &s, bool newlines = true)
|
||||||
{
|
{
|
||||||
if (!newlines)
|
if (!newlines)
|
||||||
|
@ -131,7 +146,7 @@ static inline std::string &TrimLeft(std::string &s, bool newlines = true)
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
// trim from end
|
/// Trim from end
|
||||||
static inline std::string &TrimRight(std::string &s, bool newlines = true)
|
static inline std::string &TrimRight(std::string &s, bool newlines = true)
|
||||||
{
|
{
|
||||||
if (!newlines)
|
if (!newlines)
|
||||||
|
@ -140,7 +155,8 @@ static inline std::string &TrimRight(std::string &s, bool newlines = true)
|
||||||
s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun(Assimp::IsSpaceOrNewLine<char>))));
|
s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun(Assimp::IsSpaceOrNewLine<char>))));
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
// trim from both ends
|
|
||||||
|
/// Trim from both ends
|
||||||
static inline std::string &Trim(std::string &s, bool newlines = true)
|
static inline std::string &Trim(std::string &s, bool newlines = true)
|
||||||
{
|
{
|
||||||
return TrimLeft(TrimRight(s, newlines), newlines);
|
return TrimLeft(TrimRight(s, newlines), newlines);
|
||||||
|
|
|
@ -55,226 +55,205 @@ namespace Ogre
|
||||||
void OgreImporter::ReadSkeleton(const std::string &pFile, Assimp::IOSystem *pIOHandler, const aiScene *pScene,
|
void OgreImporter::ReadSkeleton(const std::string &pFile, Assimp::IOSystem *pIOHandler, const aiScene *pScene,
|
||||||
const std::string &skeletonFile, vector<Bone> &Bones, vector<Animation> &Animations) const
|
const std::string &skeletonFile, vector<Bone> &Bones, vector<Animation> &Animations) const
|
||||||
{
|
{
|
||||||
//most likely the skeleton file will only end with .skeleton
|
string filename = skeletonFile;
|
||||||
//But this is a xml reader, so we need: .skeleton.xml
|
if (EndsWith(filename, ".skeleton"))
|
||||||
string skeletonPath = skeletonFile + ".xml";
|
{
|
||||||
|
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";
|
||||||
|
}
|
||||||
|
|
||||||
DefaultLogger::get()->debug(string("Loading Skeleton: ")+skeletonFile);
|
if (!pIOHandler->Exists(filename))
|
||||||
|
{
|
||||||
|
DefaultLogger::get()->error("Failed to find skeleton file '" + filename + "', skeleton will be missing.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
//Open the File:
|
boost::scoped_ptr<IOStream> file(pIOHandler->Open(filename));
|
||||||
boost::scoped_ptr<IOStream> File(pIOHandler->Open(skeletonFile));
|
if (!file.get())
|
||||||
if(NULL==File.get())
|
throw DeadlyImportError("Failed to open skeleton file " + filename);
|
||||||
throw DeadlyImportError("Failed to open skeleton file "+skeletonFile+".");
|
|
||||||
|
|
||||||
//Read the Mesh File:
|
boost::scoped_ptr<CIrrXML_IOStreamReader> stream(new CIrrXML_IOStreamReader(file.get()));
|
||||||
boost::scoped_ptr<CIrrXML_IOStreamReader> mIOWrapper(new CIrrXML_IOStreamReader(File.get()));
|
XmlReader* reader = irr::io::createIrrXMLReader(stream.get());
|
||||||
XmlReader* SkeletonFile = irr::io::createIrrXMLReader(mIOWrapper.get());
|
if (!reader)
|
||||||
if(!SkeletonFile)
|
throw DeadlyImportError("Failed to create XML reader for skeleton file " + filename);
|
||||||
throw DeadlyImportError(string("Failed to create XML Reader for ")+skeletonFile);
|
|
||||||
|
|
||||||
NextNode(SkeletonFile);
|
DefaultLogger::get()->debug("Reading skeleton '" + filename + "'");
|
||||||
if(string("skeleton")!=SkeletonFile->getNodeName())
|
|
||||||
throw DeadlyImportError("No <skeleton> node in SkeletonFile: "+skeletonFile);
|
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
//------------------------------------load bones-----------------------------------------
|
NextNode(reader);
|
||||||
NextNode(SkeletonFile);
|
while(CurrentNodeNameEquals(reader, "bone"))
|
||||||
if(string("bones")!=SkeletonFile->getNodeName())
|
|
||||||
throw DeadlyImportError("No bones node in skeleton "+skeletonFile);
|
|
||||||
|
|
||||||
NextNode(SkeletonFile);
|
|
||||||
|
|
||||||
while(string("bone")==SkeletonFile->getNodeName())
|
|
||||||
{
|
{
|
||||||
//TODO: Maybe we can have bone ids for the errrors, but normaly, they should never appear, so what....
|
//TODO: Maybe we can have bone ids for the errrors, but normaly, they should never appear, so what....
|
||||||
|
/// @todo What does the above mean?
|
||||||
|
|
||||||
//read a new bone:
|
Bone bone;
|
||||||
Bone NewBone;
|
bone.Id = GetAttribute<int>(reader, "id");
|
||||||
NewBone.Id=GetAttribute<int>(SkeletonFile, "id");
|
bone.Name = GetAttribute<string>(reader, "name");
|
||||||
NewBone.Name=GetAttribute<string>(SkeletonFile, "name");
|
|
||||||
|
|
||||||
//load the position:
|
NextNode(reader);
|
||||||
NextNode(SkeletonFile);
|
if (!CurrentNodeNameEquals(reader, "position"))
|
||||||
if(string("position")!=SkeletonFile->getNodeName())
|
|
||||||
throw DeadlyImportError("Position is not first node in Bone!");
|
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:
|
bone.Position.x = GetAttribute<float>(reader, "x");
|
||||||
NextNode(SkeletonFile);
|
bone.Position.y = GetAttribute<float>(reader, "y");
|
||||||
if(string("rotation")!=SkeletonFile->getNodeName())
|
bone.Position.z = GetAttribute<float>(reader, "z");
|
||||||
|
|
||||||
|
NextNode(reader);
|
||||||
|
if (!CurrentNodeNameEquals(reader, "rotation"))
|
||||||
throw DeadlyImportError("Rotation is not the second node in Bone!");
|
throw DeadlyImportError("Rotation is not the second node in Bone!");
|
||||||
NewBone.RotationAngle=GetAttribute<float>(SkeletonFile, "angle");
|
|
||||||
NextNode(SkeletonFile);
|
bone.RotationAngle = GetAttribute<float>(reader, "angle");
|
||||||
if(string("axis")!=SkeletonFile->getNodeName())
|
|
||||||
|
NextNode(reader);
|
||||||
|
if (!CurrentNodeNameEquals(reader, "axis"))
|
||||||
throw DeadlyImportError("No axis specified for bone rotation!");
|
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
|
bone.RotationAxis.x = GetAttribute<float>(reader, "x");
|
||||||
Bones.push_back(NewBone);
|
bone.RotationAxis.y = GetAttribute<float>(reader, "y");
|
||||||
|
bone.RotationAxis.z = GetAttribute<float>(reader, "z");
|
||||||
|
|
||||||
//Proceed to the next bone:
|
Bones.push_back(bone);
|
||||||
NextNode(SkeletonFile);
|
|
||||||
|
NextNode(reader);
|
||||||
}
|
}
|
||||||
//The bones in the file a not neccesarly ordered by there id's so we do it now:
|
|
||||||
|
// Order bones by Id
|
||||||
std::sort(Bones.begin(), Bones.end());
|
std::sort(Bones.begin(), Bones.end());
|
||||||
|
|
||||||
//now the id of each bone should be equal to its position in the vector:
|
// Validate that bone indexes are not skipped.
|
||||||
//so we do a simple check:
|
/** @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"))
|
||||||
{
|
{
|
||||||
bool IdsOk=true;
|
string childName = GetAttribute<string>(reader, "bone");
|
||||||
for(int i=0; i<static_cast<signed int>(Bones.size()); ++i)//i is signed, because all Id's are also signed!
|
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())
|
||||||
{
|
{
|
||||||
if(Bones[i].Id!=i)
|
iterChild->ParentId = iterParent->Id;
|
||||||
IdsOk=false;
|
iterParent->Children.push_back(iterChild->Id);
|
||||||
}
|
}
|
||||||
if(!IdsOk)
|
else
|
||||||
throw DeadlyImportError("Bone Ids are not valid!"+skeletonFile);
|
DefaultLogger::get()->warn("Failed to find bones for parenting: Child " + childName + " Parent " + parentName);
|
||||||
|
|
||||||
|
NextNode(reader);
|
||||||
}
|
}
|
||||||
DefaultLogger::get()->debug((Formatter::format(),"Number of bones: ",Bones.size()));
|
|
||||||
//________________________________________________________________________________
|
|
||||||
|
|
||||||
|
// Calculate bone matrices for root bones. Recursively does their children.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//----------------------------load bonehierarchy--------------------------------
|
|
||||||
if(string("bonehierarchy")!=SkeletonFile->getNodeName())
|
|
||||||
throw DeadlyImportError("no bonehierarchy node in "+skeletonFile);
|
|
||||||
|
|
||||||
DefaultLogger::get()->debug("loading bonehierarchy...");
|
|
||||||
NextNode(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);
|
|
||||||
|
|
||||||
NextNode(SkeletonFile);
|
|
||||||
}
|
|
||||||
//_____________________________________________________________________________
|
|
||||||
|
|
||||||
|
|
||||||
//--------- Calculate the WorldToBoneSpace Matrix recursively for all bones: ------------------
|
|
||||||
BOOST_FOREACH(Bone &theBone, Bones)
|
BOOST_FOREACH(Bone &theBone, Bones)
|
||||||
{
|
{
|
||||||
if(-1==theBone.ParentId) //the bone is a root bone
|
if (!theBone.IsParented())
|
||||||
{
|
|
||||||
theBone.CalculateBoneToWorldSpaceMatrix(Bones);
|
theBone.CalculateBoneToWorldSpaceMatrix(Bones);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
//_______________________________________________________________________
|
|
||||||
|
|
||||||
|
aiVector3D zeroVec(0.f, 0.f, 0.f);
|
||||||
|
|
||||||
//---------------------------load animations-----------------------------
|
// Animations
|
||||||
if(string("animations")==SkeletonFile->getNodeName())//animations are optional values
|
if (CurrentNodeNameEquals(reader, "animations"))
|
||||||
{
|
{
|
||||||
DefaultLogger::get()->debug("Loading Animations");
|
DefaultLogger::get()->debug(" - Animations");
|
||||||
NextNode(SkeletonFile);
|
|
||||||
while(string("animation")==SkeletonFile->getNodeName())
|
NextNode(reader);
|
||||||
|
while(CurrentNodeNameEquals(reader, "animation"))
|
||||||
{
|
{
|
||||||
Animation NewAnimation;
|
Animation animation;
|
||||||
NewAnimation.Name=GetAttribute<string>(SkeletonFile, "name");
|
animation.Name = GetAttribute<string>(reader, "name");
|
||||||
NewAnimation.Length=GetAttribute<float>(SkeletonFile, "length");
|
animation.Length = GetAttribute<float>(reader, "length");
|
||||||
|
|
||||||
//Load all Tracks
|
// Tracks
|
||||||
NextNode(SkeletonFile);
|
NextNode(reader);
|
||||||
if(string("tracks")!=SkeletonFile->getNodeName())
|
if (!CurrentNodeNameEquals(reader, "tracks"))
|
||||||
throw DeadlyImportError("no tracks node in animation");
|
throw DeadlyImportError("No <tracks> node found in animation '" + animation.Name + "' in " + skeletonFile);
|
||||||
NextNode(SkeletonFile);
|
|
||||||
while(string("track")==SkeletonFile->getNodeName())
|
NextNode(reader);
|
||||||
|
while(CurrentNodeNameEquals(reader, "track"))
|
||||||
{
|
{
|
||||||
Track NewTrack;
|
Track track;
|
||||||
NewTrack.BoneName=GetAttribute<string>(SkeletonFile, "bone");
|
track.BoneName = GetAttribute<string>(reader, "bone");
|
||||||
|
|
||||||
//Load all keyframes;
|
// Keyframes
|
||||||
NextNode(SkeletonFile);
|
NextNode(reader);
|
||||||
if(string("keyframes")!=SkeletonFile->getNodeName())
|
if (!CurrentNodeNameEquals(reader, "keyframes"))
|
||||||
throw DeadlyImportError("no keyframes node!");
|
throw DeadlyImportError("No <keyframes> node found in a track in animation '" + animation.Name + "' in " + skeletonFile);
|
||||||
NextNode(SkeletonFile);
|
|
||||||
while(string("keyframe")==SkeletonFile->getNodeName())
|
NextNode(reader);
|
||||||
|
while(CurrentNodeNameEquals(reader, "keyframe"))
|
||||||
{
|
{
|
||||||
KeyFrame NewKeyframe;
|
KeyFrame keyFrame;
|
||||||
NewKeyframe.Time=GetAttribute<float>(SkeletonFile, "time");
|
keyFrame.Time = GetAttribute<float>(reader, "time");
|
||||||
|
|
||||||
//loop over the attributes:
|
NextNode(reader);
|
||||||
|
while(CurrentNodeNameEquals(reader, "translate") || CurrentNodeNameEquals(reader, "rotate") || CurrentNodeNameEquals(reader, "scale"))
|
||||||
while(true) //will quit, if a Node is not a animationkey
|
|
||||||
{
|
{
|
||||||
NextNode(SkeletonFile);
|
if (CurrentNodeNameEquals(reader, "translate"))
|
||||||
|
|
||||||
//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");
|
keyFrame.Position.x = GetAttribute<float>(reader, "x");
|
||||||
NewKeyframe.Position.y=GetAttribute<float>(SkeletonFile, "y");
|
keyFrame.Position.y = GetAttribute<float>(reader, "y");
|
||||||
NewKeyframe.Position.z=GetAttribute<float>(SkeletonFile, "z");
|
keyFrame.Position.z = GetAttribute<float>(reader, "z");
|
||||||
}
|
}
|
||||||
|
else if (CurrentNodeNameEquals(reader, "rotate"))
|
||||||
//Rotation:
|
|
||||||
else if(string("rotate")==SkeletonFile->getNodeName())
|
|
||||||
{
|
{
|
||||||
float RotationAngle=GetAttribute<float>(SkeletonFile, "angle");
|
float angle = GetAttribute<float>(reader, "angle");
|
||||||
aiVector3D RotationAxis;
|
|
||||||
NextNode(SkeletonFile);
|
NextNode(reader);
|
||||||
if(string("axis")!=SkeletonFile->getNodeName())
|
if(string("axis")!=reader->getNodeName())
|
||||||
throw DeadlyImportError("No axis for keyframe rotation!");
|
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");
|
|
||||||
|
|
||||||
if(0==RotationAxis.x && 0==RotationAxis.y && 0==RotationAxis.z)//we have an invalid rotation axis
|
aiVector3D axis;
|
||||||
|
axis.x = GetAttribute<float>(reader, "x");
|
||||||
|
axis.y = GetAttribute<float>(reader, "y");
|
||||||
|
axis.z = GetAttribute<float>(reader, "z");
|
||||||
|
|
||||||
|
if (axis.Equal(zeroVec))
|
||||||
{
|
{
|
||||||
RotationAxis.x=1.0f;
|
axis.x = 1.0f;
|
||||||
if(0!=RotationAngle)//if we don't rotate at all, the axis does not matter
|
if (angle != 0)
|
||||||
{
|
DefaultLogger::get()->warn("Found invalid a key frame with a zero rotation axis in animation '" + animation.Name + "'");
|
||||||
DefaultLogger::get()->warn("Invalid Rotation Axis in KeyFrame!");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
NewKeyframe.Rotation=aiQuaternion(RotationAxis, RotationAngle);
|
keyFrame.Rotation = aiQuaternion(axis, angle);
|
||||||
}
|
}
|
||||||
|
else if (CurrentNodeNameEquals(reader, "scale"))
|
||||||
//Scaling:
|
|
||||||
else if(string("scale")==SkeletonFile->getNodeName())
|
|
||||||
{
|
{
|
||||||
NewKeyframe.Scaling.x=GetAttribute<float>(SkeletonFile, "x");
|
keyFrame.Scaling.x = GetAttribute<float>(reader, "x");
|
||||||
NewKeyframe.Scaling.y=GetAttribute<float>(SkeletonFile, "y");
|
keyFrame.Scaling.y = GetAttribute<float>(reader, "y");
|
||||||
NewKeyframe.Scaling.z=GetAttribute<float>(SkeletonFile, "z");
|
keyFrame.Scaling.z = GetAttribute<float>(reader, "z");
|
||||||
}
|
}
|
||||||
|
NextNode(reader);
|
||||||
//we suppose, that we read all attributes and this is a new keyframe or the end of the animation
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
track.Keyframes.push_back(keyFrame);
|
||||||
NewTrack.Keyframes.push_back(NewKeyframe);
|
|
||||||
}
|
}
|
||||||
|
animation.Tracks.push_back(track);
|
||||||
NewAnimation.Tracks.push_back(NewTrack);
|
|
||||||
}
|
}
|
||||||
|
Animations.push_back(animation);
|
||||||
|
|
||||||
Animations.push_back(NewAnimation);
|
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*/)
|
void OgreImporter::CreateAssimpSkeleton(aiScene *pScene, const std::vector<Bone> &Bones, const std::vector<Animation> &/*Animations*/)
|
||||||
{
|
{
|
||||||
if(!pScene->mRootNode)
|
if(!pScene->mRootNode)
|
||||||
|
@ -414,31 +393,22 @@ aiNode* OgreImporter::CreateAiNodeFromBone(int BoneId, const std::vector<Bone> &
|
||||||
|
|
||||||
void Bone::CalculateBoneToWorldSpaceMatrix(vector<Bone> &Bones)
|
void Bone::CalculateBoneToWorldSpaceMatrix(vector<Bone> &Bones)
|
||||||
{
|
{
|
||||||
//Calculate the matrix for this bone:
|
aiMatrix4x4 t0, t1;
|
||||||
|
aiMatrix4x4 transform = aiMatrix4x4::Rotation(-RotationAngle, RotationAxis, t1) * aiMatrix4x4::Translation(-Position, t0);
|
||||||
|
|
||||||
aiMatrix4x4 t0,t1;
|
if (!IsParented())
|
||||||
aiMatrix4x4 Transf= aiMatrix4x4::Rotation(-RotationAngle, RotationAxis, t1)
|
BoneToWorldSpace = transform;
|
||||||
* aiMatrix4x4::Translation(-Position, t0);
|
|
||||||
|
|
||||||
if(-1==ParentId)
|
|
||||||
{
|
|
||||||
BoneToWorldSpace=Transf;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
BoneToWorldSpace = transform * Bones[ParentId].BoneToWorldSpace;
|
||||||
BoneToWorldSpace=Transf*Bones[ParentId].BoneToWorldSpace;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Recursively for all children now that the parent matrix has been calculated.
|
||||||
//and recursivly for all children:
|
BOOST_FOREACH(int childId, Children)
|
||||||
BOOST_FOREACH(int theChildren, Children)
|
|
||||||
{
|
{
|
||||||
Bones[theChildren].CalculateBoneToWorldSpaceMatrix(Bones);
|
Bones[childId].CalculateBoneToWorldSpaceMatrix(Bones);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // Ogre
|
||||||
|
} // Assimp
|
||||||
|
|
||||||
}//namespace Ogre
|
#endif // ASSIMP_BUILD_NO_OGRE_IMPORTER
|
||||||
}//namespace Assimp
|
|
||||||
|
|
||||||
#endif // !! ASSIMP_BUILD_NO_OGRE_IMPORTER
|
|
||||||
|
|
Loading…
Reference in New Issue