2010-03-02 16:12:28 +00:00
/*
* OpenClonk , http : //www.openclonk.org
*
2015-02-27 20:24:29 +00:00
* Copyright ( c ) 2010 - 2015 , The OpenClonk Team and contributors
2010-03-02 16:12:28 +00:00
*
2013-12-17 20:01:09 +00:00
* Distributed under the terms of the ISC license ; see accompanying file
* " COPYING " for details .
2010-03-02 16:12:28 +00:00
*
2013-12-17 20:01:09 +00:00
* " Clonk " is a registered trademark of Matthes Bender , used with permission .
* See accompanying file " TRADEMARK " for details .
2010-03-02 16:12:28 +00:00
*
2013-12-17 20:01:09 +00:00
* To redistribute this file separately , substitute the full license texts
* for the above references .
2010-03-02 16:12:28 +00:00
*/
// A loader for the OGRE .mesh binary file format
# include "C4Include.h"
# include "StdMesh.h"
# include "StdMeshLoader.h"
# include "StdMeshLoaderBinaryChunks.h"
# include "StdMeshLoaderDataStream.h"
# include "StdMeshMaterial.h"
# include <cassert>
# include <vector>
# include <boost/foreach.hpp>
# include <boost/ptr_container/ptr_map.hpp>
# include <boost/ptr_container/ptr_vector.hpp>
namespace
{
2012-02-15 23:45:48 +00:00
bool VertexDeclarationIsSane ( const boost : : ptr_vector < Ogre : : Mesh : : ChunkGeometryVertexDeclElement > & decl , const char * filename )
2010-03-02 16:12:28 +00:00
{
bool semanticSeen [ Ogre : : Mesh : : ChunkGeometryVertexDeclElement : : VDES_MAX + 1 ] = { false } ;
BOOST_FOREACH ( Ogre : : Mesh : : ChunkGeometryVertexDeclElement element , decl )
{
switch ( element . semantic )
{
case Ogre : : Mesh : : ChunkGeometryVertexDeclElement : : VDES_Texcoords :
2014-01-26 10:16:22 +00:00
// FIXME: The Ogre format supports denoting multiple texture coordinates, but the rendering code only supports one
// currently only the first set is read, any additional ones are ignored
2012-02-15 23:45:48 +00:00
break ;
2010-03-02 16:12:28 +00:00
case Ogre : : Mesh : : ChunkGeometryVertexDeclElement : : VDES_Position :
case Ogre : : Mesh : : ChunkGeometryVertexDeclElement : : VDES_Normals :
case Ogre : : Mesh : : ChunkGeometryVertexDeclElement : : VDES_Diffuse :
case Ogre : : Mesh : : ChunkGeometryVertexDeclElement : : VDES_Specular :
// Only one set of each of these elements allowed
if ( semanticSeen [ element . semantic ] )
return false ;
2010-03-16 11:39:58 +00:00
break ;
2010-03-21 18:08:48 +00:00
default :
// We ignore unhandled element semantics.
break ;
2010-03-02 16:12:28 +00:00
}
semanticSeen [ element . semantic ] = true ;
}
return true ;
}
2010-03-28 18:58:01 +00:00
2010-03-02 16:12:28 +00:00
template < size_t N >
void ReadNormalizedVertexData ( float ( & dest ) [ N ] , const char * source , Ogre : : Mesh : : ChunkGeometryVertexDeclElement : : Type vdet )
{
BOOST_STATIC_ASSERT ( N > = 4 ) ;
dest [ 0 ] = dest [ 1 ] = dest [ 2 ] = 0 ; dest [ 3 ] = 1 ;
switch ( vdet )
{
// All VDET_Float* fall through.
case Ogre : : Mesh : : ChunkGeometryVertexDeclElement : : VDET_Float4 :
dest [ 3 ] = * reinterpret_cast < const float * > ( source + sizeof ( float ) * 3 ) ;
case Ogre : : Mesh : : ChunkGeometryVertexDeclElement : : VDET_Float3 :
dest [ 2 ] = * reinterpret_cast < const float * > ( source + sizeof ( float ) * 2 ) ;
case Ogre : : Mesh : : ChunkGeometryVertexDeclElement : : VDET_Float2 :
dest [ 1 ] = * reinterpret_cast < const float * > ( source + sizeof ( float ) * 1 ) ;
case Ogre : : Mesh : : ChunkGeometryVertexDeclElement : : VDET_Float1 :
dest [ 0 ] = * reinterpret_cast < const float * > ( source + sizeof ( float ) * 0 ) ;
break ;
case Ogre : : Mesh : : ChunkGeometryVertexDeclElement : : VDET_Color_ABGR :
2010-03-02 21:48:42 +00:00
dest [ 3 ] = source [ 0 ] / 255.0f ;
2010-03-02 16:12:28 +00:00
for ( int i = 0 ; i < 3 ; + + i )
2010-03-02 21:48:42 +00:00
dest [ i ] = source [ 3 - i ] / 255.0f ;
2010-03-02 16:12:28 +00:00
break ;
case Ogre : : Mesh : : ChunkGeometryVertexDeclElement : : VDET_Color_ARGB :
2010-03-02 21:48:42 +00:00
dest [ 3 ] = source [ 0 ] / 255.0f ;
2010-03-02 16:12:28 +00:00
for ( int i = 0 ; i < 3 ; + + i )
2010-03-02 21:48:42 +00:00
dest [ i ] = source [ i + 1 ] / 255.0f ;
2010-03-02 16:12:28 +00:00
break ;
2010-03-21 18:08:48 +00:00
default :
assert ( ! " Unexpected enum value " ) ;
break ;
2010-03-02 16:12:28 +00:00
}
}
2010-03-28 18:58:01 +00:00
2012-02-15 23:45:48 +00:00
std : : vector < StdSubMesh : : Vertex > ReadSubmeshGeometry ( const Ogre : : Mesh : : ChunkGeometry & geo , const char * filename )
2010-03-02 16:12:28 +00:00
{
2012-02-15 23:45:48 +00:00
if ( ! VertexDeclarationIsSane ( geo . vertexDeclaration , filename ) )
2010-03-02 16:12:28 +00:00
throw Ogre : : Mesh : : InvalidVertexDeclaration ( ) ;
2010-03-02 21:48:42 +00:00
// Get maximum size of a vertex according to the declaration
std : : map < int , size_t > max_offset ;
BOOST_FOREACH ( const Ogre : : Mesh : : ChunkGeometryVertexDeclElement & el , geo . vertexDeclaration )
{
size_t elsize = 0 ;
switch ( el . type )
{
case Ogre : : Mesh : : ChunkGeometryVertexDeclElement : : VDET_Float1 : elsize = sizeof ( float ) * 1 ; break ;
case Ogre : : Mesh : : ChunkGeometryVertexDeclElement : : VDET_Float2 : elsize = sizeof ( float ) * 2 ; break ;
case Ogre : : Mesh : : ChunkGeometryVertexDeclElement : : VDET_Float3 : elsize = sizeof ( float ) * 3 ; break ;
case Ogre : : Mesh : : ChunkGeometryVertexDeclElement : : VDET_Float4 : elsize = sizeof ( float ) * 4 ; break ;
case Ogre : : Mesh : : ChunkGeometryVertexDeclElement : : VDET_Color_ABGR :
case Ogre : : Mesh : : ChunkGeometryVertexDeclElement : : VDET_Color_ARGB : elsize = sizeof ( uint8_t ) * 4 ; break ;
2010-03-21 18:08:48 +00:00
default : assert ( ! " Unexpected enum value " ) ; break ;
2010-03-02 21:48:42 +00:00
}
max_offset [ el . source ] = std : : max < size_t > ( max_offset [ el . source ] , el . offset + elsize ) ;
}
2010-03-02 16:12:28 +00:00
// Generate array of vertex buffer cursors
2010-03-02 21:48:42 +00:00
std : : map < int , const char * > cursors ;
2010-03-02 16:12:28 +00:00
BOOST_FOREACH ( const Ogre : : Mesh : : ChunkGeometryVertexBuffer & buf , geo . vertexBuffers )
2010-03-02 21:48:42 +00:00
{
if ( cursors . find ( buf . index ) ! = cursors . end ( ) )
throw Ogre : : MultipleSingletonChunks ( " Multiple vertex buffers were bound to the same stream " ) ;
cursors [ buf . index ] = static_cast < const char * > ( buf . data - > data ) ;
// Check that the vertices don't overlap
if ( buf . vertexSize < max_offset [ buf . index ] )
throw Ogre : : InsufficientData ( " Vertices overlapping " ) ;
// Check that the vertex buffer has enough room for all vertices
if ( buf . GetSize ( ) < ( geo . vertexCount - 1 ) * buf . vertexSize + max_offset [ buf . index ] )
throw Ogre : : InsufficientData ( " Vertex buffer too small " ) ;
max_offset . erase ( buf . index ) ;
}
if ( ! max_offset . empty ( ) )
throw Ogre : : InsufficientData ( " A vertex element references an unbound stream " ) ;
2010-03-02 16:12:28 +00:00
// Generate vertices
std : : vector < StdSubMesh : : Vertex > vertices ;
vertices . reserve ( geo . vertexCount ) ;
for ( size_t i = 0 ; i < geo . vertexCount ; + + i )
{
StdSubMesh : : Vertex vertex ;
vertex . nx = vertex . ny = vertex . nz = 0 ;
vertex . x = vertex . y = vertex . z = 0 ;
vertex . u = vertex . v = 0 ;
2012-02-15 23:45:48 +00:00
bool read_tex = false ;
2010-03-02 16:12:28 +00:00
// Read vertex declaration
BOOST_FOREACH ( Ogre : : Mesh : : ChunkGeometryVertexDeclElement element , geo . vertexDeclaration )
{
float values [ 4 ] ;
2010-03-03 14:54:45 +00:00
ReadNormalizedVertexData ( values , cursors [ element . source ] + element . offset , element . type ) ;
2010-03-02 16:12:28 +00:00
switch ( element . semantic )
{
case Ogre : : Mesh : : ChunkGeometryVertexDeclElement : : VDES_Position :
vertex . x = values [ 0 ] ;
vertex . y = values [ 1 ] ;
vertex . z = values [ 2 ] ;
break ;
case Ogre : : Mesh : : ChunkGeometryVertexDeclElement : : VDES_Normals :
vertex . nx = values [ 0 ] ;
vertex . ny = values [ 1 ] ;
vertex . nz = values [ 2 ] ;
break ;
case Ogre : : Mesh : : ChunkGeometryVertexDeclElement : : VDES_Texcoords :
2012-02-15 23:45:48 +00:00
if ( ! read_tex ) {
vertex . u = values [ 0 ] ;
vertex . v = values [ 1 ] ;
read_tex = true ;
}
2010-03-02 16:12:28 +00:00
break ;
2010-03-21 18:08:48 +00:00
default :
// We ignore unhandled element semantics.
break ;
2010-03-02 16:12:28 +00:00
}
}
2015-08-05 03:16:36 +00:00
vertices . push_back ( OgreToClonk : : TransformVertex ( vertex ) ) ;
2010-03-02 16:12:28 +00:00
// Advance vertex buffer cursors
2010-03-02 21:48:42 +00:00
BOOST_FOREACH ( const Ogre : : Mesh : : ChunkGeometryVertexBuffer & buf , geo . vertexBuffers )
2010-03-28 18:58:01 +00:00
cursors [ buf . index ] + = buf . vertexSize ;
2010-03-02 16:12:28 +00:00
}
return vertices ;
}
}
2014-12-26 12:46:14 +00:00
void StdMeshSkeletonLoader : : StoreSkeleton ( const char * groupname , const char * filename , std : : shared_ptr < StdMeshSkeleton > skeleton )
2010-03-02 16:12:28 +00:00
{
2014-12-26 12:46:14 +00:00
assert ( groupname ! = NULL ) ;
assert ( filename ! = NULL ) ;
2014-12-16 04:42:34 +00:00
assert ( skeleton ! = NULL ) ;
2010-08-04 20:30:15 +00:00
2014-12-16 04:42:34 +00:00
// Create mirrored animations (#401)
// this is still going to be somewhere else, but for now it will keep moving around
skeleton - > PostInit ( ) ;
2014-12-26 12:46:14 +00:00
2014-12-16 04:42:34 +00:00
2014-12-26 12:46:14 +00:00
// save in map
StdCopyStrBuf filepath ;
MakeFullSkeletonPath ( filepath , groupname , filename ) ;
2015-01-06 11:45:23 +00:00
AddSkeleton ( filepath , skeleton ) ;
2014-12-26 12:46:14 +00:00
// memorize which skeletons can be appended
// skins get broken down to their original definition, which is a little messy at the moment.
StdCopyStrBuf buf_filename ( GetFilenameOnly ( filename ) ) ;
StdCopyStrBuf command_with_definition ( GetFilenameOnly ( buf_filename . getData ( ) ) ) ; // include.Clonk becomes include, include.Clonk.Farmer becomes include.Clonk
StdCopyStrBuf command ( GetFilenameOnly ( command_with_definition . getData ( ) ) ) ; // include stays include, include.Clonk becomes include
StdCopyStrBuf definition ( GetExtension ( buf_filename . getData ( ) ) ) ; // include.Clonk becomes Clonk, include.Clonk.Farmer becomes Farmer
if ( ! ( command_with_definition = = command ) ) // include.Clonk != include?
{
definition = StdCopyStrBuf ( GetExtension ( command_with_definition . getData ( ) ) ) ; // change definition to the part behind the .: Clonk
}
const char * appendto = " appendto " ; // has to be a constant
const char * include = " include " ; // dito
// check where to store
if ( command = = appendto )
{
2015-01-06 11:45:23 +00:00
AppendtoSkeletons . insert ( std : : make_pair ( filepath , definition ) ) ;
2014-12-26 12:46:14 +00:00
}
else if ( command = = include )
{
2015-01-06 11:45:23 +00:00
IncludeSkeletons . insert ( std : : make_pair ( filepath , definition ) ) ;
}
}
void StdMeshSkeletonLoader : : RemoveSkeletonsInGroup ( const char * groupname )
{
// DebugLogF("Removing skeletons in group: %s", groupname);
std : : vector < StdCopyStrBuf > delete_skeletons ;
std : : map < StdCopyStrBuf , std : : shared_ptr < StdMeshSkeleton > > : : iterator it ;
for ( it = Skeletons . begin ( ) ; it ! = Skeletons . end ( ) ; it + + )
{
StdCopyStrBuf skeletonpath ( it - > first . getData ( ) ) ;
StdCopyStrBuf group ( groupname ) ;
group . ToLowerCase ( ) ;
StdCopyStrBuf skeletongroup ;
GetParentPath ( skeletonpath . getData ( ) , & skeletongroup ) ;
if ( skeletongroup = = group )
{
// DebugLogF("Found skeleton in group: %s", it->first.getData());
delete_skeletons . push_back ( skeletonpath ) ;
}
}
for ( unsigned i = 0 ; i < delete_skeletons . size ( ) ; i + + )
{
RemoveSkeleton ( delete_skeletons [ i ] ) ;
}
}
void StdMeshSkeletonLoader : : RemoveSkeleton ( const char * groupname , const char * filename )
{
StdCopyStrBuf filepath ;
MakeFullSkeletonPath ( filepath , groupname , filename ) ;
RemoveSkeleton ( filepath ) ;
}
void StdMeshSkeletonLoader : : RemoveSkeleton ( const StdCopyStrBuf & filepath )
{
std : : map < StdCopyStrBuf , std : : shared_ptr < StdMeshSkeleton > > : : iterator existing_skeleton = Skeletons . find ( filepath ) ;
if ( existing_skeleton ! = Skeletons . end ( ) )
{
Skeletons . erase ( existing_skeleton ) ;
}
std : : map < StdCopyStrBuf , StdCopyStrBuf > : : iterator appendto_skeleton = AppendtoSkeletons . find ( filepath ) ;
if ( appendto_skeleton ! = AppendtoSkeletons . end ( ) )
{
AppendtoSkeletons . erase ( appendto_skeleton ) ;
}
std : : map < StdCopyStrBuf , StdCopyStrBuf > : : iterator include_skeleton = IncludeSkeletons . find ( filepath ) ;
if ( include_skeleton ! = IncludeSkeletons . end ( ) )
{
IncludeSkeletons . erase ( include_skeleton ) ;
}
}
void StdMeshSkeletonLoader : : AddSkeleton ( const StdCopyStrBuf & filepath , std : : shared_ptr < StdMeshSkeleton > skeleton )
{
std : : pair < StdCopyStrBuf , std : : shared_ptr < StdMeshSkeleton > > key_and_value = std : : make_pair ( filepath , skeleton ) ;
std : : pair < std : : map < StdCopyStrBuf , std : : shared_ptr < StdMeshSkeleton > > : : iterator , bool > insert = Skeletons . insert ( key_and_value ) ;
if ( insert . second = = false )
{
LogF ( " WARNING: Overloading skeleton %s " , filepath . getData ( ) ) ;
Skeletons [ filepath ] = skeleton ;
2014-12-26 12:46:14 +00:00
}
2014-12-16 04:42:34 +00:00
}
2010-03-02 16:12:28 +00:00
2014-12-26 12:46:14 +00:00
std : : shared_ptr < StdMeshSkeleton > StdMeshSkeletonLoader : : GetSkeletonByName ( const StdStrBuf & name ) const
2014-12-16 04:42:34 +00:00
{
StdCopyStrBuf filename ( name ) ;
2010-03-02 16:12:28 +00:00
2014-12-16 04:42:34 +00:00
std : : map < StdCopyStrBuf , std : : shared_ptr < StdMeshSkeleton > > : : const_iterator iter = Skeletons . find ( filename ) ;
if ( iter = = Skeletons . end ( ) ) return NULL ;
return iter - > second ;
2010-03-02 16:12:28 +00:00
}
2014-12-26 12:46:14 +00:00
void StdMeshSkeletonLoader : : LoadSkeletonBinary ( const char * groupname , const char * filename , const char * sourcefile , size_t size )
2010-03-02 16:12:28 +00:00
{
boost : : scoped_ptr < Ogre : : Skeleton : : Chunk > chunk ;
2014-12-16 04:42:34 +00:00
Ogre : : DataStream stream ( sourcefile , size ) ;
std : : shared_ptr < StdMeshSkeleton > Skeleton ( new StdMeshSkeleton ) ;
2010-03-02 16:12:28 +00:00
// First chunk must be the header
chunk . reset ( Ogre : : Skeleton : : Chunk : : Read ( & stream ) ) ;
if ( chunk - > GetType ( ) ! = Ogre : : Skeleton : : CID_Header )
throw Ogre : : Skeleton : : InvalidVersion ( ) ;
boost : : ptr_map < uint16_t , StdMeshBone > bones ;
boost : : ptr_vector < Ogre : : Skeleton : : ChunkAnimation > animations ;
for ( Ogre : : Skeleton : : ChunkID id = Ogre : : Skeleton : : Chunk : : Peek ( & stream ) ;
2014-12-16 04:42:34 +00:00
id = = Ogre : : Skeleton : : CID_BlendMode | | id = = Ogre : : Skeleton : : CID_Bone | | id = = Ogre : : Skeleton : : CID_Bone_Parent | | id = = Ogre : : Skeleton : : CID_Animation ;
id = Ogre : : Skeleton : : Chunk : : Peek ( & stream )
)
2010-03-02 16:12:28 +00:00
{
2013-03-19 16:28:19 +00:00
std : : unique_ptr < Ogre : : Skeleton : : Chunk > chunk ( Ogre : : Skeleton : : Chunk : : Read ( & stream ) ) ;
2010-03-02 16:12:28 +00:00
switch ( chunk - > GetType ( ) )
{
2012-10-08 19:50:20 +00:00
case Ogre : : Skeleton : : CID_BlendMode :
{
Ogre : : Skeleton : : ChunkBlendMode & cblend = * static_cast < Ogre : : Skeleton : : ChunkBlendMode * > ( chunk . get ( ) ) ;
// TODO: Handle it
2014-12-16 04:42:34 +00:00
if ( cblend . blend_mode ! = 0 ) // 0 is average, 1 is cumulative. I'm actually not sure what the difference really is... anyway we implement only one method yet. I think it's average, but not 100% sure.
2012-10-08 19:50:20 +00:00
LogF ( " StdMeshLoader: CID_BlendMode not implemented. " ) ;
}
break ;
2010-03-02 16:12:28 +00:00
case Ogre : : Skeleton : : CID_Bone :
2010-03-28 18:58:01 +00:00
{
Ogre : : Skeleton : : ChunkBone & cbone = * static_cast < Ogre : : Skeleton : : ChunkBone * > ( chunk . get ( ) ) ;
// Check that the bone ID is unique
if ( bones . find ( cbone . handle ) ! = bones . end ( ) )
throw Ogre : : Skeleton : : IdNotUnique ( ) ;
StdMeshBone * bone = new StdMeshBone ;
bone - > Parent = NULL ;
bone - > ID = cbone . handle ;
bone - > Name = cbone . name . c_str ( ) ;
bone - > Transformation . translate = cbone . position ;
bone - > Transformation . rotate = cbone . orientation ;
bone - > Transformation . scale = cbone . scale ;
bone - > InverseTransformation = StdMeshTransformation : : Inverse ( bone - > Transformation ) ;
bones . insert ( cbone . handle , bone ) ;
}
break ;
2010-03-02 16:12:28 +00:00
case Ogre : : Skeleton : : CID_Bone_Parent :
2010-03-28 18:58:01 +00:00
{
Ogre : : Skeleton : : ChunkBoneParent & cbparent = * static_cast < Ogre : : Skeleton : : ChunkBoneParent * > ( chunk . get ( ) ) ;
if ( bones . find ( cbparent . parentHandle ) = = bones . end ( ) | | bones . find ( cbparent . childHandle ) = = bones . end ( ) )
throw Ogre : : Skeleton : : BoneNotFound ( ) ;
bones [ cbparent . parentHandle ] . Children . push_back ( & bones [ cbparent . childHandle ] ) ;
bones [ cbparent . childHandle ] . Parent = & bones [ cbparent . parentHandle ] ;
}
break ;
2010-03-02 16:12:28 +00:00
case Ogre : : Skeleton : : CID_Animation :
// Collect animations for later (need bone table index, which we don't know yet)
animations . push_back ( static_cast < Ogre : : Skeleton : : ChunkAnimation * > ( chunk . release ( ) ) ) ;
break ;
2010-03-21 18:08:48 +00:00
default :
assert ( ! " Unexpected enum value " ) ;
break ;
2010-03-02 16:12:28 +00:00
}
if ( stream . AtEof ( ) ) break ;
}
// Find master bone (i.e., the one without a parent)
StdMeshBone * master = NULL ;
for ( boost : : ptr_map < uint16_t , StdMeshBone > : : iterator it = bones . begin ( ) ; it ! = bones . end ( ) ; + + it )
{
if ( ! it - > second - > Parent )
{
master = it - > second ;
2014-12-16 04:42:34 +00:00
Skeleton - > AddMasterBone ( master ) ;
2010-03-02 16:12:28 +00:00
}
}
if ( ! master )
throw Ogre : : Skeleton : : MissingMasterBone ( ) ;
// Transfer bone ownership to mesh (double .release() is correct)
2010-03-28 18:58:01 +00:00
while ( ! bones . empty ( ) ) bones . release ( bones . begin ( ) ) . release ( ) ;
2010-03-02 16:12:28 +00:00
// Build handle->index quick access table
std : : map < uint16_t , size_t > handle_lookup ;
2014-12-16 04:42:34 +00:00
for ( size_t i = 0 ; i < Skeleton - > GetNumBones ( ) ; + + i )
2010-03-02 16:12:28 +00:00
{
2014-12-16 04:42:34 +00:00
handle_lookup [ Skeleton - > GetBone ( i ) . ID ] = i ;
2010-03-02 16:12:28 +00:00
}
// Fixup animations
BOOST_FOREACH ( Ogre : : Skeleton : : ChunkAnimation & canim , animations )
{
2014-12-16 04:42:34 +00:00
StdMeshAnimation & anim = Skeleton - > Animations [ StdCopyStrBuf ( canim . name . c_str ( ) ) ] ;
2010-03-02 16:12:28 +00:00
anim . Name = canim . name . c_str ( ) ;
anim . Length = canim . duration ;
2014-12-16 04:42:34 +00:00
anim . Tracks . resize ( Skeleton - > GetNumBones ( ) ) ;
2014-12-26 12:46:14 +00:00
anim . OriginSkeleton = & ( * Skeleton ) ;
2010-03-02 16:12:28 +00:00
BOOST_FOREACH ( Ogre : : Skeleton : : ChunkAnimationTrack & catrack , canim . tracks )
{
2014-12-16 04:42:34 +00:00
const StdMeshBone & bone = Skeleton - > GetBone ( handle_lookup [ catrack . bone ] ) ;
2010-03-02 16:12:28 +00:00
StdMeshTrack * & track = anim . Tracks [ bone . Index ] ;
if ( track ! = NULL )
throw Ogre : : Skeleton : : MultipleBoneTracks ( ) ;
track = new StdMeshTrack ;
BOOST_FOREACH ( Ogre : : Skeleton : : ChunkAnimationTrackKF & catkf , catrack . keyframes )
{
StdMeshKeyFrame & kf = track - > Frames [ catkf . time ] ;
kf . Transformation . rotate = catkf . rotation ;
kf . Transformation . scale = catkf . scale ;
kf . Transformation . translate = bone . InverseTransformation . rotate * ( bone . InverseTransformation . scale * catkf . translation ) ;
2015-08-05 03:16:36 +00:00
kf . Transformation = OgreToClonk : : TransformTransformation ( kf . Transformation ) ;
2010-03-02 16:12:28 +00:00
}
}
}
// Fixup bone transforms
2014-12-16 04:42:34 +00:00
BOOST_FOREACH ( StdMeshBone * bone , Skeleton - > Bones )
2010-03-02 16:12:28 +00:00
{
if ( bone - > Parent )
2015-08-05 03:16:36 +00:00
bone - > Transformation = bone - > Parent - > Transformation * OgreToClonk : : TransformTransformation ( bone - > Transformation ) ;
else
bone - > Transformation = OgreToClonk : : TransformTransformation ( bone - > Transformation ) ;
bone - > InverseTransformation = StdMeshTransformation : : Inverse ( bone - > Transformation ) ;
2010-03-02 16:12:28 +00:00
}
2014-12-16 04:42:34 +00:00
2014-12-26 12:46:14 +00:00
StoreSkeleton ( groupname , filename , Skeleton ) ;
2014-12-16 04:42:34 +00:00
}
StdMesh * StdMeshLoader : : LoadMeshBinary ( const char * sourcefile , size_t length , const StdMeshMatManager & mat_mgr , StdMeshSkeletonLoader & loader , const char * filename )
{
boost : : scoped_ptr < Ogre : : Mesh : : Chunk > root ;
Ogre : : DataStream stream ( sourcefile , length ) ;
// First chunk must be the header
root . reset ( Ogre : : Mesh : : Chunk : : Read ( & stream ) ) ;
if ( root - > GetType ( ) ! = Ogre : : Mesh : : CID_Header )
throw Ogre : : Mesh : : InvalidVersion ( ) ;
// Second chunk is the mesh itself
root . reset ( Ogre : : Mesh : : Chunk : : Read ( & stream ) ) ;
if ( root - > GetType ( ) ! = Ogre : : Mesh : : CID_Mesh )
throw Ogre : : Mesh : : InvalidVersion ( ) ;
// Generate mesh from data
Ogre : : Mesh : : ChunkMesh & cmesh = * static_cast < Ogre : : Mesh : : ChunkMesh * > ( root . get ( ) ) ;
std : : unique_ptr < StdMesh > mesh ( new StdMesh ) ;
// if the mesh has a skeleton, then try loading
// it from the loader by the definition name
if ( ! cmesh . skeletonFile . empty ( ) )
{
StdCopyStrBuf skeleton_filename = StdCopyStrBuf ( ) ;
StdMeshSkeletonLoader : : MakeFullSkeletonPath ( skeleton_filename , filename , cmesh . skeletonFile . c_str ( ) ) ;
mesh - > Skeleton = loader . GetSkeletonByName ( skeleton_filename ) ;
// with this exception the assert below is useless
// also, I think the bone_lookup should only be used if there is a skeleton anyway
// so there could be meshes without bones even?
if ( mesh - > Skeleton = = NULL )
{
StdCopyStrBuf exception ( " The specified skeleton file was not found: " ) ;
exception . Append ( skeleton_filename . getData ( ) ) ;
throw Ogre : : InsufficientData ( exception . getData ( ) ) ;
}
}
assert ( mesh - > Skeleton ! = NULL ) ; // the bone assignments could instead be added only, if there is a skeleton
// Build bone handle->index quick access table
std : : map < uint16_t , size_t > bone_lookup ;
for ( size_t i = 0 ; i < mesh - > GetSkeleton ( ) . GetNumBones ( ) ; + + i )
{
bone_lookup [ mesh - > GetSkeleton ( ) . GetBone ( i ) . ID ] = i ;
}
// Read submeshes
mesh - > SubMeshes . reserve ( cmesh . submeshes . size ( ) ) ;
for ( size_t i = 0 ; i < cmesh . submeshes . size ( ) ; + + i )
{
mesh - > SubMeshes . push_back ( StdSubMesh ( ) ) ;
StdSubMesh & sm = mesh - > SubMeshes . back ( ) ;
Ogre : : Mesh : : ChunkSubmesh & csm = cmesh . submeshes [ i ] ;
sm . Material = mat_mgr . GetMaterial ( csm . material . c_str ( ) ) ;
if ( ! sm . Material )
throw Ogre : : Mesh : : InvalidMaterial ( ) ;
if ( csm . operation ! = Ogre : : Mesh : : ChunkSubmesh : : SO_TriList )
throw Ogre : : Mesh : : NotImplemented ( " Submesh operations other than TriList aren't implemented yet " ) ;
sm . Faces . resize ( csm . faceVertices . size ( ) / 3 ) ;
for ( size_t face = 0 ; face < sm . Faces . size ( ) ; + + face )
{
sm . Faces [ face ] . Vertices [ 0 ] = csm . faceVertices [ face * 3 + 0 ] ;
sm . Faces [ face ] . Vertices [ 1 ] = csm . faceVertices [ face * 3 + 1 ] ;
sm . Faces [ face ] . Vertices [ 2 ] = csm . faceVertices [ face * 3 + 2 ] ;
}
Ogre : : Mesh : : ChunkGeometry & geo = * ( csm . hasSharedVertices ? cmesh . geometry : csm . geometry ) ;
sm . Vertices = ReadSubmeshGeometry ( geo , filename ) ;
// Read bone assignments
std : : vector < Ogre : : Mesh : : BoneAssignment > & boneAssignments = ( csm . hasSharedVertices ? cmesh . boneAssignments : csm . boneAssignments ) ;
assert ( ! csm . hasSharedVertices | | csm . boneAssignments . empty ( ) ) ;
2015-02-27 20:24:29 +00:00
for ( const auto & ba : boneAssignments )
2014-12-16 04:42:34 +00:00
{
if ( ba . vertex > = sm . GetNumVertices ( ) )
throw Ogre : : Mesh : : VertexNotFound ( ) ;
if ( bone_lookup . find ( ba . bone ) = = bone_lookup . end ( ) )
throw Ogre : : Skeleton : : BoneNotFound ( ) ;
2015-02-27 20:24:29 +00:00
size_t bone_index = bone_lookup [ ba . bone ] ;
// Check quickly if all weight slots are used
StdSubMesh : : Vertex & vertex = sm . Vertices [ ba . vertex ] ;
if ( vertex . bone_weight [ StdMeshVertex : : MaxBoneWeightCount - 1 ] ! = 0 )
{
throw Ogre : : Mesh : : NotImplemented ( " Vertex is influenced by too many bones " ) ;
}
for ( size_t weight_index = 0 ; weight_index < StdMeshVertex : : MaxBoneWeightCount ; + + weight_index )
{
if ( vertex . bone_weight [ weight_index ] = = 0 )
{
vertex . bone_weight [ weight_index ] = ba . weight ;
vertex . bone_index [ weight_index ] = bone_index ;
break ;
}
}
2014-12-16 04:42:34 +00:00
}
// Normalize bone assignments
2015-02-27 20:24:29 +00:00
for ( StdSubMesh : : Vertex & vertex : sm . Vertices )
2014-12-16 04:42:34 +00:00
{
float sum = 0 ;
2015-02-27 20:24:29 +00:00
for ( float weight : vertex . bone_weight )
sum + = weight ;
if ( sum ! = 0 )
for ( float & weight : vertex . bone_weight )
weight / = sum ;
2015-03-05 12:09:40 +00:00
else
vertex . bone_weight [ 0 ] = 1.0f ;
2014-12-16 04:42:34 +00:00
}
}
2015-08-05 03:16:36 +00:00
// Construct bounding box. Don't use bounds and radius from cmesh
// because they are in a different coordinate frame.
//mesh->BoundingBox = cmesh.bounds;
//mesh->BoundingRadius = cmesh.radius;
bool first = true ;
for ( unsigned int i = 0 ; i < mesh - > SubMeshes . size ( ) + 1 ; + + i )
{
const std : : vector < StdSubMesh : : Vertex > * vertices = NULL ;
if ( i < mesh - > SubMeshes . size ( ) )
vertices = & mesh - > SubMeshes [ i ] . Vertices ;
else
vertices = & mesh - > SharedVertices ;
for ( unsigned int j = 0 ; j < vertices - > size ( ) ; + + j )
{
const StdMeshVertex & vertex = ( * vertices ) [ j ] ;
const float d = std : : sqrt ( vertex . x * vertex . x
+ vertex . y * vertex . y
+ vertex . z * vertex . z ) ;
// First vertex
if ( first )
{
mesh - > BoundingBox . x1 = mesh - > BoundingBox . x2 = vertex . x ;
mesh - > BoundingBox . y1 = mesh - > BoundingBox . y2 = vertex . y ;
mesh - > BoundingBox . z1 = mesh - > BoundingBox . z2 = vertex . z ;
mesh - > BoundingRadius = d ;
first = false ;
}
else
{
mesh - > BoundingBox . x1 = Min ( vertex . x , mesh - > BoundingBox . x1 ) ;
mesh - > BoundingBox . x2 = Max ( vertex . x , mesh - > BoundingBox . x2 ) ;
mesh - > BoundingBox . y1 = Min ( vertex . y , mesh - > BoundingBox . y1 ) ;
mesh - > BoundingBox . y2 = Max ( vertex . y , mesh - > BoundingBox . y2 ) ;
mesh - > BoundingBox . z1 = Min ( vertex . z , mesh - > BoundingBox . z1 ) ;
mesh - > BoundingBox . z2 = Max ( vertex . z , mesh - > BoundingBox . z2 ) ;
mesh - > BoundingRadius = Max ( mesh - > BoundingRadius , d ) ;
}
}
}
// We allow bounding box to be empty if it's only due to Z direction since
// this is what goes inside the screen in Clonk.
if ( mesh - > BoundingBox . x1 = = mesh - > BoundingBox . x2 | | mesh - > BoundingBox . y1 = = mesh - > BoundingBox . y2 )
throw Ogre : : Mesh : : EmptyBoundingBox ( ) ;
2014-12-16 04:42:34 +00:00
return mesh . release ( ) ;
2010-03-02 16:12:28 +00:00
}
2014-12-26 12:46:14 +00:00
void StdMeshSkeletonLoader : : ResolveIncompleteSkeletons ( )
{
DoResetSkeletons ( ) ;
DoAppendSkeletons ( ) ;
DoIncludeSkeletons ( ) ;
}
void StdMeshSkeletonLoader : : DoResetSkeletons ( )
{
std : : map < StdCopyStrBuf , std : : shared_ptr < StdMeshSkeleton > > : : iterator it ;
for ( it = Skeletons . begin ( ) ; it ! = Skeletons . end ( ) ; it + + )
{
std : : shared_ptr < StdMeshSkeleton > skeleton = it - > second ;
// remove animations from destination
std : : map < StdCopyStrBuf , StdMeshAnimation > : : const_iterator animations = skeleton - > Animations . begin ( ) ;
while ( animations ! = skeleton - > Animations . end ( ) )
{
if ( animations - > second . OriginSkeleton ! = & ( * ( skeleton ) ) )
{
animations = skeleton - > Animations . erase ( animations ) ;
}
else
{
+ + animations ;
}
}
}
}
void StdMeshSkeletonLoader : : DoAppendSkeletons ( )
{
// handle the "appendto.<C4ID>.skeleton" files.
2015-01-06 11:45:23 +00:00
std : : map < StdCopyStrBuf , StdCopyStrBuf > : : iterator it ;
2014-12-26 12:46:14 +00:00
for ( it = AppendtoSkeletons . begin ( ) ; it ! = AppendtoSkeletons . end ( ) ; it + + )
{
StdCopyStrBuf id ( it - > second ) ;
StdMeshSkeleton * destination = GetSkeletonByDefinition ( id . getData ( ) ) ;
// append animations, if the definition has a mesh
2015-01-06 18:33:33 +00:00
if ( destination = = NULL )
2014-12-26 12:46:14 +00:00
{
2015-01-06 18:33:33 +00:00
// Note that GetSkeletonByDefinition logs already why
// the skeleton does not exist.
LogF ( " WARNING: Appending skeleton '%s' failed " , it - > first . getData ( ) ) ;
2014-12-26 12:46:14 +00:00
}
else
{
2015-01-06 11:45:23 +00:00
std : : shared_ptr < StdMeshSkeleton > source = GetSkeletonByName ( it - > first ) ;
2014-12-26 12:46:14 +00:00
std : : map < StdCopyStrBuf , StdMeshAnimation > : : const_iterator animations ;
// append animations from source
for ( animations = source - > Animations . begin ( ) ; animations ! = source - > Animations . end ( ) ; animations + + )
{
if ( destination - > Animations . find ( animations - > first ) ! = destination - > Animations . end ( ) )
{
2015-01-06 11:45:23 +00:00
LogF ( " WARNING: Overloading animation '%s' is not allowed. This animation already exists in '%s'. " , animations - > first . getData ( ) , id . getData ( ) ) ;
2014-12-26 12:46:14 +00:00
}
else
{
destination - > InsertAnimation ( * source , animations - > second ) ;
}
}
}
}
}
void StdMeshSkeletonLoader : : DoIncludeSkeletons ( )
{
// handle the "include.<C4ID>.skeleton" files.
2015-01-06 11:45:23 +00:00
std : : map < StdCopyStrBuf , StdCopyStrBuf > : : iterator it ;
2014-12-26 12:46:14 +00:00
for ( it = IncludeSkeletons . begin ( ) ; it ! = IncludeSkeletons . end ( ) ; it + + )
{
StdCopyStrBuf id ( it - > second ) ;
StdMeshSkeleton * source = GetSkeletonByDefinition ( id . getData ( ) ) ;
// append animations, if the definition has a mesh
2015-01-06 18:33:33 +00:00
if ( source = = NULL )
2014-12-26 12:46:14 +00:00
{
2015-01-06 18:33:33 +00:00
// Note that GetSkeletonByDefinition logs already why
// the skeleton does not exist.
LogF ( " WARNING: Including skeleton '%s' failed " , it - > first . getData ( ) ) ;
2014-12-26 12:46:14 +00:00
}
else
{
2015-01-06 11:45:23 +00:00
std : : shared_ptr < StdMeshSkeleton > destination = GetSkeletonByName ( it - > first ) ;
2014-12-26 12:46:14 +00:00
std : : map < StdCopyStrBuf , StdMeshAnimation > : : const_iterator animations ;
// append animations from source
for ( animations = source - > Animations . begin ( ) ; animations ! = source - > Animations . end ( ) ; animations + + )
{
if ( destination - > Animations . find ( animations - > first ) ! = destination - > Animations . end ( ) )
{
2015-01-06 11:45:23 +00:00
LogF ( " WARNING: Animation '%s' from %s is not included. A newer version of the animation exists in the destination file. " , animations - > first . getData ( ) , id . getData ( ) ) ;
2014-12-26 12:46:14 +00:00
}
else
{
destination - > InsertAnimation ( * source , animations - > second ) ;
}
}
}
}
}