Ogre: Shared BoneWeights get loaded

git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@1200 67173fc5-114c-0410-ac8e-9d2fd5bffc1f
pull/5/head
jonathanklein 2012-03-13 16:29:29 +00:00
parent 761c974fde
commit 973e3fede3
3 changed files with 181 additions and 142 deletions

View File

@ -159,6 +159,7 @@ void OgreImporter::InternReadFile(const std::string &pFile, aiScene *pScene, Ass
{
string SkeletonFile=GetAttribute<string>(MeshFile, "name");
LoadSkeleton(SkeletonFile, Bones, Animations);
XmlRead(MeshFile);
}
else
{
@ -167,6 +168,23 @@ void OgreImporter::InternReadFile(const std::string &pFile, aiScene *pScene, Ass
}
//__________________________________________________________________
//now there might be boneassignments for the shared geometry:
if(MeshFile->getNodeName()==string("boneassignments"))
{
ReadBoneWeights(m_SharedGeometry, MeshFile);
}
//----------------- Process Meshs -----------------------
BOOST_FOREACH(boost::shared_ptr<SubMesh> theSubMesh, SubMeshes)
{
ProcessSubMesh(*theSubMesh, m_SharedGeometry);
}
//_______________________________________________________
//----------------- Now fill the Assimp scene ---------------------------

View File

@ -22,6 +22,8 @@ struct Keyframe;
///A submesh from Ogre
struct SubMesh
{
bool SharedData;
std::string Name;
std::string MaterialName;
std::vector<Face> FaceList;
@ -35,7 +37,7 @@ struct SubMesh
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),
SubMesh(): SharedData(false), HasPositions(false), HasNormals(false), HasTangents(false),
NumUvs(0), MaterialIndex(-1), BonesUsed(0) {}//initialize everything
};
@ -50,29 +52,44 @@ public:
virtual void SetupProperties(const Importer* pImp);
private:
//-------------------------------- OgreMesh.cpp -------------------------------
/// Helper Functions to read parts of the XML File
void ReadSubMesh(SubMesh& theSubMesh, XmlReader* Reader);//the submesh reference is the result value
/// Reads a single Vertexbuffer and writes its data in the Submesh
static void ReadVertexBuffer(SubMesh &theSubMesh, XmlReader *Reader, unsigned int NumVertices);
/// writes the results in Bones and Animations, Filename is not const, because its call-by-value and the function will change it!
/// Reads bone weights are stores them into the given submesh
static void ReadBoneWeights(SubMesh &theSubMesh, XmlReader *Reader);
/// After Loading a SubMehs some work needs to be done (make all Vertexes unique, normalize weights)
static void OgreImporter::ProcessSubMesh(SubMesh &theSubMesh, SubMesh &theSharedGeometry);
/// Uses the bone data to convert a SubMesh into a aiMesh which will be created and returned
aiMesh* CreateAssimpSubMesh(const SubMesh &theSubMesh, const std::vector<Bone>& Bones) const;
//-------------------------------- OgreSkeleton.cpp -------------------------------
/// Writes the results in Bones and Animations, Filename is not const, because its call-by-value and the function will change it!
void LoadSkeleton(std::string FileName, std::vector<Bone> &Bones, std::vector<Animation> &Animations) const;
/// converts the animations in aiAnimations and puts them into the scene
/// Converts the animations in aiAnimations and puts them into the scene
void PutAnimationsInScene(const std::vector<Bone> &Bones, const std::vector<Animation> &Animations);
/// uses the bone data to convert a SubMesh into a aiMesh which will be created and returned
aiMesh* CreateAssimpSubMesh(const SubMesh &theSubMesh, const std::vector<Bone>& Bones) const;
//creates the aiskeleton in current scene
/// Creates the aiskeleton in current scene
void CreateAssimpSkeleton(const std::vector<Bone> &Bones, const std::vector<Animation> &Animations);
/// Recursivly creates a filled aiNode from a given root bone
static aiNode* CreateAiNodeFromBone(int BoneId, const std::vector<Bone> &Bones, aiNode* ParentNode);
//-------------------------------- OgreMaterial.cpp -------------------------------
aiMaterial* LoadMaterial(const std::string MaterialName) const;
static void ReadTechnique(std::stringstream &ss, aiMaterial* NewMaterial);
///Recursivly creates a filled aiNode from a given root bone
static aiNode* CreateAiNodeFromBone(int BoneId, const std::vector<Bone> &Bones, aiNode* ParentNode);
//Now we don't have to give theses parameters to all functions
std::string m_CurrentFilename;
@ -126,6 +143,7 @@ struct Bone
bool operator==(const aiString& rval) const
{return Name==std::string(rval.data); }
// implemented in OgreSkeleton.cpp
void CalculateBoneToWorldSpaceMatrix(std::vector<Bone>& Bones);
};

View File

@ -55,10 +55,8 @@ 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");
theSubMesh.SharedData=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
@ -119,28 +117,10 @@ void OgreImporter::ReadSubMesh(SubMesh &theSubMesh, XmlReader *Reader)
}//end of "geometry
else if(string(Reader->getNodeName())=="boneassignments")
else if(Reader->getNodeName()==string("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");
//calculate the number of bones used (this is the highest id +1 becuase bone ids start at 0)
theSubMesh.BonesUsed=max(theSubMesh.BonesUsed, NewWeight.BoneId+1);
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
ReadBoneWeights(theSubMesh, Reader);
}
}
DefaultLogger::get()->debug((Formatter::format(),
"Positionen: ",theSubMesh.Positions.size(),
@ -149,115 +129,6 @@ void OgreImporter::ReadSubMesh(SubMesh &theSubMesh, XmlReader *Reader)
" 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(bSharedData)//copy vertexinformations to our mesh:
{
theSubMesh.HasPositions=m_SharedGeometry.HasPositions;
theSubMesh.HasNormals=m_SharedGeometry.HasNormals;
theSubMesh.HasTangents=m_SharedGeometry.HasTangents;
theSubMesh.NumUvs=m_SharedGeometry.NumUvs;
}
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;
}
}
}
//_________________________________________________________
}
@ -382,6 +253,138 @@ void OgreImporter::ReadVertexBuffer(SubMesh &theSubMesh, XmlReader *Reader, unsi
}
void OgreImporter::ReadBoneWeights(SubMesh &theSubMesh, XmlReader *Reader)
{
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");
//calculate the number of bones used (this is the highest id +1 becuase bone ids start at 0)
theSubMesh.BonesUsed=max(theSubMesh.BonesUsed, NewWeight.BoneId+1);
theSubMesh.Weights[VertexId].push_back(NewWeight);
}
}
void OgreImporter::ProcessSubMesh(SubMesh &theSubMesh, SubMesh &theSharedGeometry)
{
//---------------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(UniqueVertexCount);
//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= theSubMesh.SharedData ? theSharedGeometry : theSubMesh;
if(theSubMesh.SharedData)//copy vertexinformations to our mesh:
{
theSubMesh.HasPositions=theSharedGeometry.HasPositions;
theSubMesh.HasNormals=theSharedGeometry.HasNormals;
theSubMesh.HasTangents=theSharedGeometry.HasTangents;
theSubMesh.NumUvs=theSharedGeometry.NumUvs;
theSubMesh.BonesUsed=theSharedGeometry.BonesUsed;
}
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() > 0)
{
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;
}
}
}
//_________________________________________________________
}
aiMesh* OgreImporter::CreateAssimpSubMesh(const SubMesh& theSubMesh, const vector<Bone>& Bones) const
{