/****************************************************************************** libx42make - skinned vertex animation library (exporter utilities) Copyright (C) 2007 HermitWorks Entertainment Corporation This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor Boston, MA 02110-1301, USA. ******************************************************************************/ #include "local.h" namespace x42 { namespace make { class x42strings { public: x42NameIndex_t intern_string( const std::string &str ); void to_map_and_blob( std::vector< x42NameIndex_t > &map, std::vector< u8 > &blob ); private: std::vector< std::string > strings; }; x42NameIndex_t x42strings::intern_string( const std::string &str ) { for( uint i = 0; i < strings.size(); i++ ) { if( str.compare( strings[i] ) == 0 ) return (x42NameIndex_t)i; } size_t ret = strings.size(); strings.push_back( str ); return (x42NameIndex_t)ret; } void x42strings::to_map_and_blob( std::vector< x42NameIndex_t > &map, std::vector< u8 > &blob ) { for( std::vector< std::string >::iterator iter = strings.begin(); iter < strings.end(); ++iter ) { map.push_back( (x42NameIndex_t)blob.size() ); blob.insert( blob.end(), iter->begin(), iter->end() ); blob.push_back( 0 ); } } template< typename T, typename I = u16 > class palette { public: I get_index( const T &v ) { for( uint i = 0; i < values.size(); i++ ) { if( values[i] == v ) return checked_int_cast< I >( i ); } values.push_back( v ); return checked_int_cast< I >( (uint)values.size() - 1U ); } std::vector< T > values; }; class x42builder { public: explicit x42builder( const ModelData &data ); x42::model make_model(); private: x42header_t h; palette< vec3 > posVals; palette< vec3 > scaleVals; palette< quat > rotVals; std::vector< x42keyStreamEntry_t > ks; std::vector< x42animation_t > anims; std::vector< x42animGroup_t > animGroups; std::vector< x42bone_t > bones; std::vector< u8 > boneGroups; std::vector< x42influence_t > influences; std::vector< x42tag_t > tags; std::vector< x42lodRange_t > lods; std::vector< x42group_t > groups; std::vector< x42vertAnim_t > vertPos; std::vector< x42vertNormal_t > vertNorm; std::vector< x42vertTangent_t > vertTan; std::vector< vec2 > vertTc; std::vector< rgba > vertCl; std::vector< index > indices; std::vector< u8 > strings; x42strings _sb; void pack_bones( const ModelData &data ); void pack_influences( const ModelData &data ); void pack_tags( const ModelData &data ); void pack_groups( const ModelData &data ); void pack_anims( const ModelData &data ); void fixup_names(); bool has_uniform_scale() const; void make_header( const ModelData &data ); }; x42builder::x42builder( const ModelData &data ) { persist_flags pers = persist_flags::none; pack_bones( data ); pack_influences( data ); pack_tags( data ); pack_groups( data ); pack_anims( data ); fixup_names(); make_header( data ); } template< typename T > inline void copy_vector_data( T *dst, const std::vector< T > &src ) { if( src.size() ) memcpy( dst, &src[0], sizeof( T ) * src.size() ); } x42::model x42builder::make_model() { x42::model ret( h, persist_flags::include_everything ); x42data_t *dst = ret.raw_data(); copy_vector_data( dst->keyStream, ks ); copy_vector_data( dst->animations, anims ); copy_vector_data( (vec3*)dst->posValues, posVals.values ); copy_vector_data( (vec3*)dst->scaleValues, scaleVals.values ); copy_vector_data( (quat*)dst->rotValues, rotVals.values ); copy_vector_data( dst->animGroups, animGroups ); copy_vector_data( dst->bones, bones ); copy_vector_data( dst->boneGroups, boneGroups ); copy_vector_data( dst->influences, influences ); copy_vector_data( dst->tags, tags ); copy_vector_data( dst->lods, lods ); copy_vector_data( dst->groups, groups ); copy_vector_data( dst->vertPos, vertPos ); copy_vector_data( dst->vertNorm, vertNorm ); copy_vector_data( dst->vertTan, vertTan ); copy_vector_data( (vec2*)dst->vertTc, vertTc ); copy_vector_data( (rgba*)dst->vertCl, vertCl ); copy_vector_data( dst->indices, indices ); copy_vector_data( (u8*)const_cast< char* >( dst->strings ), strings ); return ret; } struct anim_key { u16 frame; u16 value; }; template< typename T, typename I > std::queue< anim_key > track_to_palletized_queue( palette< T, I > &p, const std::vector< AnimTrackElem< T > > &track ) { std::queue< anim_key > ret; for( uint i = 0; i < track.size(); i++ ) { anim_key k; k.frame = (u16)track[i].frame; k.value = p.get_index( track[i].value ); ret.push( k ); } return ret; } struct bone_trk { std::queue< anim_key > q; u16 lastFrame; }; struct bone_trks { bone_trk p; bone_trk s; bone_trk r; bone_trk& operator[] ( uint i ) { switch( i ) { case X42_KT_POSITION: return p; case X42_KT_SCALE: return s; case X42_KT_ROTATION: return r; default: throw std::exception(); } } }; void x42builder::pack_bones( const ModelData &data ) { if( !data.bones.size() ) return; bones.resize( data.bones.size() ); boneGroups.resize( data.bones.size() ); std::vector< bone_trks > tracks( data.bones.size() ); uint maxAnimGroup = 0; for( uint i = 0; i < data.bones.size(); i++ ) { ConstBonePtr b = data.bones[i]; if( b->animGroup > maxAnimGroup ) maxAnimGroup = data.bones[i]->animGroup; boneGroups[i] = (u8)b->animGroup; bone_trks &t = tracks[i]; t.p.q = track_to_palletized_queue( posVals, b->posTrack ); t.s.q = track_to_palletized_queue( scaleVals, b->scaleTrack ); t.r.q = track_to_palletized_queue( rotVals, b->rotTrack ); } animGroups.resize( maxAnimGroup + 1 ); for( uint i = 0; i < animGroups.size(); i++ ) { animGroups[i].name = _sb.intern_string( std::string( "" ) ); animGroups[i].beginBone = (u16)data.numFrames; animGroups[i].endBone = 0; } for( uint i = 0; i < data.bones.size(); i++ ) { ConstBonePtr in = data.bones[i]; x42bone_t &out = bones[i]; if( animGroups[in->animGroup].beginBone > i ) animGroups[in->animGroup].beginBone = (u16)i; if( animGroups[in->animGroup].endBone <= i ) animGroups[in->animGroup].endBone = (u16)(i + 1); out.name = _sb.intern_string( in->name ); out.parentIdx = in->parent ? (u16)in->parent->index : X42_MODEL_BONE; out.flags = (u16)in->flags; out.extent = in->extent; for( uint ti = 0; ti < 3; ti++ ) { bone_trk &te = tracks[i][ti]; x42keyStreamEntry_t ke; ke.type = (u16)ti; ke.bone = (u16)i; ke.frame = 0; //force first elem to frame 0 ke.value = te.q.front().value; ks.push_back( ke ); if( te.q.front().frame != 0 ) { //first key is a ways into the stream, //emit it again with its proper frame value ke.frame = te.q.front().frame; ks.push_back( ke ); } else { //first key is at start of anim te.q.pop(); //emit the second as well ke.frame = te.q.front().frame; ke.value = te.q.front().value; ks.push_back( ke ); } te.q.pop(); te.lastFrame = ke.frame; } } //emit the rest of the keys for( ; ; ) { uint bestBone = 0, bestType = 0; uint bestFrame = uint_traits::max_val; for( uint i = 0; i < tracks.size(); i++ ) { for( uint ti = 0; ti < 3; ti++ ) { bone_trk &te = tracks[i][ti]; if( te.q.empty() ) continue; if( te.lastFrame < bestFrame ) { bestBone = i; bestType = ti; bestFrame = te.lastFrame; } } } if( bestFrame == uint_traits::max_val ) //out of keys, exit break; bone_trk &te = tracks[bestBone][bestType]; x42keyStreamEntry_t ke; ke.type = (u16)bestType; ke.bone = (u16)bestBone; ke.frame = te.q.front().frame; ke.value = te.q.front().value; ks.push_back( ke ); te.q.pop(); te.lastFrame = ke.frame; } } static void copy_affine( affine_t &out, const affine &in ) { for( uint c = 0; c < 4; c++ ) for( uint r = 0; r < 3; r++ ) out.c[c][r] = in.c[c][r]; } void x42builder::pack_influences( const ModelData &data ) { influences.resize( data.influences.size() ); for( uint i = 0; i < influences.size(); i++ ) { const Influence &in = data.influences[i]; x42influence_t &out = influences[i]; out.bone = in.bone ? (u16)in.bone->index : X42_MODEL_BONE; copy_affine( out.meshToBone, in.objToBone ); } } void x42builder::pack_tags( const ModelData &data ) { tags.resize( data.tags.size() ); for( uint i = 0; i < tags.size(); i++ ) { const Tag &in = data.tags[i]; x42tag_t &out = tags[i]; out.name = _sb.intern_string( in.name ); out.bone = in.bone ? (u16)in.bone->index : X42_MODEL_BONE; copy_affine( out.tagMatrix, in.tagMatrix ); } } inline void copy_vec3( vec3_t &out, const vec3 &in ) { out[0] = in.x; out[1] = in.y; out[2] = in.z; } void x42builder::pack_groups( const ModelData &data ) { lods.resize( data.lods.size() ); for( uint l = 0; l < lods.size(); l++ ) { ConstLodPtr inLod = data.lods[l]; x42lodRange_t &outLod = lods[l]; outLod.lodName = (u16)inLod->GetLod(); outLod.firstGroup = (u16)groups.size(); for( uint i = 0; i < inLod->groups.size(); i++ ) { ConstGroupPtr in = inLod->groups[i]; x42group_t out; memset( &out, 0, sizeof( out ) ); out.material = _sb.intern_string( in->material ); out.surfaceName = _sb.intern_string( in->surfaceName ); out.maxVertInfluences = (u16)in->maxInfsPerVert; out.numInfluences = (u16)in->infMap.size(); if( in->infMap.size() == 1 ) { const Influence &inf = data.influences[in->infMap[0]]; if( !inf.bone && inf.objToBone == affinei() ) out.maxVertInfluences = 0; //special case } for( uint i = 0; i < in->infMap.size(); i++ ) out.influences[i] = (u16)in->infMap[i]; out.primType = (u16)in->primType; out.firstVert = (u32)vertPos.size(); out.numVerts = (u32)in->verts.size(); if( in->indices.size() ) { out.numElems = (u32)in->indices.size(); out.firstIndex = (u32)indices.size(); } else { out.numElems = (u32)in->verts.size(); out.firstIndex = X42_NO_INDICES; } for( uint i = 0; i < in->verts.size(); i++ ) { const Vertex &v = in->verts[i]; x42vertAnim_t va; x42vertNormal_t vn; x42vertTangent_t vt; copy_vec3( va.pos, v.pos ); for( uint i = 0; i < X42_WEIGHTS_PER_VERT; i++ ) { va.idx[i] = (byte)v.idx[i]; va.wt[i] = v.wt[i]; } copy_vec3( vn.norm, v.norm ); vn.pad = 0; copy_vec3( vt.tan, v.tan ); copy_vec3( vt.bit, v.bin ); vec3 txb = cross( v.tan, v.bin ); float nf = (float)sign( dot( v.norm, txb ) ); if( nf == 0 ) nf = 1; vt.nfac0 = nf; vt.nfac1 = nf; vertPos.push_back( va ); vertNorm.push_back( vn ); vertTan.push_back( vt ); vertTc.push_back( v.tex ); vertCl.push_back( v.cl ); } for( uint i = 0; i < in->indices.size(); i++ ) indices.push_back( in->indices[i] ); groups.push_back( out ); } outLod.numGroups = (u16)groups.size() - outLod.firstGroup; } } void x42builder::pack_anims( const ModelData &data ) { anims.resize( data.animations.size() ); for( uint i = 0; i < anims.size(); i++ ) { const Animation &in = data.animations[i]; x42animation_t &out = anims[i]; out.name = _sb.intern_string( in.name ); out.firstFrame = checked_int_cast< u16 >( in.firstFrame - data.baseFrame ); out.lastFrame = checked_int_cast< u16 >( in.lastFrame - data.baseFrame ); out.loopStart = checked_int_cast< u16 >( in.loopStart - data.baseFrame ); out.loopEnd = checked_int_cast< u16 >( in.loopEnd - data.baseFrame ); uint fps = (uint)(in.frameRate * 1000); //to frames per millisecond out.frameRate = checked_int_cast< u16 >( fps ); } } template< typename T > void fixup_name_refs( std::vector< T > &v, const std::vector< x42NameIndex_t > &map ) { for( uint i = 0; i < v.size(); i++ ) v[i].name = map[v[i].name]; } void x42builder::fixup_names() { std::vector< x42NameIndex_t > map; _sb.to_map_and_blob( map, strings ); fixup_name_refs( anims, map ); fixup_name_refs( animGroups, map ); fixup_name_refs( bones, map ); fixup_name_refs( tags, map ); for( uint i = 0; i < groups.size(); i++ ) { groups[i].material = map[groups[i].material]; groups[i].surfaceName = map[groups[i].surfaceName]; } } bool x42builder::has_uniform_scale() const { for( uint i = 0; i < scaleVals.values.size(); i++ ) { const vec3 &v = scaleVals.values[i]; if( v.x != v.y || v.y != v.z || v.z != v.x ) return false; } return true; } inline void copy_sphere( sphere_t &out, const sphere &in ) { copy_vec3( out.center, in.center ); out.radius = in.radius; } inline void copy_aabb( aabb_t &out, const aabb &in ) { copy_vec3( out.mins, in.mins ); copy_vec3( out.maxs, in.maxs ); } void x42builder::make_header( const ModelData &data ) { h.ident.ident = X42_IDENT; h.ident.version = X42_SAVE_VER; h.modelFlags = X42_MF_HAS_NORMALS | X42_MF_HAS_TANGENT_BASIS | X42_MF_HAS_TEXTURE_COORDINATES | X42_MF_HAS_COLORS; if( has_uniform_scale() ) h.modelFlags |= X42_MF_UNIFORM_SCALE; h.baseFrame = (s16)data.baseFrame; h.numFrames = (u16)data.numFrames; h.nameBlobLen = (u16)strings.size(); h.numBones = (u16)bones.size(); h.numAnimGroups = (u16)animGroups.size(); h.numPosValues = (u16)posVals.values.size(); h.numScaleValues = (u16)scaleVals.values.size(); h.numRotValues = (u16)rotVals.values.size(); h.keyStreamLength = (u16)ks.size(); h.numAnims = (u16)anims.size(); h.numTags = (u16)tags.size(); h.numInfluences = (u16)influences.size(); h.numLods = (u16)lods.size(); h.numGroups = (u16)groups.size(); h.numVerts = (u32)vertPos.size(); h.numIndices = (u32)indices.size(); copy_aabb( h.boundingBox, data.boundingBox ); copy_sphere( h.boundingSphere, data.boundingSphere ); } model ModelData::ToModel( void ) const { x42builder builder( *this ); return builder.make_model(); } }; };