2014-05-18 08:57:44 +00:00
/*
Open Asset Import Library ( assimp )
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2020-01-20 13:53:12 +00:00
Copyright ( c ) 2006 - 2020 , assimp team
2018-01-28 18:42:05 +00:00
2014-05-18 08:57:44 +00:00
All rights reserved .
2015-05-19 03:52:10 +00:00
Redistribution and use of this software in source and binary forms ,
with or without modification , are permitted provided that the
2014-05-18 08:57:44 +00:00
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 .
2015-05-19 03:52:10 +00:00
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
" AS IS " AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT
2014-05-18 08:57:44 +00:00
LIMITED TO , THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
2015-05-19 03:52:10 +00:00
A PARTICULAR PURPOSE ARE DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT
2014-05-18 08:57:44 +00:00
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL ,
2015-05-19 03:52:10 +00:00
SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT
2014-05-18 08:57:44 +00:00
LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE ,
2015-05-19 03:52:10 +00:00
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
2014-05-18 08:57:44 +00:00
OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
2014-05-20 01:52:53 +00:00
# include "OgreXmlSerializer.h"
2014-05-21 01:00:11 +00:00
# include "OgreBinarySerializer.h"
# include "OgreParsingUtils.h"
2020-07-16 09:33:28 +00:00
2018-01-06 00:18:33 +00:00
# include <assimp/TinyFormatter.h>
2016-06-06 20:04:29 +00:00
# include <assimp/DefaultLogger.hpp>
2020-07-16 09:33:28 +00:00
2016-04-05 21:23:53 +00:00
# include <memory>
2014-05-20 01:52:53 +00:00
# ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER
// Define as 1 to get verbose logging.
# define OGRE_XML_SERIALIZER_DEBUG 0
2020-03-01 12:15:45 +00:00
namespace Assimp {
namespace Ogre {
2020-08-27 15:05:09 +00:00
//AI_WONT_RETURN void ThrowAttibuteError(const XmlParser *reader, const std::string &name, const std::string &error = "") AI_WONT_RETURN_SUFFIX;
2020-07-10 20:25:38 +00:00
AI_WONT_RETURN void ThrowAttibuteError ( const std : : string & nodeName , const std : : string & name , const std : : string & error ) {
2020-03-01 12:15:45 +00:00
if ( ! error . empty ( ) ) {
2020-07-10 20:25:38 +00:00
throw DeadlyImportError ( error + " in node ' " + nodeName + " ' and attribute ' " + name + " ' " ) ;
2020-03-01 12:15:45 +00:00
} else {
2020-07-10 20:25:38 +00:00
throw DeadlyImportError ( " Attribute ' " + name + " ' does not exist in node ' " + nodeName + " ' " ) ;
2015-05-19 03:57:13 +00:00
}
2014-05-20 01:52:53 +00:00
}
2020-03-01 12:15:45 +00:00
template < >
2020-07-10 20:25:38 +00:00
int32_t OgreXmlSerializer : : ReadAttribute < int32_t > ( XmlNode & xmlNode , const char * name ) const {
2020-08-18 22:19:56 +00:00
if ( ! XmlParser : : hasAttribute ( xmlNode , name ) ) {
2020-08-27 15:05:09 +00:00
ThrowAttibuteError ( xmlNode . name ( ) , name , " Not found " ) ;
2015-05-19 03:57:13 +00:00
}
2020-07-10 20:25:38 +00:00
pugi : : xml_attribute attr = xmlNode . attribute ( name ) ;
return static_cast < int32_t > ( attr . as_int ( ) ) ;
2014-05-20 01:52:53 +00:00
}
2020-03-01 12:15:45 +00:00
template < >
2020-07-10 20:25:38 +00:00
uint32_t OgreXmlSerializer : : ReadAttribute < uint32_t > ( XmlNode & xmlNode , const char * name ) const {
2020-08-18 22:19:56 +00:00
if ( ! XmlParser : : hasAttribute ( xmlNode , name ) ) {
2020-08-27 15:05:09 +00:00
ThrowAttibuteError ( xmlNode . name ( ) , name , " Not found " ) ;
2015-05-19 03:57:13 +00:00
}
2020-07-10 20:25:38 +00:00
2020-03-01 12:15:45 +00:00
// @note This is hackish. But we are never expecting unsigned values that go outside the
// int32_t range. Just monitor for negative numbers and kill the import.
2020-07-10 20:25:38 +00:00
int32_t temp = ReadAttribute < int32_t > ( xmlNode , name ) ;
2020-03-01 12:15:45 +00:00
if ( temp < 0 ) {
2020-08-27 15:05:09 +00:00
ThrowAttibuteError ( xmlNode . name ( ) , name , " Found a negative number value where expecting a uint32_t value " ) ;
2020-03-01 12:15:45 +00:00
}
return static_cast < uint32_t > ( temp ) ;
2014-05-20 01:52:53 +00:00
}
2020-03-01 12:15:45 +00:00
template < >
2020-07-10 20:25:38 +00:00
uint16_t OgreXmlSerializer : : ReadAttribute < uint16_t > ( XmlNode & xmlNode , const char * name ) const {
2020-08-18 22:19:56 +00:00
if ( ! XmlParser : : hasAttribute ( xmlNode , name ) ) {
2020-08-27 15:05:09 +00:00
ThrowAttibuteError ( xmlNode . name ( ) , name , " Not found " ) ;
2015-05-19 03:57:13 +00:00
}
2020-03-01 12:15:45 +00:00
2020-07-10 20:25:38 +00:00
return static_cast < uint16_t > ( xmlNode . attribute ( name ) . as_int ( ) ) ;
2014-05-20 01:52:53 +00:00
}
2020-03-01 12:15:45 +00:00
template < >
2020-07-10 20:25:38 +00:00
float OgreXmlSerializer : : ReadAttribute < float > ( XmlNode & xmlNode , const char * name ) const {
2020-08-18 22:19:56 +00:00
if ( ! XmlParser : : hasAttribute ( xmlNode , name ) ) {
2020-08-27 15:05:09 +00:00
ThrowAttibuteError ( xmlNode . name ( ) , name , " Not found " ) ;
2015-05-19 03:57:13 +00:00
}
2020-07-10 20:25:38 +00:00
return xmlNode . attribute ( name ) . as_float ( ) ;
2014-05-20 01:52:53 +00:00
}
2020-03-01 12:15:45 +00:00
template < >
2020-07-10 20:25:38 +00:00
std : : string OgreXmlSerializer : : ReadAttribute < std : : string > ( XmlNode & xmlNode , const char * name ) const {
2020-08-18 22:19:56 +00:00
if ( ! XmlParser : : hasAttribute ( xmlNode , name ) ) {
2020-08-27 15:05:09 +00:00
ThrowAttibuteError ( xmlNode . name ( ) , name , " Not found " ) ;
2015-05-19 03:57:13 +00:00
}
2020-03-01 12:15:45 +00:00
2020-07-10 20:25:38 +00:00
return xmlNode . attribute ( name ) . as_string ( ) ;
2014-05-20 01:52:53 +00:00
}
2020-03-01 12:15:45 +00:00
template < >
2020-07-10 20:25:38 +00:00
bool OgreXmlSerializer : : ReadAttribute < bool > ( XmlNode & xmlNode , const char * name ) const {
std : : string value = Ogre : : ToLower ( ReadAttribute < std : : string > ( xmlNode , name ) ) ;
2020-03-01 12:15:45 +00:00
if ( ASSIMP_stricmp ( value , " true " ) = = 0 ) {
2015-05-19 03:57:13 +00:00
return true ;
2020-03-01 12:15:45 +00:00
} else if ( ASSIMP_stricmp ( value , " false " ) = = 0 ) {
2015-05-19 03:57:13 +00:00
return false ;
2020-07-10 20:25:38 +00:00
}
2014-05-20 01:52:53 +00:00
2020-08-27 15:05:09 +00:00
ThrowAttibuteError ( xmlNode . name ( ) , name , " Boolean value is expected to be 'true' or 'false', encountered ' " + value + " ' " ) ;
2020-07-10 20:25:38 +00:00
return false ;
2014-05-20 01:52:53 +00:00
}
2014-05-20 21:09:30 +00:00
// Mesh XML constants
2014-05-20 01:52:53 +00:00
// <mesh>
2020-03-01 12:15:45 +00:00
static const char * nnMesh = " mesh " ;
static const char * nnSharedGeometry = " sharedgeometry " ;
static const char * nnSubMeshes = " submeshes " ;
static const char * nnSubMesh = " submesh " ;
static const char * nnSubMeshNames = " submeshnames " ;
static const char * nnSkeletonLink = " skeletonlink " ;
static const char * nnLOD = " levelofdetail " ;
static const char * nnExtremes = " extremes " ;
static const char * nnPoses = " poses " ;
static const char * nnAnimations = " animations " ;
2014-05-20 01:52:53 +00:00
// <submesh>
2020-03-01 12:15:45 +00:00
static const char * nnFaces = " faces " ;
static const char * nnFace = " face " ;
static const char * nnGeometry = " geometry " ;
static const char * nnTextures = " textures " ;
2014-05-20 01:52:53 +00:00
// <mesh/submesh>
2020-03-01 12:15:45 +00:00
static const char * nnBoneAssignments = " boneassignments " ;
2014-05-20 01:52:53 +00:00
// <sharedgeometry/geometry>
2020-03-01 12:15:45 +00:00
static const char * nnVertexBuffer = " vertexbuffer " ;
2014-05-20 01:52:53 +00:00
// <vertexbuffer>
2020-03-01 12:15:45 +00:00
static const char * nnVertex = " vertex " ;
static const char * nnPosition = " position " ;
static const char * nnNormal = " normal " ;
static const char * nnTangent = " tangent " ;
static const char * nnBinormal = " binormal " ;
static const char * nnTexCoord = " texcoord " ;
static const char * nnColorDiffuse = " colour_diffuse " ;
static const char * nnColorSpecular = " colour_specular " ;
2014-05-20 01:52:53 +00:00
// <boneassignments>
2018-01-31 08:57:34 +00:00
static const char * nnVertexBoneAssignment = " vertexboneassignment " ;
2014-05-20 01:52:53 +00:00
2014-05-20 21:09:30 +00:00
// Skeleton XML constants
2014-05-20 01:52:53 +00:00
// <skeleton>
2020-03-01 12:15:45 +00:00
static const char * nnSkeleton = " skeleton " ;
static const char * nnBones = " bones " ;
static const char * nnBoneHierarchy = " bonehierarchy " ;
static const char * nnAnimationLinks = " animationlinks " ;
2014-05-20 01:52:53 +00:00
// <bones>
2020-03-01 12:15:45 +00:00
static const char * nnBone = " bone " ;
static const char * nnRotation = " rotation " ;
static const char * nnAxis = " axis " ;
static const char * nnScale = " scale " ;
2014-05-20 01:52:53 +00:00
// <bonehierarchy>
2020-03-01 12:15:45 +00:00
static const char * nnBoneParent = " boneparent " ;
2014-05-20 01:52:53 +00:00
// <animations>
2020-03-01 12:15:45 +00:00
static const char * nnAnimation = " animation " ;
static const char * nnTracks = " tracks " ;
2014-05-20 01:52:53 +00:00
// <tracks>
2020-03-01 12:15:45 +00:00
static const char * nnTrack = " track " ;
static const char * nnKeyFrames = " keyframes " ;
static const char * nnKeyFrame = " keyframe " ;
static const char * nnTranslate = " translate " ;
static const char * nnRotate = " rotate " ;
2014-05-20 21:09:30 +00:00
// Common XML constants
2018-01-31 08:57:34 +00:00
static const char * anX = " x " ;
static const char * anY = " y " ;
static const char * anZ = " z " ;
2014-05-20 01:52:53 +00:00
2014-05-20 21:09:30 +00:00
// Mesh
2014-05-20 01:52:53 +00:00
2020-07-16 09:33:28 +00:00
OgreXmlSerializer : : OgreXmlSerializer ( XmlParser * parser ) :
mParser ( parser ) {
// empty
}
MeshXml * OgreXmlSerializer : : ImportMesh ( XmlParser * parser ) {
if ( nullptr = = parser ) {
return nullptr ;
}
OgreXmlSerializer serializer ( parser ) ;
2014-05-20 01:52:53 +00:00
2015-05-19 03:57:13 +00:00
MeshXml * mesh = new MeshXml ( ) ;
serializer . ReadMesh ( mesh ) ;
2018-03-20 22:38:08 +00:00
2015-05-19 03:57:13 +00:00
return mesh ;
2014-05-20 01:52:53 +00:00
}
2018-03-20 22:38:08 +00:00
void OgreXmlSerializer : : ReadMesh ( MeshXml * mesh ) {
2020-09-01 19:48:50 +00:00
XmlNode root = mParser - > getRootNode ( ) ;
if ( nullptr = = root | | std : : string ( nnMesh ) ! = root . name ( ) ) {
throw DeadlyImportError ( " Root node is < " + std : : string ( root . name ( ) ) + " > expecting <mesh> " ) ;
2015-05-19 03:57:13 +00:00
}
2020-09-01 19:48:50 +00:00
for ( XmlNode currentNode : root . children ( ) ) {
2020-07-10 20:25:38 +00:00
const std : : string currentName = currentNode . name ( ) ;
if ( currentName = = nnSharedGeometry ) {
mesh - > sharedVertexData = new VertexDataXml ( ) ;
ReadGeometry ( currentNode , mesh - > sharedVertexData ) ;
} else if ( currentName = = nnSubMesh ) {
ReadSubMesh ( currentNode , mesh ) ;
} else if ( currentName = = nnBoneAssignments ) {
ReadBoneAssignments ( currentNode , mesh - > sharedVertexData ) ;
} else if ( currentName = = nnSkeletonLink ) {
}
}
2020-05-18 10:45:00 +00:00
ASSIMP_LOG_VERBOSE_DEBUG ( " Reading Mesh " ) ;
2014-05-20 01:52:53 +00:00
}
2020-07-10 20:25:38 +00:00
void OgreXmlSerializer : : ReadGeometry ( XmlNode & node , VertexDataXml * dest ) {
dest - > count = ReadAttribute < uint32_t > ( node , " vertexcount " ) ;
2020-05-18 10:45:00 +00:00
ASSIMP_LOG_VERBOSE_DEBUG_F ( " - Reading geometry of " , dest - > count , " vertices " ) ;
2014-05-20 01:52:53 +00:00
2020-07-10 20:25:38 +00:00
for ( XmlNode currentNode : node . children ( ) ) {
const std : : string & currentName = currentNode . name ( ) ;
if ( currentName = = nnVertexBuffer ) {
ReadGeometryVertexBuffer ( currentNode , dest ) ;
}
2015-05-19 03:57:13 +00:00
}
2014-05-20 01:52:53 +00:00
}
2020-07-10 20:25:38 +00:00
void OgreXmlSerializer : : ReadGeometryVertexBuffer ( XmlNode & node , VertexDataXml * dest ) {
2020-08-18 22:19:56 +00:00
bool positions = ( XmlParser : : hasAttribute ( node , " positions " ) & & ReadAttribute < bool > ( node , " positions " ) ) ;
bool normals = ( XmlParser : : hasAttribute ( node , " normals " ) & & ReadAttribute < bool > ( node , " normals " ) ) ;
bool tangents = ( XmlParser : : hasAttribute ( node , " tangents " ) & & ReadAttribute < bool > ( node , " tangents " ) ) ;
uint32_t uvs = ( XmlParser : : hasAttribute ( node , " texture_coords " ) ? ReadAttribute < uint32_t > ( node , " texture_coords " ) : 0 ) ;
2015-05-19 03:57:13 +00:00
// Not having positions is a error only if a previous vertex buffer did not have them.
if ( ! positions & & ! dest - > HasPositions ( ) ) {
throw DeadlyImportError ( " Vertex buffer does not contain positions! " ) ;
}
2020-03-01 12:15:45 +00:00
if ( positions ) {
2020-05-18 10:45:00 +00:00
ASSIMP_LOG_VERBOSE_DEBUG ( " - Contains positions " ) ;
2015-05-19 03:57:13 +00:00
dest - > positions . reserve ( dest - > count ) ;
}
2020-03-01 12:15:45 +00:00
if ( normals ) {
2020-05-18 10:45:00 +00:00
ASSIMP_LOG_VERBOSE_DEBUG ( " - Contains normals " ) ;
2015-05-19 03:57:13 +00:00
dest - > normals . reserve ( dest - > count ) ;
}
2020-03-01 12:15:45 +00:00
if ( tangents ) {
2020-05-18 10:45:00 +00:00
ASSIMP_LOG_VERBOSE_DEBUG ( " - Contains tangents " ) ;
2015-05-19 03:57:13 +00:00
dest - > tangents . reserve ( dest - > count ) ;
}
2020-03-01 12:15:45 +00:00
if ( uvs > 0 ) {
2020-05-18 10:45:00 +00:00
ASSIMP_LOG_VERBOSE_DEBUG_F ( " - Contains " , uvs , " texture coords " ) ;
2015-05-19 03:57:13 +00:00
dest - > uvs . resize ( uvs ) ;
2020-03-01 12:15:45 +00:00
for ( size_t i = 0 , len = dest - > uvs . size ( ) ; i < len ; + + i ) {
2015-05-19 03:57:13 +00:00
dest - > uvs [ i ] . reserve ( dest - > count ) ;
}
}
2020-07-10 20:25:38 +00:00
for ( XmlNode currentNode : node . children ( ) ) {
const std : : string & currentName = currentNode . name ( ) ;
if ( positions & & currentName = = nnPosition ) {
aiVector3D pos ;
pos . x = ReadAttribute < float > ( currentNode , anX ) ;
pos . y = ReadAttribute < float > ( currentNode , anY ) ;
pos . z = ReadAttribute < float > ( currentNode , anZ ) ;
dest - > positions . push_back ( pos ) ;
} else if ( normals & & currentName = = nnNormal ) {
aiVector3D normal ;
normal . x = ReadAttribute < float > ( currentNode , anX ) ;
normal . y = ReadAttribute < float > ( currentNode , anY ) ;
normal . z = ReadAttribute < float > ( currentNode , anZ ) ;
dest - > normals . push_back ( normal ) ;
} else if ( tangents & & currentName = = nnTangent ) {
aiVector3D tangent ;
tangent . x = ReadAttribute < float > ( currentNode , anX ) ;
tangent . y = ReadAttribute < float > ( currentNode , anY ) ;
tangent . z = ReadAttribute < float > ( currentNode , anZ ) ;
dest - > tangents . push_back ( tangent ) ;
} else if ( uvs > 0 & & currentName = = nnTexCoord ) {
for ( auto & curUvs : dest - > uvs ) {
aiVector3D uv ;
uv . x = ReadAttribute < float > ( currentNode , " u " ) ;
uv . y = ( ReadAttribute < float > ( currentNode , " v " ) * - 1 ) + 1 ; // Flip UV from Ogre to Assimp form
curUvs . push_back ( uv ) ;
}
}
}
2015-05-19 03:57:13 +00:00
// Sanity checks
if ( dest - > positions . size ( ) ! = dest - > count ) {
2020-03-01 12:15:45 +00:00
throw DeadlyImportError ( Formatter : : format ( ) < < " Read only " < < dest - > positions . size ( ) < < " positions when should have read " < < dest - > count ) ;
2015-05-19 03:57:13 +00:00
}
if ( normals & & dest - > normals . size ( ) ! = dest - > count ) {
throw DeadlyImportError ( Formatter : : format ( ) < < " Read only " < < dest - > normals . size ( ) < < " normals when should have read " < < dest - > count ) ;
}
if ( tangents & & dest - > tangents . size ( ) ! = dest - > count ) {
throw DeadlyImportError ( Formatter : : format ( ) < < " Read only " < < dest - > tangents . size ( ) < < " tangents when should have read " < < dest - > count ) ;
}
2020-03-01 12:15:45 +00:00
for ( unsigned int i = 0 ; i < dest - > uvs . size ( ) ; + + i ) {
2015-05-19 03:57:13 +00:00
if ( dest - > uvs [ i ] . size ( ) ! = dest - > count ) {
throw DeadlyImportError ( Formatter : : format ( ) < < " Read only " < < dest - > uvs [ i ] . size ( )
2020-03-01 12:15:45 +00:00
< < " uvs for uv index " < < i < < " when should have read " < < dest - > count ) ;
2015-05-19 03:57:13 +00:00
}
}
2014-05-20 01:52:53 +00:00
}
2020-07-10 20:25:38 +00:00
void OgreXmlSerializer : : ReadSubMesh ( XmlNode & node , MeshXml * mesh ) {
2020-03-01 12:15:45 +00:00
static const char * anMaterial = " material " ;
2018-01-30 17:47:15 +00:00
static const char * anUseSharedVertices = " usesharedvertices " ;
2020-03-01 12:15:45 +00:00
static const char * anCount = " count " ;
static const char * anV1 = " v1 " ;
static const char * anV2 = " v2 " ;
static const char * anV3 = " v3 " ;
static const char * anV4 = " v4 " ;
2015-05-19 03:57:13 +00:00
2020-03-01 12:15:45 +00:00
SubMeshXml * submesh = new SubMeshXml ( ) ;
2015-05-19 03:57:13 +00:00
2020-08-18 22:19:56 +00:00
if ( XmlParser : : hasAttribute ( node , anMaterial ) ) {
2020-07-10 20:25:38 +00:00
submesh - > materialRef = ReadAttribute < std : : string > ( node , anMaterial ) ;
2015-05-19 03:57:13 +00:00
}
2020-08-18 22:19:56 +00:00
if ( XmlParser : : hasAttribute ( node , anUseSharedVertices ) ) {
2020-07-14 07:00:06 +00:00
submesh - > usesSharedVertexData = ReadAttribute < bool > ( node , anUseSharedVertices ) ;
2015-05-19 03:57:13 +00:00
}
2020-05-18 10:45:00 +00:00
ASSIMP_LOG_VERBOSE_DEBUG_F ( " Reading SubMesh " , mesh - > subMeshes . size ( ) ) ;
ASSIMP_LOG_VERBOSE_DEBUG_F ( " - Material: ' " , submesh - > materialRef , " ' " ) ;
ASSIMP_LOG_VERBOSE_DEBUG_F ( " - Uses shared geometry: " , ( submesh - > usesSharedVertexData ? " true " : " false " ) ) ;
2015-05-19 03:57:13 +00:00
// TODO: maybe we have always just 1 faces and 1 geometry and always in this order. this loop will only work correct, when the order
// of faces and geometry changed, and not if we have more than one of one
/// @todo Fix above comment with better read logic below
bool quadWarned = false ;
2020-07-10 20:25:38 +00:00
for ( XmlNode & currentNode : node . children ( ) ) {
const std : : string & currentName = currentNode . name ( ) ;
if ( currentName = = nnFaces ) {
submesh - > indexData - > faceCount = ReadAttribute < uint32_t > ( currentNode , anCount ) ;
submesh - > indexData - > faces . reserve ( submesh - > indexData - > faceCount ) ;
for ( XmlNode currentChildNode : currentNode . children ( ) ) {
const std : : string & currentChildName = currentNode . name ( ) ;
if ( currentChildName = = nnFace ) {
aiFace face ;
face . mNumIndices = 3 ;
face . mIndices = new unsigned int [ 3 ] ;
face . mIndices [ 0 ] = ReadAttribute < uint32_t > ( currentChildNode , anV1 ) ;
face . mIndices [ 1 ] = ReadAttribute < uint32_t > ( currentChildNode , anV2 ) ;
face . mIndices [ 2 ] = ReadAttribute < uint32_t > ( currentChildNode , anV3 ) ;
/// @todo Support quads if Ogre even supports them in XML (I'm not sure but I doubt it)
2020-08-18 22:19:56 +00:00
if ( ! quadWarned & & XmlParser : : hasAttribute ( currentChildNode , anV4 ) ) {
2020-07-10 20:25:38 +00:00
ASSIMP_LOG_WARN ( " Submesh <face> has quads with <v4>, only triangles are supported at the moment! " ) ;
quadWarned = true ;
}
}
}
if ( submesh - > indexData - > faces . size ( ) = = submesh - > indexData - > faceCount ) {
ASSIMP_LOG_VERBOSE_DEBUG_F ( " - Faces " , submesh - > indexData - > faceCount ) ;
} else {
throw DeadlyImportError ( Formatter : : format ( ) < < " Read only " < < submesh - > indexData - > faces . size ( ) < < " faces when should have read " < < submesh - > indexData - > faceCount ) ;
}
} else if ( currentName = = nnGeometry ) {
if ( submesh - > usesSharedVertexData ) {
throw DeadlyImportError ( " Found <geometry> in <submesh> when use shared geometry is true. Invalid mesh file. " ) ;
}
submesh - > vertexData = new VertexDataXml ( ) ;
ReadGeometry ( currentNode , submesh - > vertexData ) ;
2020-07-16 09:33:28 +00:00
} else if ( currentName = = nnBoneAssignments ) {
2020-07-10 20:25:38 +00:00
ReadBoneAssignments ( currentNode , submesh - > vertexData ) ;
}
}
2016-11-20 01:49:33 +00:00
submesh - > index = static_cast < unsigned int > ( mesh - > subMeshes . size ( ) ) ;
2015-05-19 03:57:13 +00:00
mesh - > subMeshes . push_back ( submesh ) ;
2014-05-20 01:52:53 +00:00
}
2020-07-10 20:25:38 +00:00
void OgreXmlSerializer : : ReadBoneAssignments ( XmlNode & node , VertexDataXml * dest ) {
2015-05-19 03:57:13 +00:00
if ( ! dest ) {
throw DeadlyImportError ( " Cannot read bone assignments, vertex data is null. " ) ;
}
2018-01-30 17:47:15 +00:00
static const char * anVertexIndex = " vertexindex " ;
2020-03-01 12:15:45 +00:00
static const char * anBoneIndex = " boneindex " ;
static const char * anWeight = " weight " ;
2015-05-19 03:57:13 +00:00
std : : set < uint32_t > influencedVertices ;
2020-07-10 20:25:38 +00:00
for ( XmlNode & currentNode : node . children ( ) ) {
const std : : string & currentName = currentNode . name ( ) ;
if ( currentName = = nnVertexBoneAssignment ) {
VertexBoneAssignment ba ;
ba . vertexIndex = ReadAttribute < uint32_t > ( currentNode , anVertexIndex ) ;
ba . boneIndex = ReadAttribute < uint16_t > ( currentNode , anBoneIndex ) ;
ba . weight = ReadAttribute < float > ( currentNode , anWeight ) ;
dest - > boneAssignments . push_back ( ba ) ;
influencedVertices . insert ( ba . vertexIndex ) ;
}
}
2015-05-19 03:57:13 +00:00
/** Normalize bone weights.
2016-12-08 02:31:51 +00:00
Some exporters won ' t care if the sum of all bone weights
2015-05-19 03:57:13 +00:00
for a single vertex equals 1 or not , so validate here . */
const float epsilon = 0.05f ;
2020-03-01 12:15:45 +00:00
for ( const uint32_t vertexIndex : influencedVertices ) {
2015-05-19 03:57:13 +00:00
float sum = 0.0f ;
2020-03-01 12:15:45 +00:00
for ( VertexBoneAssignmentList : : const_iterator baIter = dest - > boneAssignments . begin ( ) , baEnd = dest - > boneAssignments . end ( ) ; baIter ! = baEnd ; + + baIter ) {
2015-05-19 03:57:13 +00:00
if ( baIter - > vertexIndex = = vertexIndex )
sum + = baIter - > weight ;
}
2020-03-01 12:15:45 +00:00
if ( ( sum < ( 1.0f - epsilon ) ) | | ( sum > ( 1.0f + epsilon ) ) ) {
for ( auto & boneAssign : dest - > boneAssignments ) {
2016-05-21 22:44:37 +00:00
if ( boneAssign . vertexIndex = = vertexIndex )
boneAssign . weight / = sum ;
2015-05-19 03:57:13 +00:00
}
}
}
2020-05-18 10:45:00 +00:00
ASSIMP_LOG_VERBOSE_DEBUG_F ( " - " , dest - > boneAssignments . size ( ) , " bone assignments " ) ;
2014-05-20 01:52:53 +00:00
}
2014-05-20 21:09:30 +00:00
// Skeleton
2020-03-01 12:15:45 +00:00
bool OgreXmlSerializer : : ImportSkeleton ( Assimp : : IOSystem * pIOHandler , MeshXml * mesh ) {
2015-05-19 03:57:13 +00:00
if ( ! mesh | | mesh - > skeletonRef . empty ( ) )
return false ;
// Highly unusual to see in read world cases but support
// XML mesh referencing a binary skeleton file.
2020-03-01 12:15:45 +00:00
if ( EndsWith ( mesh - > skeletonRef , " .skeleton " , false ) ) {
2015-05-19 03:57:13 +00:00
if ( OgreBinarySerializer : : ImportSkeleton ( pIOHandler , mesh ) )
return true ;
/** Last fallback if .skeleton failed to be read. Try reading from
. skeleton . xml even if the XML file referenced a binary skeleton .
@ note This logic was in the previous version and I don ' t want to break
old code that might depends on it . */
mesh - > skeletonRef = mesh - > skeletonRef + " .xml " ;
}
2020-07-10 20:25:38 +00:00
XmlParserPtr xmlParser = OpenXmlParser ( pIOHandler , mesh - > skeletonRef ) ;
if ( ! xmlParser . get ( ) )
2015-05-19 03:57:13 +00:00
return false ;
Skeleton * skeleton = new Skeleton ( ) ;
2020-07-10 20:25:38 +00:00
OgreXmlSerializer serializer ( xmlParser . get ( ) ) ;
2020-09-01 19:48:50 +00:00
XmlNode root = xmlParser - > getRootNode ( ) ;
serializer . ReadSkeleton ( root , skeleton ) ;
2015-05-19 03:57:13 +00:00
mesh - > skeleton = skeleton ;
return true ;
2014-05-21 01:00:11 +00:00
}
2020-03-01 12:15:45 +00:00
bool OgreXmlSerializer : : ImportSkeleton ( Assimp : : IOSystem * pIOHandler , Mesh * mesh ) {
2020-07-10 20:25:38 +00:00
if ( ! mesh | | mesh - > skeletonRef . empty ( ) ) {
2015-05-19 03:57:13 +00:00
return false ;
2020-07-10 20:25:38 +00:00
}
2015-05-19 03:57:13 +00:00
2020-07-10 20:25:38 +00:00
XmlParserPtr xmlParser = OpenXmlParser ( pIOHandler , mesh - > skeletonRef ) ;
if ( ! xmlParser . get ( ) ) {
2015-05-19 03:57:13 +00:00
return false ;
2020-07-10 20:25:38 +00:00
}
2015-05-19 03:57:13 +00:00
Skeleton * skeleton = new Skeleton ( ) ;
2020-07-10 20:25:38 +00:00
OgreXmlSerializer serializer ( xmlParser . get ( ) ) ;
2020-09-01 19:48:50 +00:00
XmlNode root = xmlParser - > getRootNode ( ) ;
2020-07-14 07:00:06 +00:00
2020-09-01 19:48:50 +00:00
serializer . ReadSkeleton ( root , skeleton ) ;
2015-05-19 03:57:13 +00:00
mesh - > skeleton = skeleton ;
2020-07-14 07:00:06 +00:00
2015-05-19 03:57:13 +00:00
return true ;
2014-05-21 01:00:11 +00:00
}
2020-07-10 20:25:38 +00:00
XmlParserPtr OgreXmlSerializer : : OpenXmlParser ( Assimp : : IOSystem * pIOHandler , const std : : string & filename ) {
2020-03-01 12:15:45 +00:00
if ( ! EndsWith ( filename , " .skeleton.xml " , false ) ) {
2018-04-26 12:10:18 +00:00
ASSIMP_LOG_ERROR_F ( " Imported Mesh is referencing to unsupported ' " , filename , " ' skeleton file. " ) ;
2020-07-10 20:25:38 +00:00
return XmlParserPtr ( ) ;
2015-05-19 03:57:13 +00:00
}
2020-03-01 12:15:45 +00:00
if ( ! pIOHandler - > Exists ( filename ) ) {
2018-04-26 12:10:18 +00:00
ASSIMP_LOG_ERROR_F ( " Failed to find skeleton file ' " , filename , " ' that is referenced by imported Mesh. " ) ;
2020-07-10 20:25:38 +00:00
return XmlParserPtr ( ) ;
2015-05-19 03:57:13 +00:00
}
2016-04-05 21:23:53 +00:00
std : : unique_ptr < IOStream > file ( pIOHandler - > Open ( filename ) ) ;
2015-05-19 03:57:13 +00:00
if ( ! file . get ( ) ) {
throw DeadlyImportError ( " Failed to open skeleton file " + filename ) ;
}
2020-07-10 20:25:38 +00:00
XmlParserPtr xmlParser = XmlParserPtr ( new XmlParser ) ;
if ( ! xmlParser - > parse ( file . get ( ) ) ) {
2015-05-19 03:57:13 +00:00
throw DeadlyImportError ( " Failed to create XML reader for skeleton file " + filename ) ;
}
2020-07-10 20:25:38 +00:00
return xmlParser ;
2014-05-20 21:09:30 +00:00
}
2020-07-10 20:25:38 +00:00
void OgreXmlSerializer : : ReadSkeleton ( XmlNode & node , Skeleton * skeleton ) {
if ( node . name ( ) ! = nnSkeleton ) {
2020-07-14 07:00:06 +00:00
throw DeadlyImportError ( " Root node is < " + std : : string ( node . name ( ) ) + " > expecting <skeleton> " ) ;
2015-05-19 03:57:13 +00:00
}
2020-05-18 10:45:00 +00:00
ASSIMP_LOG_VERBOSE_DEBUG ( " Reading Skeleton " ) ;
2015-05-19 03:57:13 +00:00
// Optional blend mode from root node
2020-08-18 22:19:56 +00:00
if ( XmlParser : : hasAttribute ( node , " blendmode " ) ) {
2020-07-10 20:25:38 +00:00
skeleton - > blendMode = ( ToLower ( ReadAttribute < std : : string > ( node , " blendmode " ) ) = = " cumulative " ? Skeleton : : ANIMBLEND_CUMULATIVE : Skeleton : : ANIMBLEND_AVERAGE ) ;
2015-05-19 03:57:13 +00:00
}
2020-07-10 20:25:38 +00:00
for ( XmlNode & currentNode : node . children ( ) ) {
const std : : string currentName = currentNode . name ( ) ;
if ( currentName = = nnBones ) {
ReadBones ( currentNode , skeleton ) ;
} else if ( currentName = = nnBoneHierarchy ) {
ReadBoneHierarchy ( currentNode , skeleton ) ;
} else if ( currentName = = nnAnimations ) {
ReadAnimations ( currentNode , skeleton ) ;
}
}
2014-05-20 21:09:30 +00:00
}
2020-07-10 20:25:38 +00:00
void OgreXmlSerializer : : ReadAnimations ( XmlNode & node , Skeleton * skeleton ) {
2015-05-19 03:57:13 +00:00
if ( skeleton - > bones . empty ( ) ) {
throw DeadlyImportError ( " Cannot read <animations> for a Skeleton without bones " ) ;
}
2014-05-20 21:09:30 +00:00
2020-05-18 10:45:00 +00:00
ASSIMP_LOG_VERBOSE_DEBUG ( " - Animations " ) ;
2015-05-19 03:52:10 +00:00
2020-07-10 20:25:38 +00:00
for ( XmlNode & currentNode : node . children ( ) ) {
const std : : string currentName = currentNode . name ( ) ;
if ( currentName = = nnAnimation ) {
Animation * anim = new Animation ( skeleton ) ;
anim - > name = ReadAttribute < std : : string > ( currentNode , " name " ) ;
anim - > length = ReadAttribute < float > ( currentNode , " length " ) ;
for ( XmlNode & currentChildNode : currentNode . children ( ) ) {
const std : : string currentChildName = currentNode . name ( ) ;
if ( currentChildName = = nnTracks ) {
ReadAnimationTracks ( currentChildNode , anim ) ;
skeleton - > animations . push_back ( anim ) ;
} else {
throw DeadlyImportError ( Formatter : : format ( ) < < " No <tracks> found in <animation> " < < anim - > name ) ;
}
}
}
}
2014-05-20 21:09:30 +00:00
}
2020-07-10 20:25:38 +00:00
void OgreXmlSerializer : : ReadAnimationTracks ( XmlNode & node , Animation * dest ) {
for ( XmlNode & currentNode : node . children ( ) ) {
const std : : string currentName = currentNode . name ( ) ;
if ( currentName = = nnTrack ) {
VertexAnimationTrack track ;
track . type = VertexAnimationTrack : : VAT_TRANSFORM ;
track . boneName = ReadAttribute < std : : string > ( currentNode , " bone " ) ;
for ( XmlNode & currentChildNode : currentNode . children ( ) ) {
const std : : string currentChildName = currentNode . name ( ) ;
if ( currentChildName = = nnKeyFrames ) {
ReadAnimationKeyFrames ( currentChildNode , dest , & track ) ;
dest - > tracks . push_back ( track ) ;
} else {
throw DeadlyImportError ( Formatter : : format ( ) < < " No <keyframes> found in <track> " < < dest - > name ) ;
}
}
}
}
2014-05-20 21:09:30 +00:00
}
2020-07-10 20:25:38 +00:00
void OgreXmlSerializer : : ReadAnimationKeyFrames ( XmlNode & node , Animation * anim , VertexAnimationTrack * dest ) {
2018-02-09 13:40:28 +00:00
const aiVector3D zeroVec ( 0.f , 0.f , 0.f ) ;
2020-07-10 20:25:38 +00:00
for ( XmlNode & currentNode : node . children ( ) ) {
2020-07-14 07:00:06 +00:00
TransformKeyFrame keyframe ;
2020-07-10 20:25:38 +00:00
const std : : string currentName = currentNode . name ( ) ;
if ( currentName = = nnKeyFrame ) {
keyframe . timePos = ReadAttribute < float > ( currentNode , " time " ) ;
for ( XmlNode & currentChildNode : currentNode . children ( ) ) {
const std : : string currentChildName = currentNode . name ( ) ;
if ( currentChildName = = nnTranslate ) {
keyframe . position . x = ReadAttribute < float > ( currentChildNode , anX ) ;
keyframe . position . y = ReadAttribute < float > ( currentChildNode , anY ) ;
keyframe . position . z = ReadAttribute < float > ( currentChildNode , anZ ) ;
} else if ( currentChildName = = nnRotate ) {
float angle = ReadAttribute < float > ( currentChildNode , " angle " ) ;
for ( XmlNode & currentChildChildNode : currentNode . children ( ) ) {
const std : : string currentChildChildName = currentNode . name ( ) ;
if ( currentChildChildName = = nnAxis ) {
aiVector3D axis ;
axis . x = ReadAttribute < float > ( currentChildChildNode , anX ) ;
axis . y = ReadAttribute < float > ( currentChildChildNode , anY ) ;
axis . z = ReadAttribute < float > ( currentChildChildNode , anZ ) ;
if ( axis . Equal ( zeroVec ) ) {
axis . x = 1.0f ;
if ( angle ! = 0 ) {
ASSIMP_LOG_WARN_F ( " Found invalid a key frame with a zero rotation axis in animation: " , anim - > name ) ;
}
}
keyframe . rotation = aiQuaternion ( axis , angle ) ;
}
}
} else if ( currentChildName = = nnScale ) {
keyframe . scale . x = ReadAttribute < float > ( currentChildNode , anX ) ;
keyframe . scale . y = ReadAttribute < float > ( currentChildNode , anY ) ;
keyframe . scale . z = ReadAttribute < float > ( currentChildNode , anZ ) ;
2015-05-19 03:57:13 +00:00
2020-07-10 20:25:38 +00:00
}
}
}
dest - > transformKeyFrames . push_back ( keyframe ) ;
}
2014-05-20 21:09:30 +00:00
}
2020-07-10 20:25:38 +00:00
void OgreXmlSerializer : : ReadBoneHierarchy ( XmlNode & node , Skeleton * skeleton ) {
2015-05-19 03:57:13 +00:00
if ( skeleton - > bones . empty ( ) ) {
throw DeadlyImportError ( " Cannot read <bonehierarchy> for a Skeleton without bones " ) ;
}
2020-07-10 20:25:38 +00:00
for ( XmlNode & currentNode : node . children ( ) ) {
const std : : string currentName = currentNode . name ( ) ;
if ( currentName = = nnBoneParent ) {
const std : : string name = ReadAttribute < std : : string > ( currentNode , " bone " ) ;
const std : : string parentName = ReadAttribute < std : : string > ( currentNode , " parent " ) ;
Bone * bone = skeleton - > BoneByName ( name ) ;
Bone * parent = skeleton - > BoneByName ( parentName ) ;
if ( bone & & parent ) {
parent - > AddChild ( bone ) ;
} else {
throw DeadlyImportError ( " Failed to find bones for parenting: Child " + name + " for parent " + parentName ) ;
}
}
}
2015-05-19 03:57:13 +00:00
// Calculate bone matrices for root bones. Recursively calculates their children.
2020-03-01 12:15:45 +00:00
for ( size_t i = 0 , len = skeleton - > bones . size ( ) ; i < len ; + + i ) {
2015-05-19 03:57:13 +00:00
Bone * bone = skeleton - > bones [ i ] ;
if ( ! bone - > IsParented ( ) )
bone - > CalculateWorldMatrixAndDefaultPose ( skeleton ) ;
}
2014-05-20 21:09:30 +00:00
}
2020-03-01 12:15:45 +00:00
static bool BoneCompare ( Bone * a , Bone * b ) {
ai_assert ( nullptr ! = a ) ;
ai_assert ( nullptr ! = b ) ;
2015-05-19 03:57:13 +00:00
return ( a - > id < b - > id ) ;
2014-05-20 21:09:30 +00:00
}
2020-07-10 20:25:38 +00:00
void OgreXmlSerializer : : ReadBones ( XmlNode & node , Skeleton * skeleton ) {
2020-05-18 10:45:00 +00:00
ASSIMP_LOG_VERBOSE_DEBUG ( " - Bones " ) ;
2015-05-19 03:57:13 +00:00
2020-07-10 20:25:38 +00:00
for ( XmlNode & currentNode : node . children ( ) ) {
const std : : string currentName = currentNode . name ( ) ;
if ( currentName = = nnBone ) {
Bone * bone = new Bone ( ) ;
bone - > id = ReadAttribute < uint16_t > ( currentNode , " id " ) ;
bone - > name = ReadAttribute < std : : string > ( currentNode , " name " ) ;
for ( XmlNode & currentChildNode : currentNode . children ( ) ) {
const std : : string currentChildName = currentNode . name ( ) ;
2020-07-14 07:00:06 +00:00
if ( currentChildName = = nnRotation ) {
2020-07-10 20:25:38 +00:00
bone - > position . x = ReadAttribute < float > ( currentChildNode , anX ) ;
bone - > position . y = ReadAttribute < float > ( currentChildNode , anY ) ;
bone - > position . z = ReadAttribute < float > ( currentChildNode , anZ ) ;
} else if ( currentChildName = = nnScale ) {
2020-07-14 07:00:06 +00:00
float angle = ReadAttribute < float > ( currentChildNode , " angle " ) ;
for ( XmlNode currentChildChildNode : currentChildNode . children ( ) ) {
const std : : string & currentChildChildName = currentChildChildNode . name ( ) ;
if ( currentChildChildName = = nnAxis ) {
aiVector3D axis ;
axis . x = ReadAttribute < float > ( currentChildChildNode , anX ) ;
axis . y = ReadAttribute < float > ( currentChildChildNode , anY ) ;
axis . z = ReadAttribute < float > ( currentChildChildNode , anZ ) ;
bone - > rotation = aiQuaternion ( axis , angle ) ;
} else {
throw DeadlyImportError ( Formatter : : format ( ) < < " No axis specified for bone rotation in bone " < < bone - > id ) ;
}
}
2020-07-10 20:25:38 +00:00
} else if ( currentChildName = = nnScale ) {
2020-08-18 22:19:56 +00:00
if ( XmlParser : : hasAttribute ( currentChildNode , " factor " ) ) {
2020-07-14 07:00:06 +00:00
float factor = ReadAttribute < float > ( currentChildNode , " factor " ) ;
bone - > scale . Set ( factor , factor , factor ) ;
} else {
2020-08-18 22:19:56 +00:00
if ( XmlParser : : hasAttribute ( currentChildNode , anX ) )
2020-07-14 07:00:06 +00:00
bone - > scale . x = ReadAttribute < float > ( currentChildNode , anX ) ;
2020-08-18 22:19:56 +00:00
if ( XmlParser : : hasAttribute ( currentChildNode , anY ) )
2020-07-14 07:00:06 +00:00
bone - > scale . y = ReadAttribute < float > ( currentChildNode , anY ) ;
2020-08-18 22:19:56 +00:00
if ( XmlParser : : hasAttribute ( currentChildNode , anZ ) )
2020-07-14 07:00:06 +00:00
bone - > scale . z = ReadAttribute < float > ( currentChildNode , anZ ) ;
}
2020-07-10 20:25:38 +00:00
}
}
2020-07-14 07:00:06 +00:00
skeleton - > bones . push_back ( bone ) ;
2020-07-10 20:25:38 +00:00
}
}
2015-05-19 03:57:13 +00:00
// Order bones by Id
std : : sort ( skeleton - > bones . begin ( ) , skeleton - > bones . end ( ) , BoneCompare ) ;
// Validate that bone indexes are not skipped.
/** @note Left this from original authors code, but not sure if this is strictly necessary
as per the Ogre skeleton spec . It might be more that other ( later ) code in this imported does not break . */
2020-03-01 12:15:45 +00:00
for ( size_t i = 0 , len = skeleton - > bones . size ( ) ; i < len ; + + i ) {
2015-05-19 03:57:13 +00:00
Bone * b = skeleton - > bones [ i ] ;
2020-05-18 10:45:00 +00:00
ASSIMP_LOG_VERBOSE_DEBUG_F ( " " , b - > id , " " , b - > name ) ;
2015-05-19 03:57:13 +00:00
if ( b - > id ! = static_cast < uint16_t > ( i ) ) {
throw DeadlyImportError ( Formatter : : format ( ) < < " Bone ids are not in sequence starting from 0. Missing index " < < i ) ;
}
}
2014-05-20 21:09:30 +00:00
}
2020-03-01 12:15:45 +00:00
} // namespace Ogre
} // namespace Assimp
2014-05-20 01:52:53 +00:00
# endif // ASSIMP_BUILD_NO_OGRE_IMPORTER