blitz3d/blitz3d/loader_b3d.cpp

361 lines
6.7 KiB
C++

#include "std.h"
#include "loader_b3d.h"
#include "meshmodel.h"
#include "pivot.h"
#include "meshutil.h"
//#define SHOW_BONES
static FILE *in;
static vector<int> chunk_stack;
static vector<Texture> textures;
static vector<Brush> brushes;
static vector<Object*> bones;
static bool collapse;
static bool animonly;
static int swap_endian( int n ){
return ((n&0xff)<<24)|((n&0xff00)<<8)|((n&0xff0000)>>8)|((n&0xff000000)>>24);
}
static void clear(){
bones.clear();
brushes.clear();
textures.clear();
chunk_stack.clear();
}
static int readChunk(){
int header[2];
if( fread( header,8,1,in )<1 ) return 0;
chunk_stack.push_back( ftell( in )+header[1] );
return swap_endian( header[0] );
}
static void exitChunk(){
fseek( in,chunk_stack.back(),SEEK_SET );
chunk_stack.pop_back();
}
static int chunkSize(){
return chunk_stack.back()-ftell( in );
}
static void read( void *buf,int n ){
fread( buf,n,1,in );
}
static void skip( int n ){
fseek( in,n,SEEK_CUR );
}
static int readInt(){
int n;
read( &n,4 );
return n;
}
static void readIntArray( int t[],int n ){
read( t,n*4 );
}
static float readFloat(){
float n;
read( &n,4 );
return n;
}
static void readFloatArray( float t[],int n ){
read( t,n*4 );
}
static void readColor( unsigned *t ){
float r=readFloat();if(r<0) r=0;else if(r>1) r=1;
float g=readFloat();if(g<0) g=0;else if(g>1) g=1;
float b=readFloat();if(b<0) b=0;else if(b>1) b=1;
float a=readFloat();if(a<0) a=0;else if(a>1) a=1;
*t=(int(a*255)<<24)|(int(r*255)<<16)|(int(g*255)<<8)|int(b*255);
}
static string readString(){
string t;
for(;;){
char c;
read( &c,1 );
if( !c ) return t;
t+=c;
}
}
static void readTextures(){
while( chunkSize() ){
string name=readString();
int flags=readInt();
int blend=readInt();
float pos[2],scl[2];
readFloatArray( pos,2 );
readFloatArray( scl,2 );
float rot=readFloat();
//create texture
Texture tex( name,flags & 0xffff );
tex.setBlend( blend );
if( flags & 0x10000 ) tex.setFlags( gxScene::TEX_COORDS2 );
if( pos[0]!=0 || pos[1]!=0 ) tex.setPosition( pos[0],pos[1] );
if( scl[0]!=1 || scl[1]!=1 ) tex.setScale( scl[0],scl[1] );
if( rot!=0 ) tex.setRotation( rot );
textures.push_back( tex );
}
}
static void readBrushes(){
int n_texs=readInt();
int tex_id[8]={-1,-1,-1,-1,-1,-1,-1,-1};
while( chunkSize() ){
string name=readString();
float col[4];
readFloatArray( col,4 );
float shi=readFloat();
int blend=readInt();
int fx=readInt();
readIntArray( tex_id,n_texs );
Brush bru;
bru.setColor( Vector( col[0],col[1],col[2] ) );
bru.setAlpha( col[3] );
bru.setShininess( shi );
bru.setBlend( blend );
bru.setFX( fx );
for( int k=0;k<8;++k ){
if( tex_id[k]<0 ) continue;
bru.setTexture( k,textures[tex_id[k]],0 );
}
brushes.push_back( bru );
}
}
static int readVertices(){
int flags=readInt();
int tc_sets=readInt();
int tc_size=readInt();
float tc[4]={0};
Surface::Vertex t;
while( chunkSize() ){
readFloatArray( t.coords,3 );
if( flags&1 ){
readFloatArray( t.normal,3 );
}
if( flags&2 ){
readColor( &t.color );
}
for( int k=0;k<tc_sets;++k ){
readFloatArray( tc,tc_size );
if( k<2 ) memcpy( t.tex_coords[k],tc,8 );
}
MeshLoader::addVertex( t );
}
return flags;
}
static void readTriangles(){
int brush_id=readInt();
Brush b=brush_id>=0 ? brushes[brush_id] : Brush();
while( chunkSize() ){
int verts[3];
readIntArray( verts,3 );
MeshLoader::addTriangle( verts,b );
}
}
static int readMesh(){
int flags=0;
while( chunkSize() ){
switch( readChunk() ){
case 'VRTS':
flags=readVertices();
break;
case 'TRIS':
readTriangles();
break;
}
exitChunk();
}
return flags;
}
static Object *readBone(){
#ifdef SHOW_BONES
Brush b;
b.setColor( Vector( 1,0,0 ) );
b.setAlpha( .75f );
MeshModel *bone=MeshUtil::createSphere( b,16 );
Transform t;
t.m.i.x=.1f;
t.m.j.y=.1f;
t.m.k.z=.1f;
bone->transform( t );
#else
Pivot *bone=d_new Pivot();
#endif
bones.push_back( bone );
while( chunkSize() ){
int vert=readInt();
float weight=readFloat();
MeshLoader::addBone( vert,weight,bones.size() );
}
return bone;
}
static void readKeys( Animation &anim ){
int flags=readInt();
while( chunkSize() ){
int frame=readInt();
if( flags&1 ){
float pos[3];
readFloatArray( pos,3 );
anim.setPositionKey( frame,Vector(pos[0],pos[1],pos[2]) );
}
if( flags&2 ){
float scl[3];
readFloatArray( scl,3 );
anim.setScaleKey( frame,Vector(scl[0],scl[1],scl[2]) );
}
if( flags&4 ){
float rot[4];
readFloatArray( rot,4 );
anim.setRotationKey( frame,Quat(rot[0],Vector(rot[1],rot[2],rot[3])) );
}
}
}
static Object *readObject( Object *parent ){
Object *obj=0;
string name=readString();
float pos[3],scl[3],rot[4];
readFloatArray( pos,3 );
readFloatArray( scl,3 );
readFloatArray( rot,4 );
Animation keys;
int anim_len=0;
MeshModel *mesh=0;
int mesh_flags,mesh_brush;
while( chunkSize() ){
switch( readChunk() ){
case 'MESH':
MeshLoader::beginMesh();
obj=mesh=d_new MeshModel();
mesh_brush=readInt();
mesh_flags=readMesh();
break;
case 'BONE':
obj=readBone();
break;
case 'KEYS':
readKeys( keys );
break;
case 'ANIM':
readInt();
anim_len=readInt();
readFloat();
break;
case 'NODE':
if( !obj ) obj=d_new MeshModel();
readObject( obj );
break;
}
exitChunk();
}
if( !obj ) obj=d_new MeshModel();
obj->setName( name );
obj->setLocalPosition( Vector( pos[0],pos[1],pos[2] ) );
obj->setLocalScale( Vector( scl[0],scl[1],scl[2] ) );
obj->setLocalRotation( Quat( rot[0],Vector( rot[1],rot[2],rot[3] ) ) );
obj->setAnimation( keys );
if( mesh ){
MeshLoader::endMesh( mesh );
if( !(mesh_flags&1) ) mesh->updateNormals();
if( mesh_brush!=-1 ) mesh->setBrush( brushes[mesh_brush] );
}
if( mesh && bones.size() ){
bones.insert( bones.begin(),mesh );
mesh->setAnimator( d_new Animator( bones,anim_len ) );
mesh->createBones();
bones.clear();
}else if( anim_len ){
obj->setAnimator( d_new Animator( obj,anim_len ) );
}
if( parent ) obj->setParent( parent );
return obj;
}
MeshModel *Loader_B3D::load( const string &f,const Transform &conv,int hint ){
collapse=!!(hint&MeshLoader::HINT_COLLAPSE);
animonly=!!(hint&MeshLoader::HINT_ANIMONLY);
in=fopen( f.c_str(),"rb" );
if( !in ) return 0;
::clear();
int tag=readChunk();
if( tag!='BB3D' ){
fclose( in );
return 0;
}
int version=readInt();
if( version>1 ){
fclose( in );
return 0;
}
Object *obj=0;
while( chunkSize() ){
switch( readChunk() ){
case 'TEXS':
readTextures();
break;
case 'BRUS':
readBrushes();
break;
case 'NODE':
obj=readObject( 0 );
break;
}
exitChunk();
}
fclose( in );
::clear();
return obj ? obj->getModel()->getMeshModel() : 0;
}