2012-03-07 22:42:40 +00:00
/*
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 ;
2012-03-09 11:43:31 +00:00
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 ;
}
2012-03-07 22:42:40 +00:00
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