runtime: CMake-ify

This commit is contained in:
Michael Fabian 'Xaymar' Dirks
2019-01-18 15:55:06 +01:00
parent 717609a900
commit 24788185aa
184 changed files with 2957 additions and 2527 deletions
+138
View File
@@ -0,0 +1,138 @@
project(runtime_blitz3d)
add_library(${PROJECT_NAME} STATIC
"animation.cpp"
"animation.hpp"
"animator.cpp"
"animator.hpp"
"blitz3d.hpp"
"brush.cpp"
"brush.hpp"
"cachedtexture.cpp"
"cachedtexture.hpp"
"camera.cpp"
"camera.hpp"
"collision.cpp"
"collision.hpp"
"entity.cpp"
"entity.hpp"
"frustum.cpp"
"frustum.hpp"
"geom.cpp"
"geom.hpp"
"light.cpp"
"light.hpp"
"listener.cpp"
"listener.hpp"
"loader_3ds.cpp"
"loader_3ds.hpp"
"loader_b3d.cpp"
"loader_b3d.hpp"
# "loader_x.cpp"
# "loader_x.hpp"
"md2model.cpp"
"md2model.hpp"
"md2norms.cpp"
"md2norms.hpp"
"md2rep.cpp"
"md2rep.hpp"
"meshcollider.cpp"
"meshcollider.hpp"
"meshloader.cpp"
"meshloader.hpp"
"meshmodel.cpp"
"meshmodel.hpp"
"meshutil.cpp"
"meshutil.hpp"
"mirror.cpp"
"mirror.hpp"
"model.cpp"
"model.hpp"
"object.cpp"
"object.hpp"
"pivot.cpp"
"pivot.hpp"
"planemodel.cpp"
"planemodel.hpp"
"q3bspmodel.cpp"
"q3bspmodel.hpp"
"q3bsprep.cpp"
"q3bsprep.hpp"
"rendercontext.hpp"
"sprite.cpp"
"sprite.hpp"
"std.cpp"
"std.hpp"
"surface.cpp"
"surface.hpp"
"terrain.cpp"
"terrain.hpp"
"terrainrep.cpp"
"terrainrep.hpp"
"texture.cpp"
"texture.hpp"
"world.cpp"
"world.hpp"
)
target_link_libraries(${PROJECT_NAME}
PRIVATE
config
gxruntime
stdutil
PUBLIC
)
target_include_directories(${PROJECT_NAME}
PUBLIC ${PROJECT_SOURCE_DIR}
)
if (WIN32)
target_compile_definitions(${PROJECT_NAME}
PRIVATE
_CRT_SECURE_NO_WARNINGS
# windows.h
WIN32_LEAN_AND_MEAN
NOGPICAPMASKS
NOVIRTUALKEYCODES
NOWINMESSAGES
NOWINSTYLES
NOSYSMETRICS
NOMENUS
NOICONS
NOKEYSTATES
NOSYSCOMMANDS
NORASTEROPS
NOSHOWWINDOW
NOATOM
NOCLIPBOARD
NOCOLOR
NOCTLMGR
NODRAWTEXT
#NOGDI
NOKERNEL
#NOUSER
NONLS
NOMB
NOMEMMGR
NOMETAFILE
NOMINMAX
#NOMSG
NOOPENFILE
NOSCROLL
NOSERVICE
NOSOUND
NOTEXTMETRIC
NOWH
NOWINOFFSETS
NOCOMM
NOKANJI
NOHELP
NOPROFILER
NODEFERWINDOWPOS
NOMCX
NOIME
NOMDI
NOINOUT
)
endif()
+288
View File
@@ -0,0 +1,288 @@
#include "std.hpp"
#include "animation.hpp"
struct Animation::Rep{
int ref_cnt;
typedef map<int,Quat> KeyList;
KeyList scale_anim,rot_anim,pos_anim;
Rep():
ref_cnt(1){
}
Rep( const Rep &t ):
ref_cnt(1),
scale_anim(t.scale_anim),rot_anim(t.rot_anim),pos_anim(t.pos_anim){
}
Vector getLinearValue( const KeyList &keys,float time )const{
KeyList::const_iterator next,curr;
//for( next=keys.begin();next!=keys.end() && time>=next->first;++next ){}
next=keys.upper_bound( (int)time );
if( next==keys.begin() ) return next->second.v;
curr=next;--curr;
if( next==keys.end() ) return curr->second.v;
float delta=( time-curr->first )/( next->first-curr->first );
return ( next->second.v-curr->second.v )*delta+curr->second.v;
}
Quat getSlerpValue( const KeyList &keys,float time )const{
KeyList::const_iterator next,curr;
//for( next=keys.begin();next!=keys.end() && time>=next->first;++next ){}
next=keys.upper_bound( (int)time );
if( next==keys.begin() ) return next->second;
curr=next;--curr;
if( next==keys.end() ) return curr->second;
float delta=( time-curr->first )/( next->first-curr->first );
return curr->second.slerpTo( next->second,delta );
}
void setKey( KeyList &keys,int time,const Quat &value ){
keys[time]=value;
}
};
Animation::Animation():
rep( new Rep() ){
}
Animation::Animation( const Animation &t ):
rep( t.rep ){
++rep->ref_cnt;
}
Animation::Animation( const Animation &t,int first,int last ):
rep( new Rep() ){
Rep::KeyList::const_iterator it;
for( it=t.rep->pos_anim.begin();it!=t.rep->pos_anim.end();++it ){
if( it->first<first || it->first>last ) continue;
rep->setKey( rep->pos_anim,it->first-first,it->second );
}
for( it=t.rep->scale_anim.begin();it!=t.rep->scale_anim.end();++it ){
if( it->first<first || it->first>last ) continue;
rep->setKey( rep->scale_anim,it->first-first,it->second );
}
for( it=t.rep->rot_anim.begin();it!=t.rep->rot_anim.end();++it ){
if( it->first<first || it->first>last ) continue;
rep->setKey( rep->rot_anim,it->first-first,it->second );
}
}
Animation::~Animation(){
if( !--rep->ref_cnt ) delete rep;
}
Animation &Animation::operator=( const Animation &t ){
++t.rep->ref_cnt;
if( !--rep->ref_cnt ) delete rep;
rep=t.rep;
return *this;
}
Animation::Rep *Animation::write(){
if( rep->ref_cnt>1 ){
--rep->ref_cnt;
rep=new Rep( *rep );
}
return rep;
}
void Animation::setScaleKey( int time,const Vector &q ){
write();
rep->setKey( rep->scale_anim,time,Quat( 0,q ) );
}
void Animation::setPositionKey( int time,const Vector &q ){
write();
rep->setKey( rep->pos_anim,time,Quat( 0,q ) );
}
void Animation::setRotationKey( int time,const Quat &q ){
write();
rep->setKey( rep->rot_anim,time,q );
}
int Animation::numScaleKeys()const{
return rep->scale_anim.size();
}
int Animation::numRotationKeys()const{
return rep->rot_anim.size();
}
int Animation::numPositionKeys()const{
return rep->pos_anim.size();
}
Vector Animation::getScale( float time )const{
if( !rep->scale_anim.size() ) return Vector(1,1,1);
return rep->getLinearValue( rep->scale_anim,time );
}
Vector Animation::getPosition( float time )const{
if( !rep->pos_anim.size() ) return Vector(0,0,0);
return rep->getLinearValue( rep->pos_anim,time );
}
Quat Animation::getRotation( float time )const{
if( !rep->rot_anim.size() ) return Quat();
return rep->getSlerpValue( rep->rot_anim,time );
}
/*
struct Animation::Rep{
int ref_cnt;
struct Key{
int time;
Quat value;
};
typedef list<Key> KeyList;
KeyList scale_anim,rot_anim,pos_anim;
Rep():
ref_cnt(1){
}
Rep( const Rep &t ):
ref_cnt(1),
scale_anim(t.scale_anim),rot_anim(t.rot_anim),pos_anim(t.pos_anim){
}
Vector getLinearValue( const KeyList &keys,float time )const{
if( keys.size()==1 ) return keys.front().value.v;
if( time>=keys.back().time ) return keys.back().value.v;
if( time<=keys.front().time ) return keys.front().value.v;
KeyList::const_iterator it;
for( it=keys.begin();time>=it->time;++it ){}
const Key *next=&*it;
const Key *curr=&*--it;
float delta=( time-curr->time )/( next->time-curr->time );
return (next->value.v-curr->value.v)*delta+curr->value.v;
}
Quat getSlerpValue( const KeyList &keys,float time )const{
if( keys.size()==1 ) return keys.front().value;
if( time>=keys.back().time ) return keys.back().value;
if( time<=keys.front().time ) return keys.front().value;
KeyList::const_iterator it;
for( it=keys.begin();time>=it->time;++it ){}
const Key *next=&*it;
const Key *curr=&*--it;
float delta=(time-curr->time)/(next->time-curr->time);
return curr->value.slerpTo( next->value,delta );
}
void setKey( KeyList &keys,int time,const Quat &value ){
KeyList::iterator it;
for( it=keys.begin();it!=keys.end() && time>it->time;++it ){}
if( it==keys.end() || time<it->time ){
it=keys.insert( it );
it->time=time;
}
it->value=value;
}
};
Animation::Animation():
rep( new Rep() ){
}
Animation::Animation( const Animation &t ):
rep( t.rep ){
++rep->ref_cnt;
}
Animation::Animation( const Animation &t,int first,int last ):
rep( new Rep() ){
Rep::KeyList::const_iterator it;
for( it=t.rep->pos_anim.begin();it!=t.rep->pos_anim.end();++it ){
const Rep::Key &key=*it;
if( key.time<first || key.time>last ) continue;
rep->setKey( rep->pos_anim,key.time-first,key.value );
}
for( it=t.rep->scale_anim.begin();it!=t.rep->scale_anim.end();++it ){
const Rep::Key &key=*it;
if( key.time<first || key.time>last ) continue;
rep->setKey( rep->scale_anim,key.time-first,key.value );
}
for( it=t.rep->rot_anim.begin();it!=t.rep->rot_anim.end();++it ){
const Rep::Key &key=*it;
if( key.time<first || key.time>last ) continue;
rep->setKey( rep->rot_anim,key.time-first,key.value );
}
}
Animation::~Animation(){
if( !--rep->ref_cnt ) delete rep;
}
Animation &Animation::operator=( const Animation &t ){
++t.rep->ref_cnt;
if( !--rep->ref_cnt ) delete rep;
rep=t.rep;
return *this;
}
Animation::Rep *Animation::write(){
if( rep->ref_cnt>1 ){
--rep->ref_cnt;
rep=new Rep( *rep );
}
return rep;
}
void Animation::setScaleKey( int time,const Vector &q ){
write();
rep->setKey( rep->scale_anim,time,Quat( 0,q ) );
}
void Animation::setPositionKey( int time,const Vector &q ){
write();
rep->setKey( rep->pos_anim,time,Quat( 0,q ) );
}
void Animation::setRotationKey( int time,const Quat &q ){
write();
rep->setKey( rep->rot_anim,time,q );
}
int Animation::numScaleKeys()const{
return rep->scale_anim.size();
}
int Animation::numRotationKeys()const{
return rep->rot_anim.size();
}
int Animation::numPositionKeys()const{
return rep->pos_anim.size();
}
Vector Animation::getScale( float time )const{
if( !rep->scale_anim.size() ) return Vector(1,1,1);
return rep->getLinearValue( rep->scale_anim,time );
}
Vector Animation::getPosition( float time )const{
if( !rep->pos_anim.size() ) return Vector(0,0,0);
return rep->getLinearValue( rep->pos_anim,time );
}
Quat Animation::getRotation( float time )const{
if( !rep->rot_anim.size() ) return Quat();
return rep->getSlerpValue( rep->rot_anim,time );
}
*/
+37
View File
@@ -0,0 +1,37 @@
#ifndef ANIMATION_H
#define ANIMATION_H
#include <list>
#include "geom.hpp"
class Animation{
public:
Animation();
Animation( const Animation &t );
Animation( const Animation &t,int first,int last );
~Animation();
Animation &operator=( const Animation &t );
void setScaleKey( int frame,const Vector &q );
void setPositionKey( int frame,const Vector &p );
void setRotationKey( int frame,const Quat &q );
int numScaleKeys()const;
int numRotationKeys()const;
int numPositionKeys()const;
Vector getScale( float time )const;
Vector getPosition( float time )const;
Quat getRotation( float time )const;
private:
struct Rep;
Rep *rep;
Rep *write();
};
#endif
+218
View File
@@ -0,0 +1,218 @@
#include "std.hpp"
#include "animator.hpp"
#include "object.hpp"
Animator::Animator( Animator *t ):_seqs( t->_seqs ){
_objs.resize( t->_objs.size() );
_anims.resize( t->_anims.size() );
for( int k=0;k<t->_objs.size();++k ){
_objs[k]=t->_objs[k]->getLastCopy();
_anims[k].keys=t->_anims[k].keys;
}
reset();
}
Animator::Animator( Object *obj,int frames ){
addObjs( obj );
_anims.resize( _objs.size() );
addSeq( frames );
reset();
}
Animator::Animator( const vector<Object*> &objs,int frames ):_objs(objs){
_anims.resize( _objs.size() );
addSeq( frames );
reset();
}
void Animator::reset(){
_seq=_mode=_seq_len=_time=_speed=_trans_time=_trans_speed=0;
}
void Animator::addObjs( Object *obj ){
_objs.push_back( obj );
for( Entity *e=obj->GetChildren();e;e=e->GetSuccessor() ){
addObjs( e->getObject() );
}
}
void Animator::addSeq( int frames ){
Seq seq;
seq.frames=frames;
_seqs.push_back( seq );
for( int k=0;k<_objs.size();++k ){
Object *obj=_objs[k];
_anims[k].keys.push_back( obj->getAnimation() );
obj->setAnimation( Animation() );
}
}
void Animator::addSeqs( Animator *t ){
for( int n=0;n<t->_seqs.size();++n ){
_seqs.push_back( t->_seqs[n] );
for( int k=0;k<_objs.size();++k ){
int j;
for( j=0;j<t->_objs.size();++j ){
if( _objs[k]->getName()==t->_objs[j]->getName() ) break;
}
if( j==t->_objs.size() ){
_anims[k].keys.push_back( Animation() );
continue;
}
_anims[k].keys.push_back( t->_anims[j].keys[n] );
}
}
}
void Animator::extractSeq( int first,int last,int seq ){
Seq sq;
sq.frames=last-first;
_seqs.push_back( sq );
for( int k=0;k<_objs.size();++k ){
Animation &keys=_anims[k].keys[seq];
_anims[k].keys.push_back( Animation( keys,first,last ) );
}
}
void Animator::updateAnim(){
for( int k=0;k<_objs.size();++k ){
Object *obj=_objs[k];
const Animation &keys=_anims[k].keys[_seq];
if( keys.numPositionKeys() ){
obj->SetLocalPosition( keys.getPosition( _time ) );
}
if( keys.numScaleKeys() ){
obj->SetLocalScale( keys.getScale( _time ) );
}
if( keys.numRotationKeys() ){
obj->SetLocalRotation( keys.getRotation( _time ) );
}
}
}
void Animator::updateTrans(){
for( int k=0;k<_objs.size();++k ){
Object *obj=_objs[k];
const Anim &anim=_anims[k];
if( anim.pos ) obj->SetLocalPosition( (anim.dest_pos-anim.src_pos)*_trans_time+anim.src_pos );
if( anim.scl ) obj->SetLocalScale( (anim.dest_scl-anim.src_scl)*_trans_time+anim.src_scl );
if( anim.rot ) obj->SetLocalRotation( anim.src_rot.slerpTo( anim.dest_rot,_trans_time ) );
}
}
void Animator::beginTrans(){
for( int k=0;k<_objs.size();++k ){
Object *obj=_objs[k];
Anim &anim=_anims[k];
const Animation &keys=_anims[k].keys[_seq];
if( anim.pos=!!keys.numPositionKeys() ){
anim.src_pos=obj->GetLocalPosition();
anim.dest_pos=keys.getPosition( _time );
}
if( anim.scl=!!keys.numScaleKeys() ){
anim.src_scl=obj->GetLocalScale();
anim.dest_scl=keys.getScale( _time );
}
if( anim.rot=!!keys.numRotationKeys() ){
anim.src_rot=obj->GetLocalRotation();
anim.dest_rot=keys.getRotation( _time );
}
}
}
void Animator::setAnimTime( float time,int seq ){
if( seq<0 || seq>_seqs.size() ) return;
_mode=0;
_speed=0;
_seq=seq;
_seq_len=_seqs[_seq].frames;
//Ok, mod the anim time!
_time=fmod( time,_seq_len );
//if( time<0 || time>_seq_len ) time=fmod( time,_seq_len );
//_time=time;
if( _time<0 ) _time+=+_seq_len;
updateAnim();
}
void Animator::animate( int mode,float speed,int seq,float trans ){
if( !mode && !speed ){ _mode=0;return; }
if( seq<0 || seq>=_seqs.size() ) return;
_seq=seq;
_mode=mode;
_seq_len=_seqs[_seq].frames;
_speed=speed;
_time=_speed>=0 ? 0 : _seq_len;
if( trans<=0 ){
updateAnim();
if( !_speed ) _mode=0;
return;
}
_mode|=0x8000;
_trans_time=0;
_trans_speed=1/trans;
beginTrans();
}
void Animator::update( float elapsed ){
if( !_mode ) return;
if( _mode&0x8000 ){
_trans_time+=_trans_speed*elapsed;
if( _trans_time<1 ){
updateTrans();
return;
}
_mode&=0x7fff;
if( !_mode || !_speed ){
updateAnim();
_mode=0;
return;
}
}
//do anim...
_time+=_speed*elapsed;
switch( _mode ){
case ANIM_MODE_LOOP:
_time=fmod( _time,_seq_len );
if( _time<0 ) _time+=_seq_len;
break;
case ANIM_MODE_PINGPONG:
_time=fmod( _time,_seq_len*2 );
if( _time<0 ) _time+=_seq_len*2;
if( _time>=_seq_len ){ _time=_seq_len-(_time-_seq_len);_speed=-_speed; }
break;
case ANIM_MODE_ONESHOT:
if( _time<0 ){ _time=0;_mode=0; }
else if( _time>=_seq_len ){ _time=_seq_len;_mode=0; }
break;
}
updateAnim();
}
+74
View File
@@ -0,0 +1,74 @@
#ifndef ANIMATOR_H
#define ANIMATOR_H
#include "animation.hpp"
class Object;
class Animator{
public:
enum{
ANIM_MODE_LOOP=1,
ANIM_MODE_PINGPONG=2,
ANIM_MODE_ONESHOT=3
};
Animator( Animator *animator );
Animator( Object *tree,int frames );
Animator( const vector<Object*> &objs,int frames );
void addSeq( int frames );
void addSeqs( Animator *t );
void extractSeq( int first,int last,int seq );
void setAnimTime( float time,int seq );
void animate( int mode,float speed,int seq,float trans );
void update( float elapsed );
int animSeq()const{ return _seq; }
int animLen()const{ return _seq_len; }
float animTime()const{ return _time; }
bool animating()const{ return !!_mode; }
int numSeqs()const{ return _seqs.size(); }
const vector<Object*> &getObjects()const{ return _objs; }
private:
struct Seq{
int frames;
};
struct Anim{
//anim keys
vector<Animation> keys;
//for transitions...
bool pos,scl,rot;
Vector src_pos,dest_pos;
Vector src_scl,dest_scl;
Quat src_rot,dest_rot;
Anim():pos(false),scl(false),rot(false){}
};
vector<Seq> _seqs;
vector<Anim> _anims;
vector<Object*> _objs;
int _seq,_mode,_seq_len;
float _time,_speed,_trans_time,_trans_speed;
void reset();
void addObjs( Object *obj );
void updateAnim();
void beginTrans();
void updateTrans();
};
#endif
+27
View File
@@ -0,0 +1,27 @@
#include "bd2model.hpp"
struct BD2Vert{
unsigned char x,y,z,n,u,v;
};
struct BD2Tri{
unsigned short v0,v1,v2;
};
struct BD2Frame{
float x_scale,y_scale,z_scale;
float x_offset,y_offset,z_offset;
BD2Vert verts[1];
};
struct BD2File{
int id; //'BD2F'
float u_scale,v_scale;
int n_verts,n_tris,n_frames;
BD2Tri tris[1];
BD2Frame frames[1];
};
struct BD2Model::Rep{
};
+13
View File
@@ -0,0 +1,13 @@
#ifndef BD2MODEL_H
#define BD2MODEL_H
class BD2Model : public Model{
public:
private:
struct Rep;
Rep *rep;
};
#endif
+5
View File
@@ -0,0 +1,5 @@
#ifndef BLITZ3D_H
#define BLITZ3D_H
#endif
+202
View File
@@ -0,0 +1,202 @@
#include "std.hpp"
#include "world.hpp"
#include <cfloat>
static World *w;
struct Face{
Vector verts[4];
Face( const Vector &v0,const Vector &v1,const Vector &v2,const Vector &v3 ){
verts[0]=v0;
verts[1]=v1;
verts[2]=v2;
verts[3]=v3;
}
};
static int face_verts[][4]={
2,3,1,0,
3,7,5,1,
7,6,4,5,
6,2,0,4,
6,7,3,2,
0,1,5,4
};
struct Coll{
int obj,surf,tri;
Coll( const ObjCollision &t ):obj((int)t.with),surf((int)t.collision.surface),tri(t.collision.index){
}
};
struct CollCmp{
bool operator()( const Coll &a,const Coll &b )const{
if( a.obj<b.obj ) return true;
if( b.obj<a.obj ) return false;
if( a.surf<b.surf ) return true;
if( b.surf<a.surf ) return false;
if( a.tri<b.tri ) return true;
return false;
}
};
typedef set<Coll,CollCmp> CollSet;
//returns: 1 for visible, 0 for hidden, -1 for don't know
static int faceVis( const Face &src,const Face &dest ){
static CollSet all;
static CollSet colls[16];
all.clear();
for( int k=0;k<4;++k ){
for( int j=0;j<4;++j ){
int n=k*4+j;
colls[n].clear();
Vector sv=src.verts[k];
Vector dv=dest.verts[j];
Vector adj=(dv-sv).normalized()*.01f;
dv-=adj;
for(;;){
sv+=adj;
Line line( sv,dv-sv );
ObjCollision c;
if( !w->traceRay( line,EPSILON,&c ) ) break;
Coll t( c );
all.insert( t );
colls[n].insert( t );
sv=c.coords;
}
if( !colls[n].size() ) return 1;
}
}
CollSet::const_iterator it;
for( it=all.begin();it!=all.end();++it ){
int k=0;
for( ;k<16;++k ){
if( !colls[k].count( *it ) ) break;
}
if( k==16 ) return 0; //definitely hidden!
}
return -1;
}
static void subdivide( list<Face> &lst ){
int n=lst.size();
while( n-- ){
const Face &f=lst.front();
Vector a( (f.verts[0]+f.verts[1])/2 );
Vector b( (f.verts[1]+f.verts[2])/2 );
Vector c( (f.verts[2]+f.verts[3])/2 );
Vector d( (f.verts[3]+f.verts[0])/2 );
Vector e( (f.verts[0]+f.verts[1]+f.verts[2]+f.verts[3])/4 );
lst.push_back( Face( f.verts[0],a,e,d ) );
lst.push_back( Face( a,f.verts[1],b,e ) );
lst.push_back( Face( e,b,f.verts[2],c ) );
lst.push_back( Face( d,e,c,f.verts[3] ) );
lst.erase( lst.begin() );
}
}
static int faceVis( const Face &src,const Face &dest,int recurs_limit ){
static list<Face> src_faces,dest_faces;
src_faces.clear();
dest_faces.clear();
src_faces.push_back( src );
dest_faces.push_back( dest );
while( recurs_limit-- ){
list<Face>::iterator src_it,dest_it;
for( src_it=src_faces.begin();src_it!=src_faces.end();++src_it ){
int cnt=0;
for( dest_it=dest_faces.begin();dest_it!=dest_faces.end();++dest_it ){
int n=faceVis( *src_it,*dest_it );
if( n==1 ) return 1;
if( !n ) ++cnt;
}
if( cnt==dest_faces.size() ){
//source can't see ANY dest faces
src_it=src_faces.erase( src_it );
--src_it;
}
}
if( !src_faces.size() ) return 0;
//ok, subdivide!
subdivide( src_faces );
subdivide( dest_faces );
}
return -1;
}
bool World::boxVis( const Box &src,const Box &dest,int recurs_limit ){
w=this;
Box big;
big.update( src );
big.update( dest );
Plane planes[6];
for( int n=0;n<6;++n ){
planes[n]=Plane(
big.corner( face_verts[n][0] ),
big.corner( face_verts[n][1] ),
big.corner( face_verts[n][2] ));
}
for( int k=0;k<6;++k ){
Vector v0=src.corner( face_verts[k][0] );
Vector v1=src.corner( face_verts[k][1] );
Vector v2=src.corner( face_verts[k][2] );
Vector v3=src.corner( face_verts[k][3] );
int n;
for( n=0;n<6;++n ){
const Plane &p=planes[n];
if( fabs(p.distance(v0))<=EPSILON &&
fabs(p.distance(v1))<=EPSILON &&
fabs(p.distance(v2))<=EPSILON &&
fabs(p.distance(v3))<=EPSILON ) break;
}
if( n<6 ) continue;
Face src_face( v0,v1,v2,v3 );
for( int j=0;j<6;++j ){
Vector v0=dest.corner( face_verts[j][0] );
Vector v1=dest.corner( face_verts[j][1] );
Vector v2=dest.corner( face_verts[j][2] );
Vector v3=dest.corner( face_verts[j][3] );
int n;
for( n=0;n<6;++n ){
const Plane &p=planes[n];
if( fabs(p.distance(v0))<=EPSILON &&
fabs(p.distance(v1))<=EPSILON &&
fabs(p.distance(v2))<=EPSILON &&
fabs(p.distance(v3))<=EPSILON ) break;
}
if( n<6 ) continue;
Face dest_face( v0,v1,v2,v3 );
int t=faceVis( src_face,dest_face,recurs_limit );
if( t ) return true;
}
}
return false;
}
+211
View File
@@ -0,0 +1,211 @@
#include "std.hpp"
#include "brush.hpp"
#include "../gxruntime/gxgraphics.hpp"
struct Brush::Rep{
union{ int ref_cnt;Rep *next; };
int blend,max_tex;
bool blend_valid;
gxScene::RenderState rs;
Texture texs[gxScene::MAX_TEXTURES];
static Rep *pool;
Rep():
ref_cnt(1),blend(0),max_tex(0),blend_valid(true){
memset( &rs,0,sizeof(rs) );
rs.blend=gxScene::BLEND_REPLACE;
rs.color[0]=rs.color[1]=rs.color[2]=rs.alpha=1;
}
Rep( const Rep &t ):
ref_cnt(1),blend(t.blend),max_tex(t.max_tex),rs(t.rs),blend_valid(t.blend_valid){
for( int k=0;k<max_tex;++k ) texs[k]=t.texs[k];
}
void *operator new( size_t sz ){
static const int GROW=64;
if( !pool ){
pool=new Rep[GROW];
for( int k=0;k<GROW-1;++k ) pool[k].next=&pool[k+1];
pool[GROW-1].next=0;
}
Rep *p=pool;
pool=p->next;
return p;
}
void operator delete( void *q ){
Rep *p=(Rep*)q;
p->next=pool;
pool=p;
}
};
Brush::Rep *Brush::Rep::pool;
Brush::Brush():
rep( new Rep() ){
}
Brush::Brush( const Brush &t ):
rep( t.rep ){
++rep->ref_cnt;
}
Brush::Brush( const Brush &a,const Brush &b ):
rep( new Rep( *a.rep ) ){
*(Vector*)rep->rs.color*=*(Vector*)b.rep->rs.color;
rep->rs.alpha*=b.rep->rs.alpha;
rep->rs.shininess+=b.rep->rs.shininess;
if( b.rep->blend ) rep->blend=b.rep->blend;
rep->rs.fx|=b.rep->rs.fx;
if( b.rep->max_tex>rep->max_tex ) rep->max_tex=b.rep->max_tex;
for( int k=0;k<rep->max_tex;++k ){
if( b.rep->rs.tex_states[k].canvas ){
rep->rs.tex_states[k].canvas=b.rep->rs.tex_states[k].canvas;
rep->texs[k]=b.rep->texs[k];
}
}
rep->blend_valid=false;
}
Brush::~Brush(){
if( !--rep->ref_cnt ) delete rep;
}
Brush &Brush::operator=( const Brush &t ){
++t.rep->ref_cnt;
if( !--rep->ref_cnt ) delete rep;
rep=t.rep;return *this;
}
Brush::Rep *Brush::write()const{
if( rep->ref_cnt>1 ){
--rep->ref_cnt;
rep=new Rep( *rep );
}
return rep;
}
void Brush::setColor( const Vector &color ){
*(Vector*)write()->rs.color=color;
}
void Brush::setAlpha( float alpha ){
float a=rep->rs.alpha;
write()->rs.alpha=alpha;
if( (a<1)!=(alpha<1) ) rep->blend_valid=false;
}
void Brush::setShininess( float n ){
write()->rs.shininess=n;
}
void Brush::setBlend( int blend ){
write()->blend=blend;
rep->blend_valid=false;
}
void Brush::setFX( int fx ){
write()->rs.fx=fx;
rep->blend_valid=false;
}
void Brush::setTexture( int index,const Texture &t,int n ){
write();
gxScene::RenderState &rs=rep->rs;
rep->texs[index]=t;
rs.tex_states[index].canvas=t.getCanvas( n );
rep->max_tex=0;
for( int k=0;k<gxScene::MAX_TEXTURES;++k ){
if( rs.tex_states[k].canvas ) rep->max_tex=k+1;
}
rep->blend_valid=false;
}
const Vector &Brush::getColor()const{
return *(Vector*)rep->rs.color;
}
float Brush::getAlpha()const{
return rep->rs.alpha;
}
float Brush::getShininess()const{
return rep->rs.shininess;
}
int Brush::getBlend()const{
if( rep->blend_valid ) return rep->rs.blend;
rep->blend_valid=true; //well, it will be...
gxScene::RenderState &rs=rep->rs;
//alphatest
if( rep->texs[0].getCanvasFlags() & gxCanvas::CANVAS_TEX_MASK ){
rs.fx|=gxScene::FX_ALPHATEST;
}else{
rs.fx&=~gxScene::FX_ALPHATEST;
}
//0 = default/replace
//1 = alpha
//2 = multiply
//3 = add
if( rep->blend ){
if( rep->blend!=gxScene::BLEND_ALPHA ){
return rs.blend=rep->blend;
}
for( int k=0;k<rep->max_tex;++k ){
if( rep->texs[k].isTransparent() ){
return rs.blend=gxScene::BLEND_ALPHA;
}
}
}else if( rep->max_tex==1 && rep->texs[0].isTransparent() ){
//single transparent texture?
return rs.blend=gxScene::BLEND_ALPHA;
}
//vertex alpha or entityalpha?
if( (rs.fx&gxScene::FX_VERTEXALPHA) || rs.alpha<1 ){
return rs.blend=gxScene::BLEND_ALPHA;
}
return rs.blend=gxScene::BLEND_REPLACE;
}
int Brush::getFX()const{
return rep->rs.fx;
}
Texture Brush::getTexture( int index )const{
return rep->texs[index];
}
const gxScene::RenderState &Brush::getRenderState()const{
getBlend();
for( int k=0;k<rep->max_tex;++k ){
gxScene::RenderState::TexState *ts=&rep->rs.tex_states[k];
ts->matrix=rep->texs[k].getMatrix();
ts->blend=rep->texs[k].getBlend();
ts->flags=rep->texs[k].getFlags();
}
return rep->rs;
}
bool Brush::operator<( const Brush &t )const{
return memcmp( &getRenderState(),&t.getRenderState(),sizeof(gxScene::RenderState) )<0;
}
+42
View File
@@ -0,0 +1,42 @@
#ifndef BRUSH_H
#define BRUSH_H
#include "geom.hpp"
#include "texture.hpp"
class Brush{
public:
Brush();
Brush( const Brush &t );
Brush( const Brush &a,const Brush &b );
~Brush();
Brush &operator=( const Brush &t );
void setColor( const Vector &color );
void setAlpha( float alpha );
void setShininess( float shininess );
void setBlend( int blend );
void setFX( int fx );
void setTexture( int index,const Texture &t,int frame );
const Vector &getColor()const;
float getAlpha()const;
float getShininess()const;
int getBlend()const;
int getFX()const;
Texture getTexture( int index )const;
const gxScene::RenderState &getRenderState()const;
bool operator<( const Brush &b )const;
private:
struct Rep;
mutable Rep *rep;
Rep *write()const;
};
#endif
+165
View File
@@ -0,0 +1,165 @@
#include "std.hpp"
#include "cachedtexture.hpp"
int active_texs;
extern gxRuntime *gx_runtime;
extern gxGraphics *gx_graphics;
set<CachedTextureFactory::CachedTexture*> CachedTextureFactory::rep_set;
static string path;
struct CachedTextureFactory::CachedTexture{
int ref_cnt;
string file;
int flags,w,h,first;
vector<gxCanvas*> frames;
CachedTexture( int w,int h,int flags,int cnt ):
ref_cnt(1),flags(flags),w(w),h(h),first(0){
++active_texs;
while( cnt-->0 ){
if( gxCanvas *t=gx_graphics->createCanvas( w,h,flags ) ){
frames.push_back( t );
}else break;
}
}
CachedTexture( const string &f,int flags,int w,int h,int first,int cnt ):
ref_cnt(1),file(f),flags(flags),w(w),h(h),first(first){
++active_texs;
if( !(flags & gxCanvas::CANVAS_TEX_CUBE) ){
if( w<=0 || h<=0 || first<0 || cnt<=0 ){
w=h=first=0;
if( gxCanvas *t=gx_graphics->loadCanvas( f,flags ) ){
frames.push_back( t );
}
return;
}
}
int t_flags=flags & (
gxCanvas::CANVAS_TEX_RGB|
gxCanvas::CANVAS_TEX_ALPHA|
gxCanvas::CANVAS_TEX_MASK|
gxCanvas::CANVAS_TEX_HICOLOR ) | gxCanvas::CANVAS_NONDISPLAY;
gxCanvas *t=gx_graphics->loadCanvas( f,t_flags );
if( !t ) return;
if( !t->getDepth() ){
gx_graphics->freeCanvas( t );
return;
}
if( flags & gxCanvas::CANVAS_TEX_CUBE ){
int w=t->getWidth()/6;
if( w*6!=t->getWidth() ) return;
int h=t->getHeight();
gxCanvas *tex=gx_graphics->createCanvas( w,h,flags );
if( tex ){
frames.push_back( tex );
for( int face=0;face<6;++face ){
tex->setCubeFace(face);
gx_graphics->copy( tex,0,0,tex->getWidth(),tex->getHeight(),t,face*w,0,w,h );
}
tex->setCubeFace(1);
}
}else{
int x_tiles=t->getWidth()/w;
int y_tiles=t->getHeight()/h;
if( first+cnt>x_tiles*y_tiles ){
gx_graphics->freeCanvas( t );
return;
}
int x=(first%x_tiles)*w;
int y=(first/x_tiles)*h;
while( cnt-- ){
gxCanvas *p=gx_graphics->createCanvas( w,h,flags );
gx_graphics->copy( p,0,0,p->getWidth(),p->getHeight(),t,x,y,w,h );
frames.push_back(p);
x=x+w;if( x+w>t->getWidth() ){ x=0;y=y+h; }
}
}
gx_graphics->freeCanvas( t );
}
~CachedTexture(){
--active_texs;
for( int k=0;k<frames.size();++k ) gx_graphics->freeCanvas( frames[k] );
}
};
CachedTextureFactory::CachedTexture *CachedTextureFactory::findRep( const string &f,int flags,int w,int h,int first,int cnt ){
set<CachedTexture*>::const_iterator it;
for( it=rep_set.begin();it!=rep_set.end();++it ){
CachedTexture *rep=*it;
if( rep->file==f && rep->flags==flags && rep->w==w && rep->h==h && rep->first==first && rep->frames.size()==cnt ){
++rep->ref_cnt;return rep;
}
}
return 0;
}
CachedTextureFactory::CachedTextureFactory( int w,int h,int flags,int cnt ):
rep(new CachedTexture(w,h,flags,cnt)){
}
CachedTextureFactory::CachedTextureFactory( const string &f_,int flags,int w,int h,int first,int cnt ){
string f=f_;
if( f.substr(0,2)==".\\" ) f=f.substr(2);
if( path.size() ){
string t=path+tolower( filenamefile( f ) );
if( rep=findRep( t,flags,w,h,first,cnt ) ) return;
rep=new CachedTexture( t,flags,w,h,first,cnt );
if( rep->frames.size() ){
rep_set.insert( rep );
return;
}
delete rep;
}
string t=tolower( fullfilename( f ) );
if( rep=findRep( t,flags,w,h,first,cnt ) ) return;
rep=new CachedTexture( t,flags,w,h,first,cnt );
rep_set.insert( rep );
}
CachedTextureFactory::CachedTextureFactory( const CachedTextureFactory &t ):
rep(t.rep){
++rep->ref_cnt;
}
CachedTextureFactory::~CachedTextureFactory(){
if( !--rep->ref_cnt ){
rep_set.erase( rep );
delete rep;
}
}
CachedTextureFactory &CachedTextureFactory::operator=( const CachedTextureFactory &t ){
++t.rep->ref_cnt;
if( !--rep->ref_cnt ){
rep_set.erase( rep );
delete rep;
}
rep=t.rep;
return *this;
}
string CachedTextureFactory::getName()const{
return rep->file;
}
const vector<gxCanvas*> &CachedTextureFactory::getFrames()const{
return rep->frames;
}
void CachedTextureFactory::setPath( const string &t ){
path=tolower(t);
if( int sz=path.size() ){
if( path[sz-1]!='/' && path[sz-1]!='\\' ) path+='\\';
}
}
+33
View File
@@ -0,0 +1,33 @@
#ifndef CACHEDTEXTURE_H
#define CACHEDTEXTURE_H
#include "../gxruntime/gxcanvas.hpp"
class CachedTextureFactory{
public:
CachedTextureFactory( int w,int h,int flags,int cnt );
CachedTextureFactory( const string &f,int flags,int w,int h,int first,int cnt );
CachedTextureFactory( const CachedTextureFactory &t );
~CachedTextureFactory();
CachedTextureFactory &operator=( const CachedTextureFactory &t );
string getName()const;
const vector<gxCanvas*> &getFrames()const;
bool operator<( const CachedTextureFactory &t )const{ return rep<t.rep; }
static void setPath( const string &t );
private:
struct CachedTexture;
CachedTexture *rep;
CachedTexture *findRep( const string &f,int flags,int w,int h,int first,int cnt );
static set<CachedTexture*> rep_set;
};
#endif
+103
View File
@@ -0,0 +1,103 @@
#include "std.hpp"
#include "camera.hpp"
extern gxScene *gx_scene;
Camera::Camera(){
setZoom( 1 );
setRange( 1,1000 );
setViewport( 0,0,0,0 );
setClsColor( Vector() );
setClsMode( true,true );
setProjMode( PROJ_PERSP );
setFogRange( 1,1000 );
setFogColor( Vector() );
setFogMode( gxScene::FOG_NONE );
}
void Camera::setZoom( float z ){
zoom=z;
local_valid=false;
}
void Camera::setRange( float n,float f ){
frustum_nr=n;frustum_fr=f;
local_valid=false;
}
void Camera::setViewport( int x,int y,int w,int h ){
vp_x=x;vp_y=y;vp_w=w;vp_h=h;
local_valid=false;
}
void Camera::setClsColor( const Vector &v ){
cls_color=v;
}
void Camera::setClsMode( bool c,bool z ){
cls_argb=c;cls_z=z;
}
void Camera::setProjMode( int mode ){
proj_mode=mode;
}
void Camera::setFogColor( const Vector &v ){
fog_color=v;
}
void Camera::setFogRange( float nr,float fr ){
fog_nr=nr;fog_fr=fr;
}
void Camera::setFogMode( int mode ){
fog_mode=mode;
}
const Frustum &Camera::getFrustum()const{
if( !local_valid ){
float ar=(float)vp_h/vp_w;
frustum_w=frustum_nr*2/zoom;
frustum_h=frustum_nr*2/zoom*ar;
new( &local_frustum ) Frustum( frustum_nr,frustum_fr,frustum_w,frustum_h );
local_valid=true;
}
return local_frustum;
}
float Camera::getFrustumNear()const{
return frustum_nr;
}
float Camera::getFrustumFar()const{
return frustum_fr;
}
float Camera::getFrustumWidth()const{
getFrustum();return frustum_w;
}
float Camera::getFrustumHeight()const{
getFrustum();return frustum_h;
}
void Camera::getViewport( int *x,int *y,int *w,int *h )const{
*x=vp_x;*y=vp_y;*w=vp_w;*h=vp_h;
}
bool Camera::beginRenderFrame(){
if( !proj_mode ) return false;
getFrustum();
gx_scene->setViewport( vp_x,vp_y,vp_w,vp_h );
gx_scene->clear( &(cls_color.x),1,1,cls_argb,cls_z );
if( proj_mode==PROJ_ORTHO ){
gx_scene->setOrthoProj( frustum_nr,frustum_fr,frustum_w,frustum_h );
}else{
gx_scene->setPerspProj( frustum_nr,frustum_fr,frustum_w,frustum_h );
}
gx_scene->setFogRange( fog_nr,fog_fr );
gx_scene->setFogColor( (float*)&fog_color.x );
gx_scene->setFogMode( fog_mode );
return true;
}
+56
View File
@@ -0,0 +1,56 @@
#ifndef CAMERA_H
#define CAMERA_H
#include "model.hpp"
#include "frustum.hpp"
#include "mirror.hpp"
class Camera : public Object{
public:
enum{
PROJ_NONE=0,PROJ_PERSP=1,PROJ_ORTHO=2
};
Camera();
Camera *getCamera(){ return this; }
//called by user
void setZoom( float z );
void setRange( float nr,float fr );
void setViewport( int x,int y,int w,int h );
void setClsColor( const Vector &v );
void setClsMode( bool cls_argb,bool cls_z );
void setProjMode( int mode );
void setFogColor( const Vector &v );
void setFogRange( float nr,float fr );
void setFogMode( int mode );
//called by world
bool beginRenderFrame();
//Camera frustum...
float getFrustumNear()const;
float getFrustumFar()const;
float getFrustumWidth()const;
float getFrustumHeight()const;
const Frustum &getFrustum()const;
void getViewport( int *x,int *y,int *w,int *h )const;
int getProjMode()const{ return proj_mode; }
private:
float zoom;
int vp_x,vp_y,vp_w,vp_h;
Vector cls_color;
bool cls_argb,cls_z;
int proj_mode;
Vector fog_color;
float fog_nr,fog_fr;
int fog_mode;
float frustum_nr,frustum_fr;
mutable float frustum_w,frustum_h;
mutable Frustum local_frustum;
mutable bool local_valid;
};
#endif
+361
View File
@@ -0,0 +1,361 @@
#include "std.hpp"
#include "collision.hpp"
const float COLLISION_FLT_EPSILON=.001f;
/*
//
// OLD VERSION
//
bool Collision::sphereCollide( const Line &line,float radius,const Vector &dest,float dest_radius ){
radius+=dest_radius;
Line l( line.o-dest,line.d );
float a=l.d.dot(l.d);
if( !a ) return false;
float b=l.o.dot(l.d)*2;
float c=l.o.dot(l.o)-radius*radius;
float d=b*b-4*a*c;
if( d<0 ) return false;
float t1=(-b+sqrt(d))/(2*a);
float t2=(-b-sqrt(d))/(2*a);
float t=t1<t2 ? t1 : t2;
if( t<0 || t>=time ) return false;
time=t;
normal=(l*t).normalized();
return true;
}
bool Collision::sphereCollide( const Line &line,float radius,const Vector &dest,const Vector &radii ){
radius+=radii.x;
Line l( line.o-dest,line.d );
// float y_scale=1;
// if( radii.x!=radii.y ){
// y_scale=radii.x/radii.y;
// l.o.y*=y_scale;
// l.d.y*=y_scale;
// }
float a=l.d.dot(l.d);
if( !a ) return false;
float b=l.o.dot(l.d)*2;
float c=l.o.dot(l.o)-radius*radius;
float d=b*b-4*a*c;
if( d<0 ) return false;
float t1=(-b+sqrt(d))/(2*a);
float t2=(-b-sqrt(d))/(2*a);
float t=t1<t2 ? t1 : t2;
if( t<0 || t>=time ) return false;
time=t;
normal=(l*t).normalized();
return true;
}
//v0,v1 = edge verts
//pn = poly normal
//en = edge normal
static bool edgeTest( const Vector &v0,const Vector &v1,const Vector &pn,const Vector &en,const Line &line,float radius,Collision *curr_coll ){
Matrix tm=~Matrix( en,(v1-v0).normalized(),pn );
Vector sv=tm*(line.o-v0),dv=tm*(line.o+line.d-v0);
Line l( sv,dv-sv );
//do cylinder test...
float a,b,c,d,t1,t2,t;
a=(l.d.x*l.d.x+l.d.z*l.d.z);
if( !a ) return false; //ray parallel to cylinder
b=(l.o.x*l.d.x+l.o.z*l.d.z)*2;
c=(l.o.x*l.o.x+l.o.z*l.o.z)-radius*radius;
d=b*b-4*a*c;
if( d<0 ) return false; //ray misses cylinder
t1=(-b+sqrt(d))/(2*a);
t2=(-b-sqrt(d))/(2*a);
t=t1<t2 ? t1 : t2;
if( t>curr_coll->time ) return false; //intersects too far away
Vector i=l*t,p;
if( i.y>v0.distance(v1) ) return false; //intersection above cylinder
if( i.y>=0 ){
if( t<0 ) return false;
p.y=i.y;
}else{
//below bottom of cylinder...do sphere test...
a=l.d.dot(l.d);
if( !a ) return false; //ray parallel to sphere
b=l.o.dot(l.d)*2;
c=l.o.dot(l.o)-radius*radius;
d=b*b-4*a*c;
if( d<0 ) return false; //ray misses sphere
t1=(-b+sqrt(d))/(2*a);
t2=(-b-sqrt(d))/(2*a);
t=t1<t2 ? t1 : t2;
if( t<0 || t>curr_coll->time ) return false; //intersects behind or too far away
i=l*t;
}
curr_coll->time=t;
curr_coll->normal=~tm*(i-p);
curr_coll->normal.normalize();
return true;
}
bool Collision::triangleCollide( const Line &line,float radius,const Vector &v0,const Vector &v1,const Vector &v2 ){
//triangle plane
Plane p( v0,v1,v2 );
if( p.n.dot( line.d )>=0 ) return false;
//intersection time
Plane tp=p;tp.d-=radius;
float t=tp.t_intersect( line );
if( t>time ) return false;
//intersection point
Plane p0( v0+p.n,v1,v0 ),p1( v1+p.n,v2,v1 ),p2( v2+p.n,v0,v2 );
if( t>=0 ){
Vector i=line*t;
if( p0.distance(i)>=0 && p1.distance(i)>=0 && p2.distance(i)>=0 ){
time=t;
normal=p.n;
return true;
}
}
if( radius<=0 ) return false;
return
edgeTest( v0,v1,p.n,p0.n,line,radius,this )|
edgeTest( v1,v2,p.n,p1.n,line,radius,this )|
edgeTest( v2,v0,p.n,p2.n,line,radius,this );
}
bool Collision::boxCollide( const Line &line,float radius,const Box &box ){
static int quads[]={
2,3,1,0,
3,7,5,1,
7,6,4,5,
6,2,0,4,
6,7,3,2,
0,1,5,4
};
bool hit=false;
for( int n=0;n<24;n+=4 ){
Vector
v0( box.corner( quads[n] ) ),
v1( box.corner( quads[n+1] ) ),
v2( box.corner( quads[n+2] ) ),
v3( box.corner( quads[n+3] ) );
//quad plane
Plane p( v0,v1,v2 );
if( p.n.dot( line.d )>=0 ) continue;
p.d-=radius;
float t=p.t_intersect( line );
if( t>time ) continue;
//intersection point
Plane
p0( v0+p.n,v1,v0 ),
p1( v1+p.n,v2,v1 ),
p2( v2+p.n,v3,v2 ),
p3( v3+p.n,v0,v3 );
if( t>=0 ){
Vector i=line*t;
if( p0.distance(i)>=0 && p1.distance(i)>=0 && p2.distance(i)>=0 && p3.distance(i)>=0 ){
time=t;
normal=p.n;
hit=true;
continue;
}
}
if( radius<=0 ) continue;
hit|=
edgeTest( v0,v1,p.n,p0.n,line,radius,this )|
edgeTest( v1,v2,p.n,p1.n,line,radius,this )|
edgeTest( v2,v3,p.n,p2.n,line,radius,this )|
edgeTest( v3,v0,p.n,p3.n,line,radius,this );
}
return hit;
}
*/
bool Collision::update( const Line &line,float t,const Vector &n ){
// if( t<0 || t>time ) return false;
if( t>time ) return false;
Plane p(line*t,n);
if( p.n.dot( line.d )>=0 ) return false;
if( p.distance(line.o)<-COLLISION_FLT_EPSILON ) return false;
time=t;
normal=n;
return true;
}
//
// NEW VERSION
//
extern gxRuntime *gx_runtime;
bool Collision::sphereCollide( const Line &line,float radius,const Vector &dest,float dest_radius ){
radius+=dest_radius;
Line l( line.o-dest,line.d );
float a=l.d.dot(l.d);
if( !a ) return false;
float b=l.o.dot(l.d)*2;
float c=l.o.dot(l.o)-radius*radius;
float d=b*b-4*a*c;
if( d<0 ) return false;
float t1=(-b+sqrt(d))/(2*a);
float t2=(-b-sqrt(d))/(2*a);
float t=t1<t2 ? t1 : t2;
if( t>time ) return false;
return update( line,t,(l*t).normalized() );
}
//v0,v1 = edge verts
//pn = poly normal
//en = edge normal
static bool edgeTest( const Vector &v0,const Vector &v1,const Vector &pn,const Vector &en,const Line &line,float radius,Collision *curr_coll ){
Matrix tm=~Matrix( en,(v1-v0).normalized(),pn );
Vector sv=tm*(line.o-v0),dv=tm*(line.o+line.d-v0);
Line l( sv,dv-sv );
//do cylinder test...
float a,b,c,d,t1,t2,t;
a=(l.d.x*l.d.x+l.d.z*l.d.z);
if( !a ) return false; //ray parallel to cylinder
b=(l.o.x*l.d.x+l.o.z*l.d.z)*2;
c=(l.o.x*l.o.x+l.o.z*l.o.z)-radius*radius;
d=b*b-4*a*c;
if( d<0 ) return false; //ray misses cylinder
t1=(-b+sqrt(d))/(2*a);
t2=(-b-sqrt(d))/(2*a);
t=t1<t2 ? t1 : t2;
if( t>curr_coll->time ) return false; //intersects too far away
Vector i=l*t,p;
if( i.y>v0.distance(v1) ) return false; //intersection above cylinder
if( i.y>=0 ){
p.y=i.y;
}else{
//below bottom of cylinder...do sphere test...
a=l.d.dot(l.d);
if( !a ) return false; //ray parallel to sphere
b=l.o.dot(l.d)*2;
c=l.o.dot(l.o)-radius*radius;
d=b*b-4*a*c;
if( d<0 ) return false; //ray misses sphere
t1=(-b+sqrt(d))/(2*a);
t2=(-b-sqrt(d))/(2*a);
t=t1<t2 ? t1 : t2;
if( t>curr_coll->time ) return false;
i=l*t;
}
return curr_coll->update( line,t,(~tm*(i-p)).normalized() );
}
bool Collision::triangleCollide( const Line &line,float radius,const Vector &v0,const Vector &v1,const Vector &v2 ){
//triangle plane
Plane p( v0,v1,v2 );
if( p.n.dot( line.d )>=0 ) return false;
//move plane out
p.d-=radius;
float t=p.t_intersect( line );
if( t>time ) return false;
//edge planes
Plane p0( v0+p.n,v1,v0 ),p1( v1+p.n,v2,v1 ),p2( v2+p.n,v0,v2 );
//intersects triangle?
Vector i=line*t;
if( p0.distance(i)>=0 && p1.distance(i)>=0 && p2.distance(i)>=0 ){
return update( line,t,p.n );
}
if( radius<=0 ) return false;
return
edgeTest( v0,v1,p.n,p0.n,line,radius,this )|
edgeTest( v1,v2,p.n,p1.n,line,radius,this )|
edgeTest( v2,v0,p.n,p2.n,line,radius,this );
}
bool Collision::boxCollide( const Line &line,float radius,const Box &box ){
static int quads[]={
2,3,1,0,
3,7,5,1,
7,6,4,5,
6,2,0,4,
6,7,3,2,
0,1,5,4
};
bool hit=false;
for( int n=0;n<24;n+=4 ){
Vector
v0( box.corner( quads[n] ) ),
v1( box.corner( quads[n+1] ) ),
v2( box.corner( quads[n+2] ) ),
v3( box.corner( quads[n+3] ) );
//quad plane
Plane p( v0,v1,v2 );
if( p.n.dot( line.d )>=0 ) continue;
//move plane out
p.d-=radius;
float t=p.t_intersect( line );
if( t>time ) return false;
//edge planes
Plane
p0( v0+p.n,v1,v0 ),
p1( v1+p.n,v2,v1 ),
p2( v2+p.n,v3,v2 ),
p3( v3+p.n,v0,v3 );
//intersects triangle?
Vector i=line*t;
if( p0.distance(i)>=0 && p1.distance(i)>=0 && p2.distance(i)>=0 && p3.distance(i)>=0 ){
hit|=update( line,t,p.n );
continue;
}
if( radius<=0 ) continue;
hit|=
edgeTest( v0,v1,p.n,p0.n,line,radius,this )|
edgeTest( v1,v2,p.n,p1.n,line,radius,this )|
edgeTest( v2,v3,p.n,p2.n,line,radius,this )|
edgeTest( v3,v0,p.n,p3.n,line,radius,this );
}
return hit;
}
+27
View File
@@ -0,0 +1,27 @@
#ifndef COLLISION_H
#define COLLISION_H
#include "geom.hpp"
extern const float COLLISION_FLT_EPSILON;
struct Collision{
float time;
Vector normal;
void *surface;
unsigned short index;
Collision():time(1),surface(0),index(~0){}
bool update( const Line &line,float time,const Vector &normal );
bool sphereCollide( const Line &src_line,float src_radius,const Vector &dest,float dest_radius );
bool sphereCollide( const Line &line,float radius,const Vector &dest,const Vector &radii );
bool triangleCollide( const Line &src_line,float src_radius,const Vector &v0,const Vector &v1,const Vector &v2 );
bool boxCollide( const Line &src_line,float src_radius,const Box &box );
};
#endif
+42
View File
@@ -0,0 +1,42 @@
#include "std.hpp"
#include "emitter.hpp"
Emitter::Emitter(){
}
Emitter::Emitter( const Emitter &t ){
}
Emitter::~Emitter(){
}
void Emitter::beginRender( float tween ){
Object::beginRender( tween );
vel=getRenderTform().v-pos;
pos=getRenderTform().v;
for( int k=0;k<channels.size();++k ){
gxChannel *chan=channels[k];
if( !chan->isPlaying() ){
channels[k]=0;
continue;
}
chan->set3d( &pos.x,&vel.x );
}
}
gxChannel *Emitter::emitSound( gxSound *sound ){
gxChannel *chan=sound->play3d( &pos.x,&vel.x );
for( int k=0;k<channels.size();++k ){
if( chan=channels[k] ) return chan;
if( channels[k] ) continue;
channels[k]=chan;
return chan;
}
channels.push_back( chan );
return chan;
}
+32
View File
@@ -0,0 +1,32 @@
#ifndef EMITTER_H
#define EMITTER_H
#include "object.hpp"
class gxSound;
class gxChannel;
class Emitter : public Object{
public:
Emitter();
Emitter( const Emitter &t );
~Emitter();
//Entity interface
Entity *clone(){ return d_new Emitter( *this ); }
Emitter *getEmitter(){ return this; }
//Object interface
void beginRender( float tween );
//Public interface
gxChannel *emitSound( gxSound *sound );
private:
Vector pos,vel;
vector<gxChannel*> channels;
};
#endif
+197
View File
@@ -0,0 +1,197 @@
#include "std.hpp"
#include "entity.hpp"
//#include "stats.hpp"
Entity *Entity::_orphans, *Entity::_last_orphan;
enum {
INVALID_LOCALTFORM = 1,
INVALID_WORLDTFORM = 2
};
void Entity::RemoveParent() {
if (m_parent) {
if (m_parent->m_children == this) m_parent->m_children = m_listNext;
if (m_parent->m_last_child == this) m_parent->m_last_child = m_listPrev;
} else {
if (_orphans == this) _orphans = m_listNext;
if (_last_orphan == this) _last_orphan = m_listPrev;
}
if (m_listNext) m_listNext->m_listPrev = m_listPrev;
if (m_listPrev) m_listPrev->m_listNext = m_listNext;
}
void Entity::InsertChildToParent() {
m_listNext = 0;
if (m_parent) {
if (m_listPrev = m_parent->m_last_child) m_listPrev->m_listNext = this;
else m_parent->m_children = this;
m_parent->m_last_child = this;
} else {
if (m_listPrev = _last_orphan) m_listPrev->m_listNext = this;
else _orphans = this;
_last_orphan = this;
}
}
Entity::Entity() :
m_listNext(0), m_listPrev(0), m_parent(0), m_children(0), m_last_child(0),
m_isVisible(true), m_isEnabled(true),
m_localScale(1, 1, 1),
invalid(0) {
InsertChildToParent();
}
Entity::Entity(const Entity &e) :
m_listNext(0), m_listPrev(0), m_parent(0), m_children(0), m_last_child(0),
m_name(e.m_name), m_isVisible(e.m_isVisible), m_isEnabled(e.m_isEnabled),
m_localPosition(e.m_localPosition),
m_localScale(e.m_localScale),
m_localRotation(e.m_localRotation),
invalid(INVALID_LOCALTFORM | INVALID_WORLDTFORM) {
InsertChildToParent();
}
Entity::~Entity() {
while (GetChildren()) delete GetChildren();
RemoveParent();
}
void Entity::InvalidateWorldTransform() {
if (invalid & INVALID_WORLDTFORM) return;
invalid |= INVALID_WORLDTFORM;
for (Entity *e = m_children; e; e = e->m_listNext) {
e->InvalidateWorldTransform();
}
}
void Entity::invalidateLocal() {
invalid |= INVALID_LOCALTFORM;
InvalidateWorldTransform();
}
const Transform &Entity::GetLocalTransform()const {
if (invalid&INVALID_LOCALTFORM) {
m_localTransform.m = Matrix(m_localRotation);
m_localTransform.m.i *= m_localScale.x;
m_localTransform.m.j *= m_localScale.y;
m_localTransform.m.k *= m_localScale.z;
m_localTransform.v = m_localPosition;
invalid &= ~INVALID_LOCALTFORM;
}
return m_localTransform;
}
const Transform &Entity::GetWorldTransform()const {
if (invalid&INVALID_WORLDTFORM) {
m_worldTransform = m_parent ? m_parent->GetWorldTransform() * GetLocalTransform() : GetLocalTransform();
invalid &= ~INVALID_WORLDTFORM;
}
return m_worldTransform;
}
void Entity::SetParent(Entity *p) {
if (m_parent == p) return;
RemoveParent();
m_parent = p;
InsertChildToParent();
InvalidateWorldTransform();
}
void Entity::SetName(const string &t) {
m_name = t;
}
void Entity::SetVisible(bool visible) {
m_isVisible = visible;
}
void Entity::SetEnabled(bool enabled) {
m_isEnabled = enabled;
}
void Entity::EnumerateVisible(std::list<Object*> &out) {
if (!m_isVisible) return;
if (Object *o = getObject()) out.push_back(o);
for (Entity *e = m_children; e; e = e->m_listNext) {
e->EnumerateVisible(out);
}
}
void Entity::EnumerateEnabled(std::list<Object*> &out) {
if (!m_isEnabled) return;
if (Object *o = getObject()) out.push_back(o);
for (Entity *e = m_children; e; e = e->m_listNext) {
e->EnumerateEnabled(out);
}
}
void Entity::SetLocalPosition(const Vector &v) {
m_localPosition = v;
invalidateLocal();
}
void Entity::SetLocalScale(const Vector &v) {
m_localScale = v;
invalidateLocal();
}
void Entity::SetLocalRotation(const Quat &q) {
m_localRotation = q.normalized();
invalidateLocal();
}
void Entity::SetLocalTransform(const Transform &t) {
m_localPosition = t.v;
m_localScale = Vector(t.m.i.length(), t.m.j.length(), t.m.k.length());
m_localRotation = matrixQuat(t.m);
invalidateLocal();
}
void Entity::SetWorldPosition(const Vector &v) {
SetLocalPosition(m_parent ? -m_parent->GetWorldTransform() * v : v);
}
void Entity::SetWorldScale(const Vector &v) {
SetLocalScale(m_parent ? v / m_parent->GetWorldScale() : v);
}
void Entity::SetWorldRotation(const Quat &q) {
SetLocalRotation(m_parent ? -m_parent->GetWorldRotation() * q : q);
}
void Entity::SetWorldTransform(const Transform &t) {
SetLocalTransform(m_parent ? -m_parent->GetWorldTransform() * t : t);
}
const Vector &Entity::GetLocalPosition()const {
return m_localPosition;
}
const Vector &Entity::GetLocalScale()const {
return m_localScale;
}
const Quat &Entity::GetLocalRotation()const {
return m_localRotation;
}
const Vector &Entity::GetWorldPosition()const {
return GetWorldTransform().v;
}
const Vector &Entity::GetWorldScale()const {
m_worldScale = m_parent ? m_parent->GetWorldScale() * m_localScale : m_localScale;
return m_worldScale;
}
const Quat &Entity::GetWorldRotation()const {
m_worldRotation = m_parent ? m_parent->GetWorldRotation() * m_localRotation : m_localRotation;
return m_worldRotation;
}
+99
View File
@@ -0,0 +1,99 @@
#ifndef ENTITY_H
#define ENTITY_H
#include <list>
#include "geom.hpp"
class Entity;
class Object;
class Camera;
class Light;
class Model;
class Mirror;
class Listener;
class MeshModel;
class MD2Model;
class Entity {
public:
Entity();
Entity(const Entity &e);
virtual ~Entity();
virtual Entity *clone() = 0;
//ugly casts!
virtual Object *getObject() { return nullptr; }
virtual Camera *getCamera() { return nullptr; }
virtual Light *getLight() { return nullptr; }
virtual Model *getModel() { return nullptr; }
virtual Mirror *getMirror() { return nullptr; }
virtual Listener *getListener() { return nullptr; }
void SetName(const std::string &t);
std::string getName()const { return m_name; }
void SetParent(Entity *parent);
Entity *getParent()const { return m_parent; }
void SetVisible(bool vis);
bool IsVisible()const { return m_isVisible; }
void EnumerateVisible(std::list<Object*> &out);
void SetEnabled(bool ena);
bool IsEnabled()const { return m_isEnabled; }
void EnumerateEnabled(std::list<Object*> &out);
void SetLocalPosition(const Vector &v);
const Vector &GetLocalPosition()const;
void SetLocalScale(const Vector & v);
const Vector &GetLocalScale()const;
void SetLocalRotation(const Quat &q);
const Quat &GetLocalRotation()const;
void SetLocalTransform(const Transform &t);
const Transform &GetLocalTransform()const;
void SetWorldPosition(const Vector &v);
const Vector &GetWorldPosition()const;
void SetWorldScale(const Vector &v);
const Vector &GetWorldScale()const;
void SetWorldRotation(const Quat &q);
const Quat &GetWorldRotation()const;
void SetWorldTransform(const Transform &t);
const Transform &GetWorldTransform()const;
Entity* GetChildren()const { return m_children; }
Entity* GetSuccessor()const { return m_listNext; }
static Entity* GetEntityOrphans() { return _orphans; }
private:
Entity *m_listNext, *m_listPrev, *m_parent, *m_children, *m_last_child;
static Entity *_orphans, *_last_orphan;
bool m_isVisible, m_isEnabled;
std::string m_name;
mutable int invalid;
Quat m_localRotation;
Vector m_localPosition, m_localScale;
mutable Transform m_localTransform;
mutable Quat m_worldRotation;
mutable Vector m_worldPosition, m_worldScale;
mutable Transform m_worldTransform;
void InsertChildToParent();
void RemoveParent();
void invalidateLocal();
void InvalidateWorldTransform();
};
#endif
View File
+54
View File
@@ -0,0 +1,54 @@
#include "std.hpp"
#include "frustum.hpp"
Frustum::Frustum(){
}
Frustum::Frustum( float nr,float fr,float w,float h ){
verts[VERT_TLNEAR]=Vector( w*-.5f,h*+.5f,nr );
verts[VERT_TRNEAR]=Vector( w*+.5f,h*+.5f,nr );
verts[VERT_BRNEAR]=Vector( w*+.5f,h*-.5f,nr );
verts[VERT_BLNEAR]=Vector( w*-.5f,h*-.5f,nr );
float t=fr/nr;
verts[VERT_TLFAR]=verts[VERT_TLNEAR] * t;
verts[VERT_TRFAR]=verts[VERT_TRNEAR] * t;
verts[VERT_BRFAR]=verts[VERT_BRNEAR] * t;
verts[VERT_BLFAR]=verts[VERT_BLNEAR] * t;
verts[VERT_EYE]=Vector();
makePlanes();
}
Frustum::Frustum( const Frustum &f,const Transform &t ){
for( int k=0;k<9;++k ){
verts[k]=t*f.verts[k];
}
makePlanes();
}
bool Frustum::cull( const Vector v[],int cnt )const{
for( int n=0;n<6;++n ){
int k;
for( k=0;k<cnt && planes[n].distance( v[k] )<0;++k ){}
if( k==cnt ) return false;
}
return true;
}
bool Frustum::cull( const Box &b )const{
Vector v[8];
for( int k=0;k<8;++k ) v[k]=b.corner(k);
return cull( v,8 );
}
void Frustum::makePlanes(){
planes[PLANE_TOP]=Plane( verts[VERT_EYE],verts[VERT_TRFAR],verts[VERT_TLFAR] );
planes[PLANE_LEFT]=Plane( verts[VERT_EYE],verts[VERT_TLFAR],verts[VERT_BLFAR] );
planes[PLANE_BOTTOM]=Plane( verts[VERT_EYE],verts[VERT_BLFAR],verts[VERT_BRFAR] );
planes[PLANE_RIGHT]=Plane( verts[VERT_EYE],verts[VERT_BRFAR],verts[VERT_TRFAR] );
planes[PLANE_NEAR]=Plane( verts[VERT_TRNEAR],verts[VERT_TLNEAR],verts[VERT_BLNEAR] );
planes[PLANE_FAR]=Plane( verts[VERT_TLFAR],verts[VERT_TRFAR],verts[VERT_BRFAR] );
if( planes[PLANE_NEAR].distance( verts[VERT_EYE] )>0 ){
for( int k=0;k<6;++k ) planes[k]=-planes[k];
}
}
+32
View File
@@ -0,0 +1,32 @@
#ifndef FRUSTUM_H
#define FRUSTUM_H
#include "geom.hpp"
class Frustum{
public:
enum{
VERT_TLNEAR=0,VERT_TRNEAR,VERT_BRNEAR,VERT_BLNEAR,
VERT_TLFAR,VERT_TRFAR,VERT_BRFAR,VERT_BLFAR,VERT_EYE
};
enum{
PLANE_TOP=0,PLANE_LEFT,PLANE_BOTTOM,PLANE_RIGHT,PLANE_NEAR,PLANE_FAR
};
Frustum();
Frustum( float nr,float fr,float w,float h );
Frustum( const Frustum &f,const Transform &t );
bool cull( const Box &box )const;
bool cull( const Vector vecs[],int cnt )const;
const Plane &getPlane( int n )const{ return planes[n]; }
const Vector &getVertex( int n )const{ return verts[n]; }
private:
Plane planes[6];
Vector verts[9];
void makePlanes();
};
#endif
+23
View File
@@ -0,0 +1,23 @@
#include "std.hpp"
#include "geom.hpp"
Matrix Matrix::tmps[64];
Transform Transform::tmps[64];
Quat rotationQuat( float p,float y,float r ){
return yawQuat(y)*pitchQuat(p)*rollQuat(r);
}
/*
Quat rotationQuat( float p,float y,float r ){
float sp=sin(p/-2),cp=cos(p/-2);
float sy=sin(y/ 2),cy=cos(y/ 2);
float sr=sin(r/-2),cr=cos(r/-2);
float qw=cr*cp*cy + sr*sp*sy;
float qx=cr*sp*cy + sr*cp*sy;
float qy=cr*cp*sy - sr*sp*cy;
float qz=sr*cp*cy - cr*sp*sy;
return Quat( qw,Vector(-qx,-qy,qz) );
}
*/
+556
View File
@@ -0,0 +1,556 @@
#ifndef GEOM_H
#define GEOM_H
#include <math.h>
class Vector;
class Line;
class Plane;
class Matrix;
class Transform;
static float PI = 3.14159265359f; //180 degrees
static float TWOPI = PI*2.0f; //360 degrees
static float HALFPI = PI*.5f; //90 degrees
static float QUARTERPI = PI*.25f; //45 degrees
class Vector {
public:
float x, y, z;
Vector() :x(0), y(0), z(0) {
}
Vector(float x, float y, float z) :x(x), y(y), z(z) {
}
operator float*() {
return &x;
}
operator const float *() {
return &x;
}
float &operator[](int n) {
return (&x)[n];
}
float operator[](int n)const {
return (&x)[n];
}
Vector operator-()const {
return Vector(-x, -y, -z);
}
Vector operator*(float scale)const {
return Vector(x*scale, y*scale, z*scale);
}
Vector operator*(const Vector &q)const {
return Vector(x*q.x, y*q.y, z*q.z);
}
Vector operator/(float scale)const {
return Vector(x / scale, y / scale, z / scale);
}
Vector operator/(const Vector &q)const {
return Vector(x / q.x, y / q.y, z / q.z);
}
Vector operator+(const Vector &q)const {
return Vector(x + q.x, y + q.y, z + q.z);
}
Vector operator-(const Vector &q)const {
return Vector(x - q.x, y - q.y, z - q.z);
}
Vector &operator*=(float scale) {
x *= scale; y *= scale; z *= scale; return *this;
}
Vector &operator*=(const Vector &q) {
x *= q.x; y *= q.y; z *= q.z; return *this;
}
Vector &operator/=(float scale) {
x /= scale; y /= scale; z /= scale; return *this;
}
Vector &operator/=(const Vector &q) {
x /= q.x; y /= q.y; z /= q.z; return *this;
}
Vector &operator+=(const Vector &q) {
x += q.x; y += q.y; z += q.z; return *this;
}
Vector &operator-=(const Vector &q) {
x -= q.x; y -= q.y; z -= q.z; return *this;
}
bool operator<(const Vector &q)const {
if (fabs(x - q.x) > FLT_EPSILON) return x < q.x ? true : false;
if (fabs(y - q.y) > FLT_EPSILON) return y < q.y ? true : false;
return fabs(z - q.z) > FLT_EPSILON && z < q.z;
}
bool operator==(const Vector &q)const {
return fabs(x - q.x) <= FLT_EPSILON && fabs(y - q.y) <= FLT_EPSILON && fabs(z - q.z) <= FLT_EPSILON;
}
bool operator!=(const Vector &q)const {
return fabs(x - q.x) > FLT_EPSILON || fabs(y - q.y) > FLT_EPSILON || fabs(z - q.z) > FLT_EPSILON;
}
float dot(const Vector &q)const {
return x*q.x + y*q.y + z*q.z;
}
Vector cross(const Vector &q)const {
return Vector(y*q.z - z*q.y, z*q.x - x*q.z, x*q.y - y*q.x);
}
float length()const {
return sqrtf(x*x + y*y + z*z);
}
float distance(const Vector &q)const {
float dx = x - q.x, dy = y - q.y, dz = z - q.z; return sqrtf(dx*dx + dy*dy + dz*dz);
}
Vector normalized()const {
float l = length(); return Vector(x / l, y / l, z / l);
}
void normalize() {
float l = length(); x /= l; y /= l; z /= l;
}
float yaw()const {
return -atan2f(x, z);
}
float pitch()const {
return -atan2f(y, sqrtf(x*x + z*z));
}
void clear() {
x = y = z = 0;
}
};
class Line {
public:
Vector o, d;
Line() {
}
Line(const Vector &o, const Vector &d) :o(o), d(d) {
}
Line operator+(const Vector &q)const {
return Line(o + q, d);
}
Line operator-(const Vector &q)const {
return Line(o - q, d);
}
Vector operator*(float q)const {
return o + d*q;
}
Vector nearest(const Vector &q)const {
return o + d*(d.dot(q - o) / d.dot(d));
}
};
class Plane {
public:
Vector n;
float d;
Plane() :d(0) {
}
//normal/offset form
Plane(const Vector &n, float d) :n(n), d(d) {
}
//point/normal form
Plane(const Vector &p, const Vector &n) :n(n), d(-n.dot(p)) {
}
//create plane from tri
Plane(const Vector &v0, const Vector &v1, const Vector &v2) {
n = (v1 - v0).cross(v2 - v0).normalized(); d = -n.dot(v0);
}
Plane operator-()const {
return Plane(-n, -d);
}
float t_intersect(const Line &q)const {
return -distance(q.o) / n.dot(q.d);
}
Vector intersect(const Line &q)const {
return q*t_intersect(q);
}
Line intersect(const Plane &q)const {
Vector lv = n.cross(q.n).normalized();
return Line(q.intersect(Line(nearest(n*-d), n.cross(lv))), lv);
}
Vector nearest(const Vector &q)const {
return q - n*distance(q);
}
void negate() {
n = -n; d = -d;
}
float distance(const Vector &q)const {
return n.dot(q) + d;
}
};
struct Quat {
float w;
Vector v;
Quat() :w(1) {
}
Quat(float w, const Vector &v) :w(w), v(v) {
}
Quat operator-()const {
return Quat(w, -v);
}
Quat operator+(const Quat &q)const {
return Quat(w + q.w, v + q.v);
}
Quat operator-(const Quat &q)const {
return Quat(w - q.w, v - q.v);
}
Quat operator*(const Quat &q)const {
return Quat(w*q.w - v.dot(q.v), q.v.cross(v) + q.v*w + v*q.w);
}
Vector operator*(const Vector &q)const {
return (*this * Quat(0, q) * -*this).v;
}
Quat operator*(float q)const {
return Quat(w*q, v*q);
}
Quat operator/(float q)const {
return Quat(w / q, v / q);
}
float dot(const Quat &q)const {
return v.x*q.v.x + v.y*q.v.y + v.z*q.v.z + w*q.w;
}
float length()const {
return sqrtf(w*w + v.x*v.x + v.y*v.y + v.z*v.z);
}
void normalize() {
*this = *this / length();
}
Quat normalized()const {
return *this / length();
}
Quat slerpTo(const Quat &q, float a)const {
Quat t = q;
float d = dot(q), b = 1 - a;
if (d < 0) { t.w = -t.w; t.v = -t.v; d = -d; }
if (d < 1 - FLT_EPSILON) {
float om = acosf(d);
float si = sinf(om);
a = sinf(a*om) / si;
b = sinf(b*om) / si;
}
return *this*b + t*a;
}
Vector i()const {
float xz = v.x*v.z, wy = w*v.y;
float xy = v.x*v.y, wz = w*v.z;
float yy = v.y*v.y, zz = v.z*v.z;
return Vector(1 - 2 * (yy + zz), 2 * (xy - wz), 2 * (xz + wy));
}
Vector j()const {
float yz = v.y*v.z, wx = w*v.x;
float xy = v.x*v.y, wz = w*v.z;
float xx = v.x*v.x, zz = v.z*v.z;
return Vector(2 * (xy + wz), 1 - 2 * (xx + zz), 2 * (yz - wx));
}
Vector k()const {
float xz = v.x*v.z, wy = w*v.y;
float yz = v.y*v.z, wx = w*v.x;
float xx = v.x*v.x, yy = v.y*v.y;
return Vector(2 * (xz - wy), 2 * (yz + wx), 1 - 2 * (xx + yy));
}
};
class Matrix {
static Matrix tmps[64];
static Matrix &alloc_tmp() { static int tmp = 0; return tmps[tmp++ & 63]; }
friend class Transform;
public:
Vector i, j, k;
Matrix() :i(Vector(1, 0, 0)), j(Vector(0, 1, 0)), k(Vector(0, 0, 1)) {
}
Matrix(const Vector &i, const Vector &j, const Vector &k) :i(i), j(j), k(k) {
}
Matrix(const Quat &q) {
float xx = q.v.x*q.v.x, yy = q.v.y*q.v.y, zz = q.v.z*q.v.z;
float xy = q.v.x*q.v.y, xz = q.v.x*q.v.z, yz = q.v.y*q.v.z;
float wx = q.w*q.v.x, wy = q.w*q.v.y, wz = q.w*q.v.z;
i = Vector(1 - 2 * (yy + zz), 2 * (xy - wz), 2 * (xz + wy)),
j = Vector(2 * (xy + wz), 1 - 2 * (xx + zz), 2 * (yz - wx)),
k = Vector(2 * (xz - wy), 2 * (yz + wx), 1 - 2 * (xx + yy));
}
Matrix(float angle, const Vector &axis) {
const Vector &u = axis;
float c = cosf(angle), s = sinf(angle);
float x2 = axis.x*axis.x, y2 = axis.y*axis.y, z2 = axis.z*axis.z;
i = Vector(x2 + c*(1 - x2), u.x*u.y*(1 - c) - u.z*s, u.z*u.x*(1 - c) + u.y*s);
j = Vector(u.x*u.y*(1 - c) + u.z*s, y2 + c*(1 - y2), u.y*u.z*(1 - c) - u.x*s);
k = Vector(u.z*u.x*(1 - c) - u.y*s, u.y*u.z*(1 - c) + u.x*s, z2 + c*(1 - z2));
}
Vector &operator[](int n) {
return (&i)[n];
}
const Vector &operator[](int n)const {
return (&i)[n];
}
Matrix &operator~()const {
Matrix &m = alloc_tmp();
m.i.x = i.x; m.i.y = j.x; m.i.z = k.x;
m.j.x = i.y; m.j.y = j.y; m.j.z = k.y;
m.k.x = i.z; m.k.y = j.z; m.k.z = k.z;
return m;
}
float determinant()const {
return i.x*(j.y*k.z - j.z*k.y) - i.y*(j.x*k.z - j.z*k.x) + i.z*(j.x*k.y - j.y*k.x);
}
Matrix &operator-()const {
Matrix &m = alloc_tmp();
float t = 1.0f / determinant();
m.i.x = t*(j.y*k.z - j.z*k.y); m.i.y = -t*(i.y*k.z - i.z*k.y); m.i.z = t*(i.y*j.z - i.z*j.y);
m.j.x = -t*(j.x*k.z - j.z*k.x); m.j.y = t*(i.x*k.z - i.z*k.x); m.j.z = -t*(i.x*j.z - i.z*j.x);
m.k.x = t*(j.x*k.y - j.y*k.x); m.k.y = -t*(i.x*k.y - i.y*k.x); m.k.z = t*(i.x*j.y - i.y*j.x);
return m;
}
Matrix &cofactor()const {
Matrix &m = alloc_tmp();
m.i.x = (j.y*k.z - j.z*k.y); m.i.y = -(j.x*k.z - j.z*k.x); m.i.z = (j.x*k.y - j.y*k.x);
m.j.x = -(i.y*k.z - i.z*k.y); m.j.y = (i.x*k.z - i.z*k.x); m.j.z = -(i.x*k.y - i.y*k.x);
m.k.x = (i.y*j.z - i.z*j.y); m.k.y = -(i.x*j.z - i.z*j.x); m.k.z = (i.x*j.y - i.y*j.x);
return m;
}
bool operator==(const Matrix &q)const {
return i == q.i && j == q.j && k == q.k;
}
bool operator!=(const Matrix &q)const {
return i != q.i || j != q.j || k != q.k;
}
Vector operator*(const Vector &q)const {
return Vector(i.x*q.x + j.x*q.y + k.x*q.z, i.y*q.x + j.y*q.y + k.y*q.z, i.z*q.x + j.z*q.y + k.z*q.z);
}
Matrix &operator*(const Matrix &q)const {
Matrix &m = alloc_tmp();
m.i.x = i.x*q.i.x + j.x*q.i.y + k.x*q.i.z; m.i.y = i.y*q.i.x + j.y*q.i.y + k.y*q.i.z; m.i.z = i.z*q.i.x + j.z*q.i.y + k.z*q.i.z;
m.j.x = i.x*q.j.x + j.x*q.j.y + k.x*q.j.z; m.j.y = i.y*q.j.x + j.y*q.j.y + k.y*q.j.z; m.j.z = i.z*q.j.x + j.z*q.j.y + k.z*q.j.z;
m.k.x = i.x*q.k.x + j.x*q.k.y + k.x*q.k.z; m.k.y = i.y*q.k.x + j.y*q.k.y + k.y*q.k.z; m.k.z = i.z*q.k.x + j.z*q.k.y + k.z*q.k.z;
return m;
}
void orthogonalize() {
k.normalize();
i = j.cross(k).normalized();
j = k.cross(i);
}
Matrix &orthogonalized()const {
Matrix &m = alloc_tmp();
m = *this; m.orthogonalize();
return m;
}
};
class Box {
public:
Vector a, b;
Box() :a(Vector(INFINITY, INFINITY, INFINITY)), b(Vector(-INFINITY, -INFINITY, -INFINITY)) {
}
Box(const Vector &q) :a(q), b(q) {
}
Box(const Vector &a, const Vector &b) :a(a), b(b) {
}
Box(const Line &l) :a(l.o), b(l.o) {
update(l.o + l.d);
}
void clear() {
a.x = a.y = a.z = INFINITY;
b.x = b.y = b.z = -INFINITY;
}
bool empty()const {
return b.x < a.x || b.y < a.y || b.z < a.z;
}
Vector centre()const {
return Vector((a.x + b.x)*.5f, (a.y + b.y)*.5f, (a.z + b.z)*.5f);
}
Vector corner(int n)const {
return Vector(((n & 1) ? b : a).x, ((n & 2) ? b : a).y, ((n & 4) ? b : a).z);
}
void update(const Vector &q) {
if (q.x < a.x) a.x = q.x; if (q.y < a.y) a.y = q.y; if (q.z < a.z) a.z = q.z;
if (q.x > b.x) b.x = q.x; if (q.y > b.y) b.y = q.y; if (q.z > b.z) b.z = q.z;
}
void update(const Box &q) {
if (q.a.x < a.x) a.x = q.a.x; if (q.a.y < a.y) a.y = q.a.y; if (q.a.z < a.z) a.z = q.a.z;
if (q.b.x > b.x) b.x = q.b.x; if (q.b.y > b.y) b.y = q.b.y; if (q.b.z > b.z) b.z = q.b.z;
}
bool overlaps(const Box &q)const {
return
(b.x < q.b.x ? b.x : q.b.x) >= (a.x > q.a.x ? a.x : q.a.x) &&
(b.y < q.b.y ? b.y : q.b.y) >= (a.y > q.a.y ? a.y : q.a.y) &&
(b.z < q.b.z ? b.z : q.b.z) >= (a.z > q.a.z ? a.z : q.a.z);
}
void expand(float n) {
a.x -= n; a.y -= n; a.z -= n; b.x += n; b.y += n; b.z += n;
}
float width()const {
return b.x - a.x;
}
float height()const {
return b.y - a.y;
}
float depth()const {
return b.z - a.z;
}
bool contains(const Vector &q) {
return q.x >= a.x && q.x <= b.x && q.y >= a.y && q.y <= b.y && q.z >= a.z && q.z <= b.z;
}
};
class Transform {
static Transform tmps[64];
static Transform &alloc_tmp() { static int tmp = 0; return tmps[tmp++ & 63]; }
public:
Matrix m;
Vector v;
Transform() {
}
Transform(const Matrix &m) :m(m) {
}
Transform(const Vector &v) :v(v) {
}
Transform(const Matrix &m, const Vector &v) :m(m), v(v) {
}
Transform &operator-()const {
Transform &t = alloc_tmp();
t.m = -m; t.v = t.m*-v;
return t;
}
Transform &operator~()const {
Transform &t = alloc_tmp();
t.m = ~m; t.v = t.m*-v;
return t;
}
Vector operator*(const Vector &q)const {
return m*q + v;
}
Line operator*(const Line &q)const {
Vector t = (*this)*q.o;
return Line(t, (*this)*(q.o + q.d) - t);
}
Box operator*(const Box &q)const {
Box t((*this*q.corner(0)));
for (int k = 1; k < 8; ++k) t.update(*this*q.corner(k));
return t;
}
Transform &operator*(const Transform &q)const {
Transform &t = alloc_tmp();
t.m = m*q.m; t.v = m*q.v + v;
return t;
}
bool operator==(const Transform &q)const {
return m == q.m && v == q.v;
}
bool operator!=(const Transform &q)const {
return !operator==(q);
}
};
inline float transformRadius(float r, const Matrix &t) {
static const float sq_3 = sqrtf(1.0f / 3.0f);
return (t * Vector(sq_3, sq_3, sq_3)).length()*r;
}
inline Matrix pitchMatrix(float q) {
return Matrix(Vector(1, 0, 0), Vector(0, cosf(q), sinf(q)), Vector(0, -sinf(q), cosf(q)));
}
inline Matrix yawMatrix(float q) {
return Matrix(Vector(cosf(q), 0, sinf(q)), Vector(0, 1, 0), Vector(-sinf(q), 0, cosf(q)));
}
inline Matrix rollMatrix(float q) {
return Matrix(Vector(cosf(q), sinf(q), 0), Vector(-sinf(q), cosf(q), 0), Vector(0, 0, 1));
}
inline float matrixPitch(const Matrix &m) {
return m.k.pitch();
// return asinf( -m.k.y );
}
inline float matrixYaw(const Matrix &m) {
return m.k.yaw();
//return atan2f( -m.k.x,m.k.z );
}
inline float matrixRoll(const Matrix &m) {
return atan2f(m.i.y, m.j.y);
//Matrix t=pitchMatrix( -matrixPitch(m) )*yawMatrix( -matrixYaw(m) )*m;
//return atan2f( t.i.y,t.i.x );
}
inline Matrix scaleMatrix(float x, float y, float z) {
return Matrix(Vector(x, 0, 0), Vector(0, y, 0), Vector(0, 0, z));
}
inline Matrix scaleMatrix(const Vector &scale) {
return Matrix(Vector(scale.x, 0, 0), Vector(0, scale.y, 0), Vector(0, 0, scale.z));
}
inline Quat pitchQuat(float p) {
return Quat(cosf(p / -2), Vector(sinf(p / -2), 0, 0));
}
inline Quat yawQuat(float y) {
return Quat(cosf(y / 2), Vector(0, sinf(y / 2), 0));
}
inline Quat rollQuat(float r) {
return Quat(cosf(r / -2), Vector(0, 0, sinf(r / -2)));
}
//inline Quat rotationQuat( float p,float y,float r ){
// return yawQuat(y)*pitchQuat(p)*rollQuat(r);
//}
Quat rotationQuat(float p, float y, float r);
inline Matrix rotationMatrix(float p, float y, float r) {
return yawMatrix(y)*pitchMatrix(p)*rollMatrix(r);
}
inline Matrix rotationMatrix(const Vector &rot) {
return yawMatrix(rot.y)*pitchMatrix(rot.x)*rollMatrix(rot.z);
}
inline float quatPitch(const Quat &q) {
return q.k().pitch();
}
inline float quatYaw(const Quat &q) {
return q.k().yaw();
}
inline float quatRoll(const Quat &q) {
// Vector i=q.i(),j=q.j();
// return atan2f( i.y,j.y );
return matrixRoll(q);
}
inline Quat matrixQuat(const Matrix &p) {
Matrix m = p;
m.orthogonalize();
float t = m.i.x + m.j.y + m.k.z, w, x, y, z;
if (t > FLT_EPSILON) {
t = sqrtf(t + 1) * 2;
x = (m.k.y - m.j.z) / t;
y = (m.i.z - m.k.x) / t;
z = (m.j.x - m.i.y) / t;
w = t / 4;
} else if (m.i.x > m.j.y && m.i.x > m.k.z) {
t = sqrtf(m.i.x - m.j.y - m.k.z + 1) * 2;
x = t / 4;
y = (m.j.x + m.i.y) / t;
z = (m.i.z + m.k.x) / t;
w = (m.k.y - m.j.z) / t;
} else if (m.j.y > m.k.z) {
t = sqrtf(m.j.y - m.k.z - m.i.x + 1) * 2;
x = (m.j.x + m.i.y) / t;
y = t / 4;
z = (m.k.y + m.j.z) / t;
w = (m.i.z - m.k.x) / t;
} else {
t = sqrtf(m.k.z - m.j.y - m.i.x + 1) * 2;
x = (m.i.z + m.k.x) / t;
y = (m.k.y + m.j.z) / t;
z = t / 4;
w = (m.j.x - m.i.y) / t;
}
return Quat(w, Vector(x, y, z));
}
#endif
+16
View File
@@ -0,0 +1,16 @@
#ifndef GROUP_H
#define GROUP_H
class Group{
vector<Object*> _objs;
public:
Group( Object *obj );
~Group();
const vector<Object*> objs()const{ return _objs; }
};
#endif
+33
View File
@@ -0,0 +1,33 @@
#include "std.hpp"
#include "light.hpp"
#include "../gxruntime/gxscene.hpp"
extern gxScene *gx_scene;
Light::Light( int type ){
light=gx_scene->createLight( type );
}
Light::~Light(){
gx_scene->freeLight( light );
}
void Light::setRange( float r ){
light->setRange( r );
}
void Light::setColor( const Vector &v ){
light->setColor( (float*)&v.x );
}
void Light::setConeAngles( float inner,float outer ){
light->setConeAngles( inner,outer );
}
bool Light::beginRender( float tween ){
Object::beginRender( tween );
light->setPosition( &getRenderTform().v.x );
light->setDirection( &getRenderTform().m.k.x );
return true;
}
+31
View File
@@ -0,0 +1,31 @@
#ifndef LIGHT_H
#define LIGHT_H
#include "geom.hpp"
#include "object.hpp"
#include "../gxruntime/gxlight.hpp"
class World;
class Light : public Object{
public:
Light( int type );
~Light();
Light *getLight(){ return this; }
void setRange( float r );
void setColor( const Vector &v );
void setConeAngles( float inner,float outer );
bool beginRender( float tween );
gxLight *getGxLight()const{ return light; }
private:
friend class World;
gxLight *light;
};
#endif
+34
View File
@@ -0,0 +1,34 @@
#include "std.hpp"
#include "listener.hpp"
extern gxAudio *gx_audio;
Listener::Listener( float roll,float dopp,float dist ){
if( !gx_audio ) return;
gx_audio->set3dOptions( roll,dopp,dist );
renderListener();
}
Listener::Listener( const Listener &t ):
Object(t){
}
Listener::~Listener(){
if( !gx_audio ) return;
Vector pos,vel,up(0,1,1),forward(0,0,1);
gx_audio->set3dListener( &pos.x,&vel.x,&forward.x,&up.x );
}
void Listener::renderListener(){
if( !gx_audio ) return;
const Vector &pos=GetWorldTransform().v;
const Vector &vel=getVelocity();
const Vector &forward=GetWorldTransform().m.k.normalized();
const Vector &up=GetWorldTransform().m.j.normalized();
gx_audio->set3dListener( &pos.x,&vel.x,&forward.x,&up.x );
}
+23
View File
@@ -0,0 +1,23 @@
#ifndef LISTENER_H
#define LISTENER_H
#include "object.hpp"
class Listener : public Object{
public:
Listener( float roll,float dopp,float dist );
Listener( const Listener &t );
~Listener();
//Entity interface
Entity *clone(){ return new Listener( *this ); }
Listener *getListener(){ return this; }
//Listener interface
void renderListener();
private:
};
#endif
+78
View File
@@ -0,0 +1,78 @@
#include "std.hpp"
#include "meshloader.hpp"
#include "meshmodel.hpp"
struct Surf{
vector<Surface::Triangle> tris;
};
static map<Brush,Surf*> brush_map;
static vector<Surface::Vertex> verts;
void MeshLoader::clear(){
map<Brush,Surf*>::const_iterator it;
for( it=brush_map.begin();it!=brush_map.end();++it ){
delete it->second;
}
brush_map.clear();
verts.clear();
}
int MeshLoader::numVertices(){
return verts.size();
}
void MeshLoader::addVertex( const Surface::Vertex &v ){
verts.push_back( v );
}
Surface::Vertex &refVertex( int n ){
return verts[n];
}
void MeshLoader::addTriangle( const int verts[3],const Brush &b ){
addTriangle( verts[0],verts[1],verts[2],b );
}
void MeshLoader::addTriangle( int v0,int v1,int v2,const Brush &b ){
//find surface
Surf *surf;
map<Brush,Surf*>::const_iterator it=brush_map.find( b );
if( it!=brush_map.end() ) surf=it->second;
else{
surf=d_new Surf;
brush_map.insert( make_pair( b,surf ) );
}
Surface::Triangle tri;
tri.verts[0]=v0;tri.verts[1]=v1;tri.verts[2]=v2;
surf->tris.push_back( tri );
}
void MeshLoader::updateMesh( MeshModel *mesh ){
map<int,int> vert_map;
map<Brush,Surf*>::iterator it;
for( it=brush_map.begin();it!=brush_map.end();++it ){
vert_map.clear();
Brush b=it->first;
Surf *t=it->second;
Surface *surf=mesh->findSurface( b );
if( !surf ) surf=mesh->createSurface( b );
for( int k=0;k<t->tris.size();++k ){
for( int j=0;j<3;++j ){
int n=t->tris[k].verts[j],id;
map<int,int>::const_iterator it=vert_map.find( n );
if( it!=vert_map.end() ) id=it->second;
else{
id=surf->numVertices();
surf->addVertex( verts[n] );
vert_map.insert( make_pair( n,id ) );
}
t->tris[k].verts[j]=id;
}
surf->addTriangle( t->tris[k] );
}
}
clear();
}
View File
+521
View File
@@ -0,0 +1,521 @@
#include "std.hpp"
#include "loader_3ds.hpp"
#include "meshmodel.hpp"
#include "animation.hpp"
extern gxRuntime *gx_runtime;
#ifdef BETA
#define _log( X ) gx_runtime->debugLog( (string(X)).c_str() );
#else
#define _log( X )
#endif
class Box;
static filebuf in;
static int chunk_end;
static vector<int> parent_end;
static unsigned short anim_len;
static bool conv,flip_tris;
static Transform conv_tform;
static bool collapse,animonly;
struct Face3DS{
int verts[3];
Brush brush;
};
static vector<Face3DS> faces;
//static vector<Surface::Vertex> vertices;
static map<string,Brush> materials_map;
static map<string,MeshModel*> name_map;
static map<int,MeshModel*> id_map;
static int nextChunk(){
in.pubseekoff( chunk_end,ios_base::beg );
if( chunk_end==parent_end.back() ) return 0;
unsigned short id;int len;
in.sgetn( (char*)&id,2 );
in.sgetn( (char*)&len,4 );
chunk_end=(int)in.pubseekoff( 0,ios_base::cur )+len-6;
return id;
}
static void enterChunk(){
parent_end.push_back( chunk_end );
chunk_end=(int)in.pubseekoff( 0,ios_base::cur );
}
static void leaveChunk(){
chunk_end=parent_end.back();
parent_end.pop_back();
}
static string parseString(){
string t;
while( int c=in.sbumpc() ) t+=char(c);
return t;
}
enum {
CHUNK_RGBF = 0x0010,
CHUNK_RGBB = 0x0011,
// CHUNK_RBGB2 = 0x0012, // ?? NOT HLS.
CHUNK_MAIN = 0x4D4D,
CHUNK_SCENE = 0x3D3D,
CHUNK_BKGCOLOR = 0x1200,
CHUNK_AMBCOLOR = 0x2100,
CHUNK_OBJECT = 0x4000,
CHUNK_TRIMESH = 0x4100,
CHUNK_VERTLIST = 0x4110,
CHUNK_FACELIST = 0x4120,
CHUNK_FACEMAT = 0x4130,
CHUNK_MAPLIST = 0x4140,
CHUNK_SMOOLIST = 0x4150,
CHUNK_TRMATRIX = 0x4160,
CHUNK_LIGHT = 0x4600,
CHUNK_SPOTLIGHT = 0x4610,
CHUNK_CAMERA = 0x4700,
CHUNK_MATERIAL = 0xAFFF,
CHUNK_MATNAME = 0xA000,
CHUNK_AMBIENT = 0xA010,
CHUNK_DIFFUSE = 0xA020,
CHUNK_SPECULAR = 0xA030,
CHUNK_TEXTURE = 0xA200,
CHUNK_BUMPMAP = 0xA230,
CHUNK_MAPFILE = 0xA300,
CHUNK_KEYFRAMER = 0xB000,
CHUNK_MESHINFO = 0xB002,
CHUNK_HIERPOS = 0xB030,
CHUNK_HIERINFO = 0xB010,
CHUNK_FRAMES = 0xB008
};
static Vector parseColor(){
Vector v;
unsigned char rgb[3];
enterChunk();
while( int id=nextChunk() ){
switch( id ){
case CHUNK_RGBF:
in.sgetn( (char*)&v,12 );
break;
case CHUNK_RGBB:
in.sgetn( (char*)rgb,3 );
v=Vector( rgb[0]/255.0f,rgb[1]/255.0f,rgb[2]/255.0f );
}
}
leaveChunk();
return v;
}
static void parseVertList(){
unsigned short cnt;
in.sgetn( (char*)&cnt,2 );
_log( "VertList cnt="+itoa(cnt) );
while( cnt-- ){
Surface::Vertex v;
in.sgetn( (char*)&v.coords,12 );
if( conv ) v.coords=conv_tform * v.coords;
MeshLoader::addVertex( v );
}
}
static void parseFaceMat(){
string name=parseString();
_log( "FaceMat: "+name );
Brush mat=materials_map[name];
unsigned short cnt;
in.sgetn( (char*)&cnt,2 );
while( cnt-- ){
unsigned short face;
in.sgetn( (char*)&face,2 );
faces[face].brush=mat;
}
}
static void parseFaceList(){
unsigned short cnt;
in.sgetn( (char*)&cnt,2 );
_log( "FaceList cnt="+itoa(cnt) );
while( cnt-- ){
unsigned short v[4];
in.sgetn( (char*)v,8 );
Face3DS face;
face.verts[0]=v[0];
face.verts[1]=v[1];
face.verts[2]=v[2];
if( flip_tris ) std::swap( face.verts[1],face.verts[2] );
faces.push_back( face );
}
enterChunk();
while( int id=nextChunk() ){
switch( id ){
case CHUNK_FACEMAT:
parseFaceMat();
break;
}
}
leaveChunk();
}
static void parseMapList(){
_log( "MapList" );
unsigned short cnt;
in.sgetn( (char*)&cnt,2 );
for( int k=0;k<cnt;++k ){
float uv[2];
in.sgetn( (char*)uv,8 );
Surface::Vertex &v=MeshLoader::refVertex( k );
v.tex_coords[0][0]=v.tex_coords[1][0]=uv[0];
v.tex_coords[0][1]=v.tex_coords[1][1]=1-uv[1];
// v->tex_coords[0]=v->tex_coords[1]=Vector( uv[0],1-uv[1],1 );
}
}
static void parseTriMesh( MeshModel *mesh ){
_log( "TriMesh" );
enterChunk();
Transform tform;
faces.clear();
MeshLoader::beginMesh();
while( int id=nextChunk() ){
switch( id ){
case CHUNK_VERTLIST:
if( !animonly ) parseVertList();
break;
case CHUNK_MAPLIST:
if( !animonly ) parseMapList();
break;
case CHUNK_FACELIST:
if( !animonly ) parseFaceList();
break;
case CHUNK_TRMATRIX:
in.sgetn( (char*)&tform,48 );
if( conv ) tform=conv_tform * tform * -conv_tform;
break;
}
}
leaveChunk();
//should really do something here...
// bool neg_x=tform.m.j.cross(tform.m.k).dot(tform.m.i)<0;
int k;
mesh->SetWorldTransform( tform );
if( animonly ){
MeshLoader::endMesh( 0 );
return;
}
Transform inv_tform=-tform;
for( k=0;k<MeshLoader::numVertices();++k ){
Surface::Vertex &v=MeshLoader::refVertex( k );
v.coords=inv_tform * v.coords;
}
for( k=0;k<faces.size();++k ){
const Face3DS &f=faces[k];
MeshLoader::addTriangle( f.verts,f.brush );
}
MeshLoader::endMesh( mesh );
mesh->updateNormals();
faces.clear();
}
static void parseObject( MeshModel *root ){
//skip name
string name=parseString();
_log( "Object:"+name );
MeshModel *mesh=0;
enterChunk();
while( int id=nextChunk() ){
switch( id ){
case CHUNK_TRIMESH:
mesh=new MeshModel();
mesh->SetName( name );
mesh->SetParent( root );
name_map[name]=mesh;
parseTriMesh( mesh );
break;
}
}
leaveChunk();
}
static void parseMaterial(){
_log( "Material" );
Brush mat;
string name,tex_name;
enterChunk();
while( int id=nextChunk() ){
switch( id ){
case CHUNK_MATNAME:
name=parseString();
break;
case CHUNK_DIFFUSE:
mat.setColor( parseColor() );
break;
case CHUNK_AMBIENT:
break;
case CHUNK_SPECULAR:
break;
case CHUNK_TEXTURE:
enterChunk();
while( int id=nextChunk() ){
switch( id ){
case CHUNK_MAPFILE:
tex_name=parseString();
break;
}
}
leaveChunk();
break;
}
}
if( tex_name.size() ){
mat.setTexture( 0,Texture( tex_name,0 ),0 );
mat.setColor( Vector( 1,1,1 ) );
}
if( name.size() ){
materials_map[name]=mat;
}
leaveChunk();
}
static void parseScene( MeshModel *root ){
_log( "Scene" );
enterChunk();
while( int id=nextChunk() ){
switch( id ){
case CHUNK_OBJECT:
parseObject( root );
break;
case CHUNK_MATERIAL:
if( !animonly ) parseMaterial();
break;
}
}
leaveChunk();
}
static void parseAnimKeys( Animation *anim,int type ){
int cnt=0;
short t_flags;
in.sgetn( (char*)&t_flags,2 );
in.pubseekoff( 8,ios_base::cur );
in.sgetn( (char*)&cnt,2 );
in.pubseekoff( 2,ios_base::cur );
_log( "ANIM_TRACK: frames="+itoa( cnt ) );
Vector pos,axis,scale;
float angle;
Quat quat;
for( int k=0;k<cnt;++k ){
int time;
short flags;
in.sgetn( (char*)&time,4 );
in.sgetn( (char*)&flags,2 );
float tens=0,cont=0,bias=0,ease_to=0,ease_from=0;
if( flags & 1 ) in.sgetn( (char*)&tens,4 );
if( flags & 2 ) in.sgetn( (char*)&cont,4 );
if( flags & 4 ) in.sgetn( (char*)&bias,4 );
if( flags & 8 ) in.sgetn( (char*)&ease_to,4 );
if( flags & 16 ) in.sgetn( (char*)&ease_from,4 );
switch( type ){
case 0xb020: //POS_TRACK_TAG
in.sgetn( (char*)&pos,12 );
if( conv ) pos=conv_tform*pos;
// _log( "POS_KEY: time="+itoa(time)+" pos="+ftoa( pos.x )+","+ftoa( pos.y )+","+ftoa( pos.z ) );
if( time<=anim_len ) anim->setPositionKey( time,pos );
break;
case 0xb021: //ROT_TRACK_TAG
in.sgetn( (char*)&angle,4 );
in.sgetn( (char*)&axis,12 );
// _log( "ROT_KEY: time="+itoa(time)+" angle="+ftoa(angle)+" axis="+ftoa(axis.x)+","+ftoa(axis.y)+","+ftoa(axis.z) );
if( axis.length()>FLT_EPSILON ){
if( flip_tris ) angle=-angle;
if( conv ) axis=conv_tform.m*axis;
quat=Quat( cosf( angle/2 ),axis.normalized()*sinf( angle/2 ) )*quat;
quat.normalize();
}
if( time<=anim_len ) anim->setRotationKey( time,quat );
break;
case 0xb022: //SCL_TRACK_TAG
in.sgetn( (char*)&scale,12 );
if( conv ) scale=conv_tform.m*scale;
// scale.x=fabs(scale.x);scale.y=fabs(scale.y);scale.z=fabs(scale.z);
_log( "SCL_KEY: time="+itoa(time)+" scale="+ftoa( scale.x )+","+ftoa( scale.y )+","+ftoa( scale.z ) );
if( time<=anim_len ) anim->setScaleKey( time,scale );
break;
}
}
}
static void parseMeshInfo( MeshModel *root,float curr_time ){
_log( "OBJECT_NODE_TAG" );
enterChunk();
string name,inst;
Vector pivot;
Animation anim;
unsigned short id=65535,parent=65535,flags1,flags2;
Box box = Box( Vector(),Vector() );
Vector box_centre;
while( int chunk_id=nextChunk() ){
switch( chunk_id ){
case 0xb030: //NODE_ID
in.sgetn( (char*)&id,2 );
_log( "NODE_ID: "+itoa(id) );
break;
case 0xb010: //NODE_HDR
name=parseString();
in.sgetn( (char*)&flags1,2 );
in.sgetn( (char*)&flags2,2 );
in.sgetn( (char*)&parent,2 );
_log( "NODE_HDR: name="+name+" parent="+itoa(parent) );
break;
case 0xb011: //INSTANCE NAME
inst=parseString();
_log( "INSTANCE_NAME: "+inst );
break;
case 0xb013: //PIVOT
in.sgetn( (char*)&pivot,12 );
if( conv ) pivot=conv_tform * pivot;
_log( "PIVOT: "+ftoa(pivot.x)+","+ftoa(pivot.y)+","+ftoa(pivot.z) );
break;
case 0xb014: //BOUNDBOX
in.sgetn( (char*)&(box.a),12 );
in.sgetn( (char*)&(box.b),12 );
box_centre=box.centre();
if( conv ) box_centre=conv_tform * box_centre;
_log( "BOUNDBOX: min="+ftoa(box.a.x)+","+ftoa(box.a.y)+","+ftoa(box.a.z)+" max="+ftoa(box.b.x)+","+ftoa(box.b.y)+","+ftoa(box.b.z) );
break;
case 0xb020: //POS_TRACK_TAG
case 0xb021: //ROT_TRACK_TAG
case 0xb022: //SCALE_TRACK_TAG
if( !collapse ) parseAnimKeys( &anim,chunk_id );
break;
}
}
leaveChunk();
MeshModel *p=root;
if( parent!=65535 ){
map<int,MeshModel*>::const_iterator it=id_map.find( parent );
if( it==id_map.end() ) return;
p=it->second;
}
MeshModel *mesh=0;
if( name=="$$$DUMMY" ){
mesh=new MeshModel();
mesh->SetName( inst );
mesh->SetParent( p );
}else{
map<string,MeshModel*>::const_iterator it=name_map.find( name );
if( it==name_map.end() ) return;
mesh=it->second;
name_map.erase( name );
if( pivot!=Vector() ){
mesh->transform( -pivot );
}
Transform t=
mesh->GetWorldTransform();
mesh->SetParent( p );
mesh->SetWorldTransform( t );
}
mesh->setAnimation( anim );
if( id!=65535 ) id_map[id]=mesh;
}
static void parseKeyFramer( MeshModel *root ){
_log( "KeyFramer" );
enterChunk();
string file_3ds;
unsigned short rev,curr_time=0;
while( int id=nextChunk() ){
switch( id ){
case 0xb009: //CURR_TIME
in.sgetn( (char*)&curr_time,2 );
_log( "CURR_TIME: "+itoa(curr_time) );
break;
case 0xb00a: //KFHDR
in.sgetn( (char*)&rev,2 );
file_3ds=parseString();
in.sgetn( (char*)&anim_len,2 );
_log( "KFHDR: revision="+itoa(rev)+" 3dsfile="+file_3ds+" anim_len="+itoa(anim_len) );
break;
case 0xb002: //object keyframer data...
parseMeshInfo( root,curr_time );
break;
}
}
if( !collapse ){
root->setAnimator( new Animator( root,anim_len ) );
}
leaveChunk();
}
static MeshModel *parseFile(){
unsigned short id;int len;
in.sgetn( (char*)&id,2 );
in.sgetn( (char*)&len,4 );
if( id!=CHUNK_MAIN ) return 0;
chunk_end=(int)in.pubseekoff( 0,ios_base::cur )+len-6;
enterChunk();
MeshModel *root=new MeshModel();
while( int id=nextChunk() ){
switch( id ){
case CHUNK_SCENE:
parseScene( root );
break;
case CHUNK_KEYFRAMER:
parseKeyFramer( root );
break;
}
}
leaveChunk();
return root;
}
MeshModel *Loader_3DS::load( const string &filename,const Transform &t,int hint ){
conv_tform=t;
conv=flip_tris=false;
if( conv_tform!=Transform() ){
conv=true;
if( conv_tform.m.i.cross(conv_tform.m.j).dot(conv_tform.m.k)<0 ) flip_tris=true;
}
collapse=!!(hint&MeshLoader::HINT_COLLAPSE);
animonly=!!(hint&MeshLoader::HINT_ANIMONLY);
if( !in.open( filename.c_str(),ios_base::in|ios_base::binary ) ){
return 0;
}
MeshModel *root=parseFile();
in.close();
materials_map.clear();
name_map.clear();
id_map.clear();
return root;
}
+12
View File
@@ -0,0 +1,12 @@
#ifndef LOADER_3DS_H
#define LOADER_3DS_H
#include "meshloader.hpp"
class Loader_3DS : public MeshLoader{
public:
MeshModel *load( const string &f,const Transform &conv,int hint );
};
#endif
+360
View File
@@ -0,0 +1,360 @@
#include "std.hpp"
#include "loader_b3d.hpp"
#include "meshmodel.hpp"
#include "pivot.hpp"
#include "meshutil.hpp"
//#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=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=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=new MeshModel();
readObject( obj );
break;
}
exitChunk();
}
if( !obj ) obj=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( new Animator( bones,anim_len ) );
mesh->createBones();
bones.clear();
}else if( anim_len ){
obj->setAnimator( 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;
}
+12
View File
@@ -0,0 +1,12 @@
#ifndef LOADER_B3D_H
#define LOADER_B3D_H
#include "meshloader.hpp"
class Loader_B3D : public MeshLoader{
public:
MeshModel *load( const string &f,const Transform &conv,int hint );
};
#endif
+389
View File
@@ -0,0 +1,389 @@
#include "std.hpp"
#include "loader_x.hpp"
#include "meshmodel.hpp"
#include "animation.hpp"
#include "pivot.hpp"
#include <windows.h>
//#include <dxfile.h>
#include "../gxruntime/GraphicsRuntime.hpp"
#include <d3d9types.h>
#include <d3dx9xof.h>
#include <rmxfguid.h>
#include <rmxftmpl.h>
extern gxRuntime *gx_runtime;
static map<string,MeshModel*> frames_map;
static int anim_len;
static bool conv,flip_tris;
static Transform conv_tform;
static bool collapse,animonly;
static void parseAnimKey(ID3DXFileData *fileData, MeshModel *e) {
DWORD sz;int *data;
if( fileData->GetData( 0,&sz,(void**)&data )<0 ) return;
int type=*data++;
int cnt=*data++;
Animation anim=e->getAnimation();
for( int k=0;k<cnt;++k ){
int time=*data++;
int n=*data++;
if( time>anim_len ) anim_len=time;
switch( type ){
case 0:
if( n==4 ){
Quat rot=*(Quat*)data;
if( conv ){
if( fabs(rot.w)<1-FLT_EPSILON ){
rot.normalize();
//quat-to-axis/angle
float half=acosf( rot.w );
if( flip_tris ) half=-half;
rot=Quat( cosf( half ),(conv_tform.m*rot.v).normalized()*sinf( half ) );
}else rot=Quat();
}
anim.setRotationKey( time,rot );
}
break;
case 1:
if( n==3 ){
Vector scl=*(Vector*)data;
if( conv ) scl=conv_tform.m * scl;
scl.x=fabs(scl.x);scl.y=fabs(scl.y);scl.z=fabs(scl.z);
anim.setScaleKey( time,scl );
}
break;
case 2:
if( n==3 ){
Vector pos=*(Vector*)data;
if( conv ) pos=conv_tform*pos;
anim.setPositionKey( time,pos );
}
break;
}
data+=n;
}
e->setAnimation( anim );
}
static void parseAnim(ID3DXFileData *fileData) {
const GUID *guid;
IDirectXFileObject *childObj;
IDirectXFileData *childData;
IDirectXFileDataReference *childRef;
MeshModel *frame=0;
//find the frame reference
for( ;fileData->GetNextObject( &childObj )>=0;childObj->Release() ){
if( childObj->QueryInterface( IID_IDirectXFileDataReference,(void**)&childRef )>=0 ){
if( childRef->Resolve( &childData )>=0 ){
if( childData->GetType( &guid )>=0 ){
if( *guid==TID_D3DRMFrame ){
char name[80];DWORD len=80;
if( childData->GetName( name,&len )>=0 ){
map<string,MeshModel*>::iterator it=frames_map.find( name );
if( it!=frames_map.end() ) frame=it->second;
}
}
}
childData->Release();
}
childRef->Release();
}else if( frame && childObj->QueryInterface( IID_IDirectXFileData,(void**)&childData )>=0 ){
if( childData->GetType( &guid )>=0 ){
if( *guid==TID_D3DRMAnimationKey ){
parseAnimKey( childData,frame );
}
}
childData->Release();
}
}
}
static void parseAnimSet( IDirectXFileData *fileData ){
const GUID *guid;
IDirectXFileObject *childObj;
IDirectXFileData *childData;
for( ;fileData->GetNextObject( &childObj )>=0;childObj->Release() ){
if( childObj->QueryInterface( IID_IDirectXFileData,(void**)&childData )<0 ) continue;
if( childData->GetType( &guid )>=0 ){
if( *guid==TID_D3DRMAnimation ){
parseAnim( childData );
}
}
childData->Release();
}
}
static Brush parseMaterial( IDirectXFileData *fileData ){
const GUID *guid;
IDirectXFileObject *childObj;
IDirectXFileData *childData;
Brush brush;
DWORD sz;float *data;
if( fileData->GetData( 0,&sz,(void**)&data )<0 ) return brush;
brush.setColor( Vector( data[0],data[1],data[2] ) );
if( data[3] ) brush.setAlpha( data[3] );
for( ;fileData->GetNextObject( &childObj )>=0;childObj->Release() ){
if( childObj->QueryInterface( IID_IDirectXFileData,(void**)&childData )<0 ) continue;
if( childData->GetType( &guid )>=0 ){
if( *guid==TID_D3DRMTextureFilename ){
DWORD sz;char **data;
if( childData->GetData( 0,&sz,(void**)&data )>=0 ){
brush.setTexture( 0,Texture( *data,0 ),0 );
brush.setColor( Vector( 1,1,1 ) );
}
}
}
childData->Release();
}
return brush;
}
static void parseMaterialList( IDirectXFileData *fileData,vector<Brush> &mats ){
const GUID *guid;
IDirectXFileObject *childObj;
IDirectXFileData *childData;
IDirectXFileDataReference *childRef;
//iterate through child objects...
for( ;fileData->GetNextObject( &childObj )>=0;childObj->Release() ){
if( childObj->QueryInterface( IID_IDirectXFileData,(void**)&childData )>=0 ){
if( childData->GetType( &guid )>=0 ){
if( *guid==TID_D3DRMMaterial ){
mats.push_back( parseMaterial( childData ) );
}
}
childData->Release();
}else if( childObj->QueryInterface( IID_IDirectXFileDataReference,(void**)&childRef )>=0 ){
if( childRef->Resolve( &childData )>=0 ){
if( childData->GetType( &guid )>=0 ){
if( *guid==TID_D3DRMMaterial ){
mats.push_back( parseMaterial( childData ) );
}
}
childData->Release();
}
childRef->Release();
}
}
}
struct FaceX{
int *data,mat_index;
FaceX( int *d ):data(d),mat_index(0){}
};
static void parseMesh( IDirectXFileData *fileData,MeshModel *mesh ){
const GUID *guid;
IDirectXFileObject *childObj;
IDirectXFileData *childData;
DWORD sz;int *data;
if( fileData->GetData( 0,&sz,(void**)&data )<0 ) return;
//stuff...
vector<FaceX> faces;
vector<Brush> mats;
MeshLoader::beginMesh();
//setup vertices
int num_verts=*data++;
int k;
for( k=0;k<num_verts;++k ){
Surface::Vertex v;
v.coords=*(Vector*)data;
if( conv ) v.coords=conv_tform * v.coords;
v.color=0xffffffff;//Vector(1,1,1);
MeshLoader::addVertex( v );
data+=3;
}
//setup faces
int num_faces=*data++;
for( k=0;k<num_faces;++k ){
faces.push_back( FaceX( data ) );
data+=*data+1;
}
bool normals=false;
//get material and texture info
for( ;fileData->GetNextObject( &childObj )>=0;childObj->Release() ){
if( childObj->QueryInterface( IID_IDirectXFileData,(void**)&childData )<0 ) continue;
if( childData->GetType( &guid )>=0 ){
DWORD sz;int *data;
if( childData->GetData( 0,&sz,(void**)&data )>=0 ){
if( *guid==TID_D3DRMMeshMaterialList ){
int num_mats=*data++;
int num_faces=*data++;
for( int k=0;k<num_faces;++k ){
faces[k].mat_index=*data++;
}
parseMaterialList( childData,mats );
}else if( *guid==TID_D3DRMMeshTextureCoords ){
int num_coords=*data++;
if( num_coords==num_verts ){
float *coords=(float*)data;
for( int k=0;k<num_coords;++k ){
Surface::Vertex &v=MeshLoader::refVertex(k);
float tu=*coords++;float tv=*coords++;
v.tex_coords[0][0]=v.tex_coords[1][0]=tu;
v.tex_coords[0][1]=v.tex_coords[1][1]=tv;
}
}
}else if( *guid==TID_D3DRMMeshVertexColors ){
int num_colors=*data++;
if( num_colors==num_verts ){
for( int k=0;k<num_colors;++k ){
Surface::Vertex &v=MeshLoader::refVertex(*data++);
float *t=(float*)data;
v.color=0xff000000|(int(t[0]*255)<<16)|(int(t[1]*255)<<8)|int(t[2]*255);
// v.color=Vector( t[0],t[1],t[2] );
data+=4;
}
}
}else if( *guid==TID_D3DRMMeshNormals ){
int num_normals=*data++;
if( num_normals==num_verts ){
Matrix co=conv_tform.m.cofactor();
for( int k=0;k<num_normals;++k ){
Surface::Vertex &v=MeshLoader::refVertex(k);
v.normal=(co * *(Vector*)data).normalized();
data+=3;
}
normals=true;
}
}
}
}
childData->Release();
}
if( !mats.size() ) mats.push_back( Brush() );
for( size_t k=0;k<faces.size();++k ){
const FaceX &f=faces[k];
int *data=f.data;
int cnt=*data++;if( cnt<3 ) continue;
int tri[3];
tri[0]=data[0];
for( int j=2;j<cnt;++j ){
tri[1]=data[j-1+flip_tris];
tri[2]=data[j-flip_tris];
MeshLoader::addTriangle( tri,mats[f.mat_index] );
}
}
MeshLoader::endMesh( mesh );
if( !normals ) mesh->updateNormals();
}
static MeshModel *parseFrame( IDirectXFileData *fileData ){
MeshModel *e=new MeshModel();
const GUID *guid;
IDirectXFileObject *childObj;
IDirectXFileData *childData;
char name[80];DWORD len=80;
if( fileData->GetName( name,&len )<0 ) return e;
e->SetName( name );
frames_map[name]=e;
//iterate through child objects...
for( ;fileData->GetNextObject( &childObj )>=0;childObj->Release() ){
if( childObj->QueryInterface( IID_IDirectXFileData,(void**)&childData )<0 ) continue;
if( childData->GetType( &guid )>=0 ){
if( *guid==TID_D3DRMFrameTransformMatrix ){
DWORD size;D3DMATRIX *data;
if( childData->GetData( 0,&size,(void**)&data )>=0 ){
Transform tform=Transform( Matrix(
Vector( data->_11,data->_12,data->_13 ),
Vector( data->_21,data->_22,data->_23 ),
Vector( data->_31,data->_32,data->_33 ) ),
Vector( data->_41,data->_42,data->_43 ) );
if( conv ) tform=conv_tform * tform * -conv_tform;
e->SetLocalTransform( tform );
}
}else if( *guid==TID_D3DRMMesh ){
if( !animonly ) parseMesh( childData,e );
}else if( *guid==TID_D3DRMFrame ){
MeshModel *t=parseFrame( childData );
t->SetParent( e );
}
}
childData->Release();
}
return e;
}
static MeshModel *parseFile( const string &file ){
const GUID *guid;
IDirectXFile *xfile;
IDirectXFileData *fileData;
IDirectXFileEnumObject *enumObj;
if( DirectXFileCreate( &xfile )<0 ) return 0;
if( xfile->RegisterTemplates( (VOID*)D3DRM_XTEMPLATES,D3DRM_XTEMPLATE_BYTES )<0 ){
xfile->Release();return 0;
}
if( xfile->CreateEnumObject( (void*)file.c_str(),DXFILELOAD_FROMFILE,&enumObj )<0 ){
xfile->Release();return 0;
}
anim_len=0;
MeshModel *e=new MeshModel();
for( ;enumObj->GetNextDataObject( &fileData )>=0;fileData->Release() ){
if( fileData->GetType( &guid )<0 ) continue;
if( *guid==TID_D3DRMMesh ){
if( !animonly) parseMesh( fileData,e );
}else if( *guid==TID_D3DRMFrame ){
MeshModel *t=parseFrame( fileData );
t->SetParent( e );
}else if( *guid==TID_D3DRMAnimationSet ){
if( !collapse ) parseAnimSet( fileData );
}
}
if( !collapse ){
e->setAnimator( new Animator( e,anim_len ) );
}
enumObj->Release();
xfile->Release();
return e;
}
MeshModel *Loader_X::load( const string &filename,const Transform &t,int hint ){
conv_tform=t;
conv=flip_tris=false;
if( conv_tform!=Transform() ){
conv=true;
if( conv_tform.m.i.cross(conv_tform.m.j).dot(conv_tform.m.k)<0 ) flip_tris=true;
}
collapse=!!(hint&MeshLoader::HINT_COLLAPSE);
animonly=!!(hint&MeshLoader::HINT_ANIMONLY);
MeshModel *e=parseFile( filename );
frames_map.clear();
return e;
}
+12
View File
@@ -0,0 +1,12 @@
#ifndef LOADER_X_H
#define LOADER_X_H
#include "meshloader.hpp"
class Loader_X : public MeshLoader{
public:
MeshModel *load( const string &f,const Transform &conv,int hint );
};
#endif
+129
View File
@@ -0,0 +1,129 @@
#include "std.hpp"
#include "md2rep.hpp"
#include "md2model.hpp"
struct MD2Model::Rep : public MD2Rep{
int ref_cnt;
Rep( const string &f):MD2Rep( f ),
ref_cnt(1){
}
};
MD2Model::MD2Model( const string &f ):
rep( new Rep( f ) ),
anim_mode(0),anim_time(0),
render_a(0),render_b(0),render_t(0),trans_verts(0){
}
MD2Model::MD2Model( const MD2Model &t ):
Model(t),rep( t.rep ),
anim_mode(0),anim_time(0),
render_a(0),render_b(0),render_t(0),trans_verts(0){
++rep->ref_cnt;
}
MD2Model::~MD2Model(){
if( !--rep->ref_cnt ) delete rep;
if( trans_verts ) delete[] trans_verts;
}
void MD2Model::startMD2Anim( int first,int last,int mode,float speed,float trans ){
if( last<first ) std::swap( first,last );
if( first<0 ) first=0;
else if( first>=rep->numFrames() ) first=rep->numFrames()-1;
if( last<0 ) last=0;
else if( last>=rep->numFrames() ) last=rep->numFrames()-1;
if( trans>0 ){
if( !trans_verts ) trans_verts=new MD2Rep::Vert[rep->numVertices()];
if( anim_mode & 0x8000 ) rep->render( trans_verts,anim_time,trans_time );
else rep->render( trans_verts,render_a,render_b,render_t );
trans_speed=1.0f/trans;
trans_time=0;
mode|=0x8000;
}
anim_first=first;
anim_last=last;
anim_len=last-first;
anim_speed=speed;
anim_time=((mode&0x7fff)==Animator::ANIM_MODE_LOOP || anim_speed>=0) ? anim_first : anim_last;
anim_mode=mode;
if( !anim_speed || !anim_len ){
render_a=render_b=anim_time;
render_t=0;
anim_mode&=0x8000;
}
}
void MD2Model::animate( float e ){
Model::animate( e );
if( !anim_mode ) return;
if( anim_mode & 0x8000 ){
trans_time+=trans_speed;
if( trans_time<1 ) return;
anim_mode&=~0x8000;
if( !anim_mode ) return;
}
anim_time=anim_time+anim_speed * e;
if( anim_time<anim_first ){
switch( anim_mode ){
case Animator::ANIM_MODE_LOOP:
anim_time+=anim_len;
break;
case Animator::ANIM_MODE_PINGPONG:
anim_time=anim_first+(anim_first-anim_time);
anim_speed=-anim_speed;
break;
default:
anim_time=anim_first;
anim_mode=0;
break;
}
}else if( anim_time>=anim_last ){
switch( anim_mode ){
case Animator::ANIM_MODE_LOOP:
anim_time-=anim_len;
break;
case Animator::ANIM_MODE_PINGPONG:
anim_time=anim_last-(anim_time-anim_last);
anim_speed=-anim_speed;
break;
default:
anim_time=anim_last;
anim_mode=0;
break;
}
}
render_a=floor(anim_time);render_b=render_a+1;
if( anim_mode==Animator::ANIM_MODE_LOOP && render_b==anim_last ) render_b=anim_first;
render_t=anim_time-render_a;
}
bool MD2Model::render( const RenderContext &rc ){
static Frustum f;
new( &f ) Frustum( rc.getWorldFrustum(),-getRenderTform() );
if( !f.cull( rep->getBox() ) ) return false;
if( anim_mode & 0x8000 ){
rep->render( this,trans_verts,anim_time,trans_time );
}else{
rep->render( this,render_a,render_b,render_t );
}
return false;
}
int MD2Model::getMD2AnimLength()const{
return rep->numFrames();
}
bool MD2Model::getValid()const{
return rep->numFrames()>0;
}
+51
View File
@@ -0,0 +1,51 @@
#ifndef MD2MODEL_H
#define MD2MODEL_H
#include "model.hpp"
#include "md2rep.hpp"
class MD2Model : public Model{
public:
MD2Model( const string &filename );
MD2Model( const MD2Model &t );
~MD2Model();
//Entity interface
Entity *clone(){ return new MD2Model( *this ); }
MD2Model *getMD2Model(){ return this; }
//Object interface
void animate( float elapsed );
//Model interface
bool render( const RenderContext &rc );
//MD2 interface
void startMD2Anim( int first,int last,int mode,float speed,float trans );
int getMD2AnimLength()const;
bool getMD2Animating()const{ return !!anim_mode; }
float getMD2AnimTime()const{ return anim_time; }
bool getValid()const;
private:
struct Rep;
Rep *rep;
int anim_mode;
float anim_time,anim_speed;
int anim_first,anim_last,anim_len;
float render_t;
int render_a,render_b;
float trans_time,trans_speed;
MD2Rep::Vert *trans_verts;
//Unimplemented
MD2Model &operator=( const MD2Model & );
};
#endif
+473
View File
@@ -0,0 +1,473 @@
#include "std.h"
#include "md2model.h"
#include "md2norms.h"
static Vector *normals;
static float white[]={1,1,1};
extern gxRuntime *gx_runtime;
extern gxGraphics *gx_graphics;
struct MD2Model::Rep{
struct md2_header{
int magic;
int version;
int skinWidth;
int skinHeight;
int frameSize;
int numSkins;
int numVertices;
int numTexCoords;
int numTriangles;
int numGlCommands;
int numFrames;
int offsetSkins;
int offsetTexCoords;
int offsetTriangles;
int offsetFrames;
int offsetGlCommands;
int offsetEnd;
};
struct md2_vertex{
unsigned char x,y,z,n;
};
struct md2_texcoord{
unsigned short s,t;
};
struct md2_triangle{
unsigned short verts[3],tex_coords[3];
};
struct Frame{
Vector scale,trans;
vector<md2_vertex> verts;
};
struct TexCoords{
float u,v;
};
int ref_cnt;
int num_verts,num_frames;
vector<Frame> frames;
vector<TexCoords> tex_coords;
gxMesh *mesh;
Box box;
ModelModel::Rep( const string &file ):
ref_cnt(1),mesh(0){
filebuf in;
Header header;
if( !in.open( file.c_str(),ios_base::in|ios_base::binary ) ) return;
if( in.sgetn( (char*)&header,sizeof(header) )!=sizeof(header) ) return;
if( header.magic!='2PDI' || header.version!=8 ) return;
//read tex coords
in.pubseekpos( header.offsetTexCoords );
TexCoord *coords=d_new TexCoord[header.numTexCoords];
in.sgetn( (char*)coords,header.numTexCoords*sizeof(TexCoord) );
vector<VertInfo> verts;
map<VertInfo,int> info_map;
//build triangles
vector<Triangle> triangles;
in.pubseekpos( header.offsetTriangles );
triangles.resize( header.numTriangles );
for( k=0;k<triangles.size();++k ){
unsigned short v[3],t[3];
in.sgetn( (char*)v,6 );
in.sgetn( (char*)t,6 );
for( int j=0;j<3;++j ){
unsigned char tu=coords[ t[j] ].s*256.0f/header.skinWidth;
unsigned char tv=coords[ t[j] ].t*256.0f/header.skinHeight;
VertInfo i( v[j],tu,tv );
map<VertInfo,int>::iterator it=info_map.find( i );
if( it==info_map.end() ){
info_map[i]=triangles[k].v[j]=verts.size();
verts.push_back( i );
}else{
triangles[k].v[j]=it->second;
}
}
}
delete coords;
//load frames
string tt="MD2 Frames:"+itoa( header.numFrames );
gx_runtime->debugLog( tt.c_str() );
in.pubseekpos( header.offsetFrames );
frames.resize( header.numFrames );
MD2Vertex *md2_verts=d_new MD2Vertex[header.numVertices];
for( k=0;k<frames.size();++k ){
Frame &frame=frames[k];
//read frame header;
in.sgetn( (char*)&frame.scale,12 );
in.sgetn( (char*)&frame.trans,12 );
in.sgetn( frame.name,16 );
frame.trans=Vector(frame.trans.y,frame.trans.z,frame.trans.x);
frame.scale=Vector(frame.scale.y,frame.scale.z,frame.scale.x);
//read frame verts...
in.sgetn( (char*)md2_verts,header.numVertices*4 );
frame.vertices.resize( verts.size() );
for( int j=0;j<verts.size();++j ){
Vertex &v=frame.vertices[j];
const VertInfo &i=verts[j];
const MD2Vertex &m=md2_verts[i.index];
v.x=m.y;v.y=m.z;v.z=m.x;
v.u=i.u;v.v=i.v;v.n=m.n;
}
}
delete md2_verts;
//create initial mesh
mesh=gx_graphics->createMesh( verts.size(),triangles.size(),0 );
mesh->lock();
for( k=0;k<triangles.size();++k ){
const Triangle &t=triangles[k];
mesh->setTriangle( k,t.v[0],t.v[2],t.v[1] );
}
mesh->unlock();
//calculate bounding box.
for( k=0;k<header.numFrames;++k ){
const Frame &frame=frames[k];
const Vector &scale=frame.scale;
const Vector &trans=frame.trans;
for( int n=0;n<frame.vertices.size();++n ){
const Vertex &v=frame.vertices[n];
box.update( Vector( v.x,v.y,v.z ) * scale + trans );
}
}
if( !normals ){
normals=(Vector*)md2norms;
for( int k=0;k<sizeof(md2norms)/12;++k ){
normals[k]=Vector(normals[k].y,normals[k].z,normals[k].x);
}
}
#pragma pack( push,1 )
struct Header{
int magic;
int version;
int skinWidth;
int skinHeight;
int frameSize;
int numSkins;
int numVertices;
int numTexCoords;
int numTriangles;
int numGlCommands;
int numFrames;
int offsetSkins;
int offsetTexCoords;
int offsetTriangles;
int offsetFrames;
int offsetGlCommands;
int offsetEnd;
};
struct MD2Vertex{
unsigned char x,y,z,n;
};
struct Vertex{
unsigned char x,y,z,u,v,n;
};
struct Triangle{
unsigned short v[3]; //index into vertices
};
struct Frame{
Vector scale;
Vector trans;
char name[16];
vector<Vertex> vertices;
};
struct TexCoord{
short s,t;
};
struct VertInfo{
unsigned short index;
unsigned char u,v;
VertInfo( unsigned short i,char u,char v ):index(i),u(u),v(v){
}
bool operator<( const VertInfo &t )const{
if( index<t.index ) return true;
if( t.index<index ) return false;
if( u<t.u ) return true;
if( t.u<u ) return false;
return v<t.v;
}
};
#pragma pack( pop )
int ref_cnt;
Header header;
vector<Frame> frames;
gxMesh *mesh;
Box box;
Rep( const string &file );
~Rep();
void render( MD2Model *model,float render_t,int render_a,int render_b );
};
MD2Model::Rep::Rep( const string &file ):
ref_cnt(1),mesh(0){
filebuf in;
if( !in.open( file.c_str(),ios_base::in|ios_base::binary ) ){
return;
}
if( in.sgetn( (char*)&header,sizeof(header) )!=sizeof(header) ){
return;
}
if( header.magic!='2PDI' || header.version!=8 ){
return;
}
int k;
//read tex coords
in.pubseekpos( header.offsetTexCoords );
TexCoord *coords=d_new TexCoord[header.numTexCoords];
in.sgetn( (char*)coords,header.numTexCoords*sizeof(TexCoord) );
vector<VertInfo> verts;
map<VertInfo,int> info_map;
//build triangles
vector<Triangle> triangles;
in.pubseekpos( header.offsetTriangles );
triangles.resize( header.numTriangles );
for( k=0;k<triangles.size();++k ){
unsigned short v[3],t[3];
in.sgetn( (char*)v,6 );
in.sgetn( (char*)t,6 );
for( int j=0;j<3;++j ){
unsigned char tu=coords[ t[j] ].s*256.0f/header.skinWidth;
unsigned char tv=coords[ t[j] ].t*256.0f/header.skinHeight;
VertInfo i( v[j],tu,tv );
map<VertInfo,int>::iterator it=info_map.find( i );
if( it==info_map.end() ){
info_map[i]=triangles[k].v[j]=verts.size();
verts.push_back( i );
}else{
triangles[k].v[j]=it->second;
}
}
}
delete coords;
//load frames
string tt="MD2 Frames:"+itoa( header.numFrames );
gx_runtime->debugLog( tt.c_str() );
in.pubseekpos( header.offsetFrames );
frames.resize( header.numFrames );
MD2Vertex *md2_verts=d_new MD2Vertex[header.numVertices];
for( k=0;k<frames.size();++k ){
Frame &frame=frames[k];
//read frame header;
in.sgetn( (char*)&frame.scale,12 );
in.sgetn( (char*)&frame.trans,12 );
in.sgetn( frame.name,16 );
frame.trans=Vector(frame.trans.y,frame.trans.z,frame.trans.x);
frame.scale=Vector(frame.scale.y,frame.scale.z,frame.scale.x);
//read frame verts...
in.sgetn( (char*)md2_verts,header.numVertices*4 );
frame.vertices.resize( verts.size() );
for( int j=0;j<verts.size();++j ){
Vertex &v=frame.vertices[j];
const VertInfo &i=verts[j];
const MD2Vertex &m=md2_verts[i.index];
v.x=m.y;v.y=m.z;v.z=m.x;
v.u=i.u;v.v=i.v;v.n=m.n;
}
}
delete md2_verts;
//create initial mesh
mesh=gx_graphics->createMesh( verts.size(),triangles.size(),0 );
mesh->lock();
for( k=0;k<triangles.size();++k ){
const Triangle &t=triangles[k];
mesh->setTriangle( k,t.v[0],t.v[2],t.v[1] );
}
mesh->unlock();
//calculate bounding box.
for( k=0;k<header.numFrames;++k ){
const Frame &frame=frames[k];
const Vector &scale=frame.scale;
const Vector &trans=frame.trans;
for( int n=0;n<frame.vertices.size();++n ){
const Vertex &v=frame.vertices[n];
box.update( Vector( v.x,v.y,v.z ) * scale + trans );
}
}
if( !normals ){
normals=(Vector*)md2norms;
for( int k=0;k<sizeof(md2norms)/12;++k ){
normals[k]=Vector(normals[k].y,normals[k].z,normals[k].x);
}
}
}
MD2Model::Rep::~Rep(){
if( mesh ) gx_graphics->freeMesh( mesh );
}
void MD2Model::Rep::render( MD2Model *model,float render_t,int render_a,int render_b ){
const Frame &frame_a=frames[render_a];
const Vector &scale_a=frame_a.scale;
const Vector &trans_a=frame_a.trans;
const Frame &frame_b=frames[render_b];
const Vector &scale_b=frame_b.scale;
const Vector &trans_b=frame_b.trans;
mesh->lock();
int k;
for( k=0;k<frame_a.vertices.size();++k ){
const Vertex &v_a=frame_a.vertices[k];
const Vector &n_a=normals[v_a.n];
Vector t_a( v_a.x*scale_a.x+trans_a.x,v_a.y*scale_a.y+trans_a.y,v_a.z*scale_a.z+trans_a.z );
const Vertex &v_b=frame_b.vertices[k];
const Vector &n_b=normals[v_b.n];
Vector t_b( v_b.x*scale_b.x+trans_b.x,v_b.y*scale_b.y+trans_b.y,v_b.z*scale_b.z+trans_b.z );
Vector t=(t_b-t_a)*render_t+t_a;
Vector n=(n_b-n_a)*render_t+n_a;
float tex_coords[]={ v_a.u/256.0f,v_a.v/256.0f,1,0,0,1 };
mesh->setVertex( k,&t.x,&n.x,white,tex_coords );
}
mesh->unlock();
model->enqueue( mesh,0,frame_a.vertices.size(),0,header.numTriangles );
}
MD2Model::MD2Model( const string &f ):
rep( d_new Rep( f ) ),
anim_mode(0),anim_time(0),
render_a(0),render_b(0),render_t(0){
}
MD2Model::MD2Model( const MD2Model &t ):
Model(t),rep( t.rep ),
anim_mode(0),anim_time(0),
render_a(0),render_b(0),render_t(0){
++rep->ref_cnt;
}
MD2Model::~MD2Model(){
if( !--rep->ref_cnt ) delete rep;
}
void MD2Model::startMD2Anim( int first,int last,int mode,float speed ){
if( !speed && !mode ){ anim_mode=0;return; }
if( first<0 ) first=0;
else if( first>=rep->header.numFrames ) first=rep->header.numFrames-1;
if( last<0 ) last=0;
else if( last>=rep->header.numFrames ) last=rep->header.numFrames-1;
if( first==last ){ anim_mode=0;render_a=render_b=first;render_t=0;return; }
if( last<first ) std::swap( first,last );
anim_first=first;
anim_last=last;
anim_len=last-first;
anim_speed=speed;
anim_time=speed>0 ? first : last;
anim_mode=mode;
}
void MD2Model::animate( float e ){
Model::animate( e );
if( !anim_mode ) return;
anim_time=anim_time+anim_speed * e;
if( anim_time<anim_first ){
switch( anim_mode ){
case ANIM_MODE_LOOP:
anim_time+=anim_len;
break;
case ANIM_MODE_PINGPONG:
anim_time=anim_first+(anim_first-anim_time);
anim_speed=-anim_speed;
break;
default:
anim_time=anim_first;
anim_mode=0;
break;
}
}else if( anim_time>=anim_last ){
switch( anim_mode ){
case ANIM_MODE_LOOP:
anim_time-=anim_len;
break;
case ANIM_MODE_PINGPONG:
anim_time=anim_last-(anim_time-anim_last);
anim_speed=-anim_speed;
break;
default:
anim_time=anim_last;
anim_mode=0;
break;
}
}
render_a=floor(anim_time);render_b=render_a+1;
if( anim_mode==ANIM_MODE_LOOP && render_b==anim_last ) render_b=anim_first;
render_t=anim_time-render_a;
}
void MD2Model::render( const RenderContext &rc ){
static Frustum f;
new( &f ) Frustum( rc.getWorldFrustum(),-getRenderTform() );
if( !f.cull( rep->box ) ) return;
rep->render( this,render_t,render_a,render_b );
}
int MD2Model::getMD2AnimLength()const{
return rep->frames.size();
}
bool MD2Model::getValid()const{
return rep->mesh!=0;
}
+167
View File
@@ -0,0 +1,167 @@
#include "std.hpp"
#include "md2norms.hpp"
float md2norms[162][3]={
{-0.525731f, 0.000000f, 0.850651f},
{-0.442863f, 0.238856f, 0.864188f},
{-0.295242f, 0.000000f, 0.955423f},
{-0.309017f, 0.500000f, 0.809017f},
{-0.162460f, 0.262866f, 0.951056f},
{0.000000f, 0.000000f, 1.000000f},
{0.000000f, 0.850651f, 0.525731f},
{-0.147621f, 0.716567f, 0.681718f},
{0.147621f, 0.716567f, 0.681718f},
{0.000000f, 0.525731f, 0.850651f},
{0.309017f, 0.500000f, 0.809017f},
{0.525731f, 0.000000f, 0.850651f},
{0.295242f, 0.000000f, 0.955423f},
{0.442863f, 0.238856f, 0.864188f},
{0.162460f, 0.262866f, 0.951056f},
{-0.681718f, 0.147621f, 0.716567f},
{-0.809017f, 0.309017f, 0.500000f},
{-0.587785f, 0.425325f, 0.688191f},
{-0.850651f, 0.525731f, 0.000000f},
{-0.864188f, 0.442863f, 0.238856f},
{-0.716567f, 0.681718f, 0.147621f},
{-0.688191f, 0.587785f, 0.425325f},
{-0.500000f, 0.809017f, 0.309017f},
{-0.238856f, 0.864188f, 0.442863f},
{-0.425325f, 0.688191f, 0.587785f},
{-0.716567f, 0.681718f, -0.147621f},
{-0.500000f, 0.809017f, -0.309017f},
{-0.525731f, 0.850651f, 0.000000f},
{0.000000f, 0.850651f, -0.525731f},
{-0.238856f, 0.864188f, -0.442863f},
{0.000000f, 0.955423f, -0.295242f},
{-0.262866f, 0.951056f, -0.162460f},
{0.000000f, 1.000000f, 0.000000f},
{0.000000f, 0.955423f, 0.295242f},
{-0.262866f, 0.951056f, 0.162460f},
{0.238856f, 0.864188f, 0.442863f},
{0.262866f, 0.951056f, 0.162460f},
{0.500000f, 0.809017f, 0.309017f},
{0.238856f, 0.864188f, -0.442863f},
{0.262866f, 0.951056f, -0.162460f},
{0.500000f, 0.809017f, -0.309017f},
{0.850651f, 0.525731f, 0.000000f},
{0.716567f, 0.681718f, 0.147621f},
{0.716567f, 0.681718f, -0.147621f},
{0.525731f, 0.850651f, 0.000000f},
{0.425325f, 0.688191f, 0.587785f},
{0.864188f, 0.442863f, 0.238856f},
{0.688191f, 0.587785f, 0.425325f},
{0.809017f, 0.309017f, 0.500000f},
{0.681718f, 0.147621f, 0.716567f},
{0.587785f, 0.425325f, 0.688191f},
{0.955423f, 0.295242f, 0.000000f},
{1.000000f, 0.000000f, 0.000000f},
{0.951056f, 0.162460f, 0.262866f},
{0.850651f, -0.525731f, 0.000000f},
{0.955423f, -0.295242f, 0.000000f},
{0.864188f, -0.442863f, 0.238856f},
{0.951056f, -0.162460f, 0.262866f},
{0.809017f, -0.309017f, 0.500000f},
{0.681718f, -0.147621f, 0.716567f},
{0.850651f, 0.000000f, 0.525731f},
{0.864188f, 0.442863f, -0.238856f},
{0.809017f, 0.309017f, -0.500000f},
{0.951056f, 0.162460f, -0.262866f},
{0.525731f, 0.000000f, -0.850651f},
{0.681718f, 0.147621f, -0.716567f},
{0.681718f, -0.147621f, -0.716567f},
{0.850651f, 0.000000f, -0.525731f},
{0.809017f, -0.309017f, -0.500000f},
{0.864188f, -0.442863f, -0.238856f},
{0.951056f, -0.162460f, -0.262866f},
{0.147621f, 0.716567f, -0.681718f},
{0.309017f, 0.500000f, -0.809017f},
{0.425325f, 0.688191f, -0.587785f},
{0.442863f, 0.238856f, -0.864188f},
{0.587785f, 0.425325f, -0.688191f},
{0.688191f, 0.587785f, -0.425325f},
{-0.147621f, 0.716567f, -0.681718f},
{-0.309017f, 0.500000f, -0.809017f},
{0.000000f, 0.525731f, -0.850651f},
{-0.525731f, 0.000000f, -0.850651f},
{-0.442863f, 0.238856f, -0.864188f},
{-0.295242f, 0.000000f, -0.955423f},
{-0.162460f, 0.262866f, -0.951056f},
{0.000000f, 0.000000f, -1.000000f},
{0.295242f, 0.000000f, -0.955423f},
{0.162460f, 0.262866f, -0.951056f},
{-0.442863f, -0.238856f, -0.864188f},
{-0.309017f, -0.500000f, -0.809017f},
{-0.162460f, -0.262866f, -0.951056f},
{0.000000f, -0.850651f, -0.525731f},
{-0.147621f, -0.716567f, -0.681718f},
{0.147621f, -0.716567f, -0.681718f},
{0.000000f, -0.525731f, -0.850651f},
{0.309017f, -0.500000f, -0.809017f},
{0.442863f, -0.238856f, -0.864188f},
{0.162460f, -0.262866f, -0.951056f},
{0.238856f, -0.864188f, -0.442863f},
{0.500000f, -0.809017f, -0.309017f},
{0.425325f, -0.688191f, -0.587785f},
{0.716567f, -0.681718f, -0.147621f},
{0.688191f, -0.587785f, -0.425325f},
{0.587785f, -0.425325f, -0.688191f},
{0.000000f, -0.955423f, -0.295242f},
{0.000000f, -1.000000f, 0.000000f},
{0.262866f, -0.951056f, -0.162460f},
{0.000000f, -0.850651f, 0.525731f},
{0.000000f, -0.955423f, 0.295242f},
{0.238856f, -0.864188f, 0.442863f},
{0.262866f, -0.951056f, 0.162460f},
{0.500000f, -0.809017f, 0.309017f},
{0.716567f, -0.681718f, 0.147621f},
{0.525731f, -0.850651f, 0.000000f},
{-0.238856f, -0.864188f, -0.442863f},
{-0.500000f, -0.809017f, -0.309017f},
{-0.262866f, -0.951056f, -0.162460f},
{-0.850651f, -0.525731f, 0.000000f},
{-0.716567f, -0.681718f, -0.147621f},
{-0.716567f, -0.681718f, 0.147621f},
{-0.525731f, -0.850651f, 0.000000f},
{-0.500000f, -0.809017f, 0.309017f},
{-0.238856f, -0.864188f, 0.442863f},
{-0.262866f, -0.951056f, 0.162460f},
{-0.864188f, -0.442863f, 0.238856f},
{-0.809017f, -0.309017f, 0.500000f},
{-0.688191f, -0.587785f, 0.425325f},
{-0.681718f, -0.147621f, 0.716567f},
{-0.442863f, -0.238856f, 0.864188f},
{-0.587785f, -0.425325f, 0.688191f},
{-0.309017f, -0.500000f, 0.809017f},
{-0.147621f, -0.716567f, 0.681718f},
{-0.425325f, -0.688191f, 0.587785f},
{-0.162460f, -0.262866f, 0.951056f},
{0.442863f, -0.238856f, 0.864188f},
{0.162460f, -0.262866f, 0.951056f},
{0.309017f, -0.500000f, 0.809017f},
{0.147621f, -0.716567f, 0.681718f},
{0.000000f, -0.525731f, 0.850651f},
{0.425325f, -0.688191f, 0.587785f},
{0.587785f, -0.425325f, 0.688191f},
{0.688191f, -0.587785f, 0.425325f},
{-0.955423f, 0.295242f, 0.000000f},
{-0.951056f, 0.162460f, 0.262866f},
{-1.000000f, 0.000000f, 0.000000f},
{-0.850651f, 0.000000f, 0.525731f},
{-0.955423f, -0.295242f, 0.000000f},
{-0.951056f, -0.162460f, 0.262866f},
{-0.864188f, 0.442863f, -0.238856f},
{-0.951056f, 0.162460f, -0.262866f},
{-0.809017f, 0.309017f, -0.500000f},
{-0.864188f, -0.442863f, -0.238856f},
{-0.951056f, -0.162460f, -0.262866f},
{-0.809017f, -0.309017f, -0.500000f},
{-0.681718f, 0.147621f, -0.716567f},
{-0.681718f, -0.147621f, -0.716567f},
{-0.850651f, 0.000000f, -0.525731f},
{-0.688191f, 0.587785f, -0.425325f},
{-0.587785f, 0.425325f, -0.688191f},
{-0.425325f, 0.688191f, -0.587785f},
{-0.425325f, -0.688191f, -0.587785f},
{-0.587785f, -0.425325f, -0.688191f},
{-0.688191f, -0.587785f, -0.425325f} };
+7
View File
@@ -0,0 +1,7 @@
#ifndef MD2NORMS_H
#define MD2NORMS_H
extern float md2norms[162][3];
#endif
+309
View File
@@ -0,0 +1,309 @@
#include "std.hpp"
#include "md2rep.hpp"
#include "md2norms.hpp"
extern gxRuntime *gx_runtime;
extern gxGraphics *gx_graphics;
static Vector *normals = 0;
static float tex_coords[2][2] = { {0,0},{0,0} };
#pragma pack( push,1 )
struct md2_header {
int magic;
int version;
int skinWidth;
int skinHeight;
int frameSize;
int numSkins;
int numVertices;
int numTexCoords;
int numTriangles;
int numGlCommands;
int numFrames;
int offsetSkins;
int offsetTexCoords;
int offsetTriangles;
int offsetFrames;
int offsetGlCommands;
int offsetEnd;
};
struct md2_uv {
short u, v;
};
struct md2_vert {
unsigned char x, y, z, n;
};
struct md2_tri {
unsigned short verts[3], uvs[3];
};
#pragma pack( pop )
struct t_vert {
unsigned short i, uv;
bool operator<(const t_vert &t)const {
return memcmp(&i, &t.i, 4) < 0;
}
};
struct t_tri {
unsigned short verts[3];
};
MD2Rep::MD2Rep(const string &f) :
mesh(0), n_verts(0), n_tris(0), n_frames(0) {
filebuf in;
md2_header header;
if (!in.open(f.c_str(), ios_base::in | ios_base::binary)) return;
if (in.sgetn((char*)&header, sizeof(header)) != sizeof(header)) return;
if (header.magic != '2PDI' || header.version != 8) return;
n_frames = header.numFrames;
n_tris = header.numTriangles;
//read in tex coords
vector<md2_uv> md2_uvs;
md2_uvs.resize(header.numTexCoords);
in.pubseekpos(header.offsetTexCoords);
in.sgetn((char*)(&md2_uvs.begin()[0]), header.numTexCoords * sizeof(md2_uv));
//read in triangles
vector<md2_tri> md2_tris;
md2_tris.resize(n_tris);
in.pubseekpos(header.offsetTriangles);
in.sgetn((char*)(&md2_tris.begin()[0]), n_tris * sizeof(md2_tri));
vector<t_tri> t_tris;
vector<t_vert> t_verts;
map<t_vert, int> t_map;
int k;
for (k = 0; k < n_tris; ++k) {
t_tri tr;
for (int j = 0; j < 3; ++j) {
t_vert t;
t.i = md2_tris[k].verts[j];
t.uv = md2_tris[k].uvs[j];
map<t_vert, int>::iterator it = t_map.find(t);
if (it == t_map.end()) {
//create new vert
tr.verts[j] = t_map[t] = t_verts.size();
t_verts.push_back(t);
//add UVs
VertexUV uv;
uv.u = md2_uvs[t.uv].u / (float)(header.skinWidth);
uv.v = md2_uvs[t.uv].v / (float)(header.skinHeight);
uvs.push_back(uv);
} else {
//reuse vert
tr.verts[j] = it->second;
}
}
t_tris.push_back(tr);
}
n_verts = t_verts.size();
frames.resize(n_frames);
in.pubseekpos(header.offsetFrames);
vector<md2_vert> md2_verts;
md2_verts.resize(header.numVertices);
//read in frames
for (k = 0; k < n_frames; ++k) {
char t_buff[16];
Frame *fr = &frames[k];
in.sgetn((char*)&fr->scale, 12);
in.sgetn((char*)&fr->trans, 12);
in.sgetn(t_buff, 16);
fr->scale = Vector(fr->scale.y, fr->scale.z, fr->scale.x);
fr->trans = Vector(fr->trans.y, fr->trans.z, fr->trans.x);
//read vertices
in.sgetn((char*)&md2_verts.begin()[0], header.numVertices * sizeof(md2_vert));
fr->verts.resize(n_verts);
for (int j = 0; j < n_verts; ++j) {
Vertex *v = &fr->verts[j];
const t_vert &tv = t_verts[j];
const md2_vert &mv = md2_verts[tv.i];
v->x = mv.y;
v->y = mv.z;
v->z = mv.x;
v->n = mv.n;
box.update(Vector(v->x, v->y, v->z) * fr->scale + fr->trans);
}
}
//create mesh and setup tris
mesh = gx_graphics->createMesh(n_verts, n_tris, 0);
mesh->lock(true);
for (k = 0; k < n_tris; ++k) {
const t_tri &t = t_tris[k];
mesh->setTriangle(k, t.verts[0], t.verts[2], t.verts[1]);
}
mesh->unlock();
//build normals
if (!normals) {
normals = (Vector*)md2norms;
for (int k = 0; k < sizeof(md2norms) / 12; ++k) {
normals[k] = Vector(normals[k].y, normals[k].z, normals[k].x);
}
}
}
MD2Rep::~MD2Rep() {
if (mesh) gx_graphics->freeMesh(mesh);
}
/*
void MD2Rep::render( Vert *v,int frame ){
const Frame &frame_a=frames[frame];
const Vertex *v_a=frame_a.verts.begin();
const Vector scale_a=frame_a.scale,trans_a=frame_a.trans;
for( int k=0;k<n_verts;++v,++v_a,++k ){
v->coords=Vector( v_a->x*scale_a.x+trans_a.x,v_a->y*scale_a.y+trans_a.y,v_a->z*scale_a.z+trans_a.z );
v->normal=normals[ v_a->n ];
}
}
*/
void MD2Rep::render(Vert *v, int frame, float time) {
const Frame &frame_b = frames[frame];
const Vertex *v_b = (Vertex*)(&frame_b.verts.begin()[0]);
const Vector scale_b = frame_b.scale, trans_b = frame_b.trans;
for (int k = 0; k < n_verts; ++v, ++v_b, ++k) {
const Vector t_b(v_b->x*scale_b.x + trans_b.x, v_b->y*scale_b.y + trans_b.y, v_b->z*scale_b.z + trans_b.z);
const Vector &n_b = normals[v_b->n];
v->coords += (t_b - v->coords)*time;
v->normal += (n_b - v->normal)*time;
}
}
void MD2Rep::render(Vert *v, int render_a, int render_b, float render_t) {
const Frame &frame_a = frames[render_a];
const Vector scale_a = frame_a.scale, trans_a = frame_a.trans;
const Frame &frame_b = frames[render_b];
const Vector scale_b = frame_b.scale, trans_b = frame_b.trans;
const Vertex *v_a = (Vertex*)(&frame_a.verts.begin()[0]);
const Vertex *v_b = (Vertex*)(&frame_b.verts.begin()[0]);
for (int k = 0; k < n_verts; ++v, ++v_a, ++v_b, ++k) {
const Vector t_a(v_a->x*scale_a.x + trans_a.x, v_a->y*scale_a.y + trans_a.y, v_a->z*scale_a.z + trans_a.z);
const Vector t_b(v_b->x*scale_b.x + trans_b.x, v_b->y*scale_b.y + trans_b.y, v_b->z*scale_b.z + trans_b.z);
v->coords = (t_b - t_a)*render_t + t_a;
const Vector &n_a = normals[v_a->n];
const Vector &n_b = normals[v_b->n];
v->normal = (n_b - n_a)*render_t + n_a;
}
}
/*
void MD2Rep::render( Vert *v,const Vert *v_a,const Vert *v_b,float render_t ){
for( int k=0;k<n_verts;++v,++v_a,++v_b,++k ){
v->coords=(v_b->coords-v_a->coords)*render_t+v_a->coords;
v->normal=(v_b->normal-v_a->normal)*render_t+v_a->normal;
}
}
*/
void MD2Rep::render(Model *model, int render_a, int render_b, float render_t) {
const Frame &frame_a = frames[render_a];
const Vector scale_a = frame_a.scale, trans_a = frame_a.trans;
const Frame &frame_b = frames[render_b];
const Vector scale_b = frame_b.scale, trans_b = frame_b.trans;
const VertexUV *uv = (VertexUV*)&uvs.begin()[0];
const Vertex *v_a = (Vertex*)(&frame_a.verts.begin()[0]);
const Vertex *v_b = (Vertex*)(&frame_b.verts.begin()[0]);
mesh->lock(true);
for (int k = 0; k < n_verts; ++uv, ++v_a, ++v_b, ++k) {
const Vector t_a(v_a->x*scale_a.x + trans_a.x, v_a->y*scale_a.y + trans_a.y, v_a->z*scale_a.z + trans_a.z);
const Vector t_b(v_b->x*scale_b.x + trans_b.x, v_b->y*scale_b.y + trans_b.y, v_b->z*scale_b.z + trans_b.z);
const Vector t((t_b - t_a)*render_t + t_a);
const Vector &n_a = normals[v_a->n];
const Vector &n_b = normals[v_b->n];
const Vector n((n_b - n_a)*render_t + n_a);
tex_coords[0][0] = uv->u;
tex_coords[0][1] = uv->v;
mesh->setVertex(k, &t.x, &n.x, tex_coords);
}
mesh->unlock();
model->enqueue(mesh, 0, n_verts, 0, n_tris);
}
/*
void MD2Rep::render( Model *model,const Vert *v_a,const Vert *v_b,float render_t ){
const VertexUV *uv=uvs.begin();
mesh->lock();
for( int k=0;k<n_verts;++uv,++v_a,++v_b,++k ){
const Vector t( ( v_b->coords-v_a->coords)*render_t+v_a->coords );
const Vector n( ( v_b->normal-v_a->normal)*render_t+v_a->normal );
tex_coords[0]=uv->u;
tex_coords[1]=uv->v;
mesh->setVertex( k,&t.x,&n.x,tex_coords );
}
mesh->unlock();
model->enqueue( mesh,0,n_verts,0,n_tris );
}
*/
void MD2Rep::render(Model *model, const Vert *v_a, int render_b, float render_t) {
const Frame &frame_b = frames[render_b];
const Vector scale_b = frame_b.scale, trans_b = frame_b.trans;
const VertexUV *uv = (VertexUV*)&uvs.begin()[0];
const Vertex *v_b = (Vertex*)&frame_b.verts.begin()[0];
mesh->lock(true);
for (int k = 0; k < n_verts; ++uv, ++v_a, ++v_b, ++k) {
const Vector t_b(v_b->x*scale_b.x + trans_b.x, v_b->y*scale_b.y + trans_b.y, v_b->z*scale_b.z + trans_b.z);
const Vector t((t_b - v_a->coords)*render_t + v_a->coords);
const Vector &n_b = normals[v_b->n];
const Vector n((n_b - v_a->normal)*render_t + v_a->normal);
tex_coords[0][0] = uv->u;
tex_coords[0][1] = uv->v;
mesh->setVertex(k, &t.x, &n.x, tex_coords);
}
mesh->unlock();
model->enqueue(mesh, 0, n_verts, 0, n_tris);
}
+51
View File
@@ -0,0 +1,51 @@
#ifndef MD2REP_H
#define MD2REP_H
#include "model.hpp"
class MD2Rep{
public:
struct Vert{
Vector coords,normal;
};
MD2Rep( const string &f );
virtual ~MD2Rep();
void render( Vert *verts,int frame );
void render( Vert *verts,int frame,float time );
void render( Vert *verts,int frame_a,int frame_b,float time );
void render( Vert *verts,const Vert *verts_a,const Vert *verts_b,float time );
void render( Model *model,int frame_a,int frame_b,float time );
void render( Model *model,const Vert *verts_a,const Vert *verts_b,float time );
void render( Model *model,const Vert *verts_a,int frame_b,float time );
const Box &getBox()const{ return box; }
const int numFrames()const{ return n_frames; }
const int numVertices()const{ return n_verts; }
private:
struct Vertex{
unsigned char x,y,z,n;
};
struct VertexUV{
float u,v;
};
struct Frame{
Vector scale,trans;
vector<Vertex> verts;
};
Box box;
gxMesh *mesh;
int n_frames;
int n_verts,n_tris;
vector<Frame> frames;
vector<VertexUV> uvs;
};
#endif
+1
View File
@@ -0,0 +1 @@
+199
View File
@@ -0,0 +1,199 @@
#include "std.hpp"
#include "meshcollider.hpp"
static const int MAX_COLL_TRIS=16;
static vector<Vector> tri_centres;
extern float stats3d[10];
extern gxRuntime *gx_runtime;
static bool triTest( const Vector a[3],const Vector b[3] ){
bool pb0=false,pb1=false,pb2=false;
Plane p( a[0],a[1],a[2] ),p0,p1,p2;
for( int k=0;k<3;++k ){
Line l( b[k],b[(k+1)%3]-b[k] );
float t=p.t_intersect( l );
if( t<0 || t>1 ) continue;
Vector i=l*t;
if( !pb0 ){ p0=Plane( a[0]+p.n,a[1],a[0] );pb0=true; }
if( p0.distance( i )<0 ) continue;
if( !pb1 ){ p1=Plane( a[1]+p.n,a[2],a[1] );pb1=true; }
if( p1.distance( i )<0 ) continue;
if( !pb2 ){ p2=Plane( a[2]+p.n,a[0],a[2] );pb2=true; }
if( p2.distance( i )<0 ) continue;
return true;
}
return false;
}
static bool trisIntersect( const Vector a[3],const Vector b[3] ){
return triTest( a,b ) || triTest( b,a );
}
MeshCollider::MeshCollider( const vector<Vertex> &verts,const vector<Triangle> &tris ):
vertices(verts),triangles(tris){
vector<int> ts;
tri_centres.clear();
for( int k=0;k<triangles.size();++k ){
const MeshCollider::Triangle &t=triangles[k];
const Vector &v0=vertices[t.verts[0]].coords;
const Vector &v1=vertices[t.verts[1]].coords;
const Vector &v2=vertices[t.verts[2]].coords;
tri_centres.push_back( (v0+v1+v2)/3 );
ts.push_back( k );
}
tree=createNode( ts );
}
MeshCollider::~MeshCollider(){
delete tree;
}
bool MeshCollider::collide( const Line &line,float radius,Collision *curr_coll,const Transform &t ){
if( !tree ) return false;
//create local box
Box box( line );
box.expand( radius );
Box local_box=-t * box;
return collide( local_box,line,radius,t,curr_coll,tree );
}
bool MeshCollider::collide( const Box &line_box,const Line &line,float radius,const Transform &tform,Collision *curr_coll,MeshCollider::Node *node ){
if( !line_box.overlaps( node->box ) ){
return false;
}
bool hit=false;
if( !node->triangles.size() ){
if( node->left ) hit|=collide( line_box,line,radius,tform,curr_coll,node->left );
if( node->right ) hit|=collide( line_box,line,radius,tform,curr_coll,node->right );
return hit;
}
stats3d[0]+=node->triangles.size();
for( int k=0;k<node->triangles.size();++k ){
const Triangle &tri=triangles[node->triangles[k]];
const Vector &t_v0=vertices[ tri.verts[0] ].coords;
const Vector &t_v1=vertices[ tri.verts[1] ].coords;
const Vector &t_v2=vertices[ tri.verts[2] ].coords;
//tri box
Box tri_box( t_v0 );
tri_box.update( t_v1 );
tri_box.update( t_v2 );
if( !tri_box.overlaps( line_box ) ) continue;
if( !curr_coll->triangleCollide( line,radius,tform*t_v0,tform*t_v1,tform*t_v2 ) ) continue;
curr_coll->surface=tri.surface;
curr_coll->index=tri.index;
hit=true;
}
return hit;
}
Box MeshCollider::nodeBox( const vector<int> &tris ){
Box box;
for( int k=0;k<tris.size();++k ){
const Triangle &t=triangles[ tris[k] ];
for( int j=0;j<3;++j ) box.update( vertices[t.verts[j]].coords );
}
return box;
}
MeshCollider::Node *MeshCollider::createLeaf( const vector<int> &tris ){
Node *c=new Node;
c->box=nodeBox( tris );
c->triangles=tris;
leaves.push_back( c );
return c;
}
MeshCollider::Node *MeshCollider::createNode( const vector<int> &tris ){
if( tris.size()<=MAX_COLL_TRIS ) return createLeaf( tris );
Node *c=new Node;
c->box=nodeBox( tris );
//find longest axis
//
float max=c->box.width();
if( c->box.height()>max ) max=c->box.height();
if( c->box.depth()>max ) max=c->box.depth();
int axis=0;
if( max==c->box.height() ) axis=1;
else if( max==c->box.depth() ) axis=2;
//sort by axis
//
int k;
multimap<float,int> axis_map;
for( k=0;k<tris.size();++k ){
pair<float,int> p( tri_centres[tris[k]][axis],tris[k] );
axis_map.insert( p );
}
//generate left node
//
vector<int> new_tris;
multimap<float,int>::iterator it=axis_map.begin();
for( k=axis_map.size()/2;k--;++it ){
new_tris.push_back( it->second );
}
c->left=createNode( new_tris );
//generate right node
//
new_tris.clear();
for( ;it!=axis_map.end();++it ){
new_tris.push_back( it->second );
}
c->right=createNode( new_tris );
return c;
}
bool MeshCollider::intersects( const MeshCollider &c,const Transform &t )const{
static Vector a[MAX_COLL_TRIS][3],b[3];
if( !(t * tree->box).overlaps( c.tree->box ) ) return false;
for( int k=0;k<leaves.size();++k ){
Node *p=leaves[k];
Box box=t*p->box;
bool tformed=false;
for( int j=0;j<c.leaves.size();++j ){
Node *q=c.leaves[j];
if( !box.overlaps( q->box ) ) continue;
if( !tformed ){
for( int n=0;n<p->triangles.size();++n ){
const Triangle &tri=triangles[p->triangles[n]];
a[n][0]=t * vertices[tri.verts[0]].coords;
a[n][1]=t * vertices[tri.verts[1]].coords;
a[n][2]=t * vertices[tri.verts[2]].coords;
}
tformed=true;
}
for( int n=0;n<q->triangles.size();++n ){
const Triangle &tri=c.triangles[q->triangles[n]];
b[0]=c.vertices[tri.verts[0]].coords;
b[1]=c.vertices[tri.verts[1]].coords;
b[2]=c.vertices[tri.verts[2]].coords;
for( int t=0;t<p->triangles.size();++t ){
if( trisIntersect( a[t],b ) ) return true;
}
}
}
}
return false;
}
+45
View File
@@ -0,0 +1,45 @@
#ifndef MESHCOLLIDER_H
#define MESHCOLLIDER_H
#include "collision.hpp"
class MeshCollider{
public:
struct Vertex{
Vector coords;
};
struct Triangle{
void *surface;
int verts[3],index;
};
MeshCollider( const vector<Vertex> &verts,const vector<Triangle> &tris );
~MeshCollider();
//sphere collision
bool collide( const Line &line,float radius,Collision *curr_coll,const Transform &tform );
bool intersects( const MeshCollider &c,const Transform &t )const;
private:
vector<Vertex> vertices;
vector<Triangle> triangles;
struct Node{
Box box;
Node *left,*right;
vector<int> triangles;
Node():left(0),right(0){}
~Node(){ delete left;delete right; }
};
Node *tree;
vector<Node*> leaves;
Box nodeBox( const vector<int> &tris );
Node *createLeaf( const vector<int> &tris );
Node *createNode( const vector<int> &tris );
bool collide( const Box &box,const Line &line,float radius,const Transform &tform,Collision *curr_coll,Node *node );
};
#endif
+130
View File
@@ -0,0 +1,130 @@
#include "std.hpp"
#include "meshloader.hpp"
#include "meshmodel.hpp"
struct Tri{
int verts[3];
};
struct Surf{
vector<Tri> tris;
};
struct MLMesh{
map<Brush,Surf*> brush_map;
vector<Surface::Vertex> verts;
MLMesh(){
}
~MLMesh(){
map<Brush,Surf*>::const_iterator it;
for( it=brush_map.begin();it!=brush_map.end();++it ){
delete it->second;
}
}
};
static MLMesh *ml_mesh;
static vector<MLMesh*> mesh_stack;
void MeshLoader::beginMesh(){
mesh_stack.push_back( ml_mesh );
ml_mesh=new MLMesh();
}
int MeshLoader::numVertices(){
return ml_mesh->verts.size();
}
void MeshLoader::addVertex( const Surface::Vertex &v ){
ml_mesh->verts.push_back( v );
}
void MeshLoader::addTriangle( const int verts[3],const Brush &b ){
addTriangle( verts[0],verts[1],verts[2],b );
}
void MeshLoader::addBone( int n,float w,int b ){
Surface::Vertex &v=ml_mesh->verts[n];
int i;
for( i=0;i<MAX_SURFACE_BONES;++i ){
if( v.bone_bones[i]==255 || w>v.bone_weights[i] ) break;
}
if( i==MAX_SURFACE_BONES ) return;
for( int k=MAX_SURFACE_BONES-1;k>i;--k ){
v.bone_bones[k]=v.bone_bones[k-1];
v.bone_weights[k]=v.bone_weights[k-1];
}
v.bone_bones[i]=b;
v.bone_weights[i]=w;
}
Surface::Vertex &MeshLoader::refVertex( int n ){
return ml_mesh->verts[n];
}
void MeshLoader::addTriangle( int v0,int v1,int v2,const Brush &b ){
//find surface
Surf *surf;
map<Brush,Surf*>::const_iterator it=ml_mesh->brush_map.find( b );
if( it!=ml_mesh->brush_map.end() ) surf=it->second;
else{
surf=new Surf;
ml_mesh->brush_map.insert( make_pair( b,surf ) );
}
Tri tri;
tri.verts[0]=v0;tri.verts[1]=v1;tri.verts[2]=v2;
surf->tris.push_back( tri );
}
void MeshLoader::endMesh( MeshModel *mesh ){
if( mesh ){
//fix bone weights
int k,max_bones=0;
for( k=0;k<ml_mesh->verts.size();++k ){
Surface::Vertex &v=ml_mesh->verts[k];
if( v.bone_bones[0]==255 ) continue;
int j;
float t=0;
for( j=0;j<MAX_SURFACE_BONES;++j ){
if( v.bone_bones[j]==255 ) break;
t+=v.bone_weights[j];
}
if( j>max_bones ) max_bones=j;
t=1.0f/t;
for( j=0;j<MAX_SURFACE_BONES;++j ){
v.bone_weights[j]*=t;
}
}
map<int,int> vert_map;
map<Brush,Surf*>::iterator it;
for( it=ml_mesh->brush_map.begin();it!=ml_mesh->brush_map.end();++it ){
vert_map.clear();
Brush b=it->first;
Surf *t=it->second;
Surface *surf=mesh->findSurface( b );
if( !surf ) surf=mesh->createSurface( b );
for( int k=0;k<t->tris.size();++k ){
Surface::Triangle tri;
for( int j=0;j<3;++j ){
int n=t->tris[k].verts[j],id;
map<int,int>::const_iterator it=vert_map.find( n );
if( it!=vert_map.end() ) id=it->second;
else{
id=surf->numVertices();
surf->addVertex( ml_mesh->verts[n] );
vert_map.insert( make_pair( n,id ) );
}
tri.verts[j]=id;
}
surf->addTriangle( tri );
}
}
}
delete ml_mesh;
ml_mesh=mesh_stack.back();
mesh_stack.pop_back();
}
+42
View File
@@ -0,0 +1,42 @@
#ifndef MESHLOADER_H
#define MESHLOADER_H
#include "model.hpp"
#include "surface.hpp"
class MeshLoader{
public:
enum{
HINT_COLLAPSE=1,
HINT_ANIMONLY=2
};
virtual MeshModel *load( const string &f,const Transform &conv,int hint )=0;
//clear
static void beginMesh();
//add a vertex
static void addVertex( const Surface::Vertex &v );
//add a triangle
static void addTriangle( const int verts[3],const Brush &b );
//also add a triangle
static void addTriangle( int v0,int v1,int v2,const Brush &b );
//add a bone
static void addBone( int vert,float weight,int bone );
//reference a vertex
static Surface::Vertex &refVertex( int vert );
//number of verts
static int numVertices();
//finally, update the mesh...
static void endMesh( MeshModel *mesh );
};
#endif
+320
View File
@@ -0,0 +1,320 @@
#include "std.hpp"
#include "meshmodel.hpp"
#include "meshcollider.hpp"
extern gxGraphics *gx_graphics;
struct MeshModel::Rep : public Surface::Monitor {
int ref_cnt;
mutable Box box, cullBox;
mutable MeshCollider *collider;
mutable int box_valid, coll_valid, norms_valid;
SurfaceList surfaces;
vector<Transform> bone_tforms;
Rep() :
ref_cnt(1), collider(0), box_valid(-1), coll_valid(-1), norms_valid(-1) {
geom_changes = brush_changes = 0;
}
~Rep() {
delete collider;
for (int k = 0; k < surfaces.size(); ++k) delete surfaces[k];
}
Surface *createSurface(const Brush &b) {
Surface *t = new Surface(this);
surfaces.push_back(t);
t->setBrush(b);
return t;
}
Surface *findSurface(const Brush &b) {
for (int k = 0; k < surfaces.size(); ++k) {
Surface *s = surfaces[k];
if (s->getBrush() < b || b < s->getBrush()) continue;
return s;
}
return 0;
}
void paint(const Brush &b) {
for (int k = 0; k < surfaces.size(); ++k) {
Surface *s = surfaces[k];
s->setBrush(b);
}
}
void add(Rep *t) {
if (cullBox.empty() && !t->cullBox.empty()) {
setCullBox(t->cullBox);
}
for (int k = 0; k < t->surfaces.size(); ++k) {
Surface *src = t->surfaces[k];
Surface *dest = findSurface(src->getBrush());
if (!dest) dest = createSurface(src->getBrush());
int j;
for (j = 0; j < src->numTriangles(); ++j) {
Surface::Triangle t = src->getTriangle(j);
t.verts[0] += dest->numVertices();
t.verts[1] += dest->numVertices();
t.verts[2] += dest->numVertices();
dest->addTriangle(t);
}
for (j = 0; j < src->numVertices(); ++j) {
dest->addVertex(src->getVertex(j));
}
}
}
void transform(const Transform &t) {
Matrix co = t.m.cofactor();
for (int k = 0; k < surfaces.size(); ++k) {
Surface *s = surfaces[k];
for (int j = 0; j < s->numVertices(); ++j) {
const Vector &v = s->getVertex(j).coords;
const Vector &n = s->getVertex(j).normal;
s->setCoords(j, t*v);
s->setNormal(j, co*n);
}
}
}
void flip() {
for (int k = 0; k < surfaces.size(); ++k) {
Surface *s = surfaces[k];
int j;
for (j = 0; j < s->numVertices(); ++j) {
s->setNormal(j, -s->getVertex(j).normal);
}
for (j = 0; j < s->numTriangles(); ++j) {
Surface::Triangle t = s->getTriangle(j);
std::swap(t.verts[1], t.verts[2]);
s->setTriangle(j, t);
}
}
}
void setCullBox(const Box &t) {
cullBox = t;
}
void updateNormals() {
if (norms_valid != geom_changes) {
for (int k = 0; k < surfaces.size(); ++k) {
Surface *s = surfaces[k];
s->updateNormals();
}
norms_valid = geom_changes;
}
}
const Box &getBox()const {
if (box_valid != geom_changes) {
box.clear();
for (int k = 0; k < surfaces.size(); ++k) {
Surface *s = surfaces[k];
for (int j = 0; j < s->numVertices(); ++j) {
box.update(s->getVertex(j).coords);
}
}
box_valid = geom_changes;
}
return box;
}
const Box &getCullBox()const {
return cullBox.empty() ? getBox() : cullBox;
}
MeshCollider *getCollider()const {
if (coll_valid != geom_changes) {
delete collider;
vector<MeshCollider::Vertex> verts;
vector<MeshCollider::Triangle> tris;
for (int k = 0; k < surfaces.size(); ++k) {
Surface *s = surfaces[k];
int j;
for (j = 0; j < s->numTriangles(); ++j) {
MeshCollider::Triangle q;
q.verts[0] = s->getTriangle(j).verts[0] + verts.size();
q.verts[1] = s->getTriangle(j).verts[1] + verts.size();
q.verts[2] = s->getTriangle(j).verts[2] + verts.size();
q.surface = s;
q.index = j;
tris.push_back(q);
}
for (j = 0; j < s->numVertices(); ++j) {
MeshCollider::Vertex q;
q.coords = s->getVertex(j).coords;
verts.push_back(q);
}
}
collider = new MeshCollider(verts, tris);
coll_valid = geom_changes;
}
return collider;
}
};
MeshModel::MeshModel() :
rep(new Rep()), brush_changes(0) {}
MeshModel::MeshModel(const MeshModel &t) : Model(t),
rep(t.rep), brush_changes(rep->brush_changes - 1) {
++rep->ref_cnt;
surf_bones.resize(t.surf_bones.size());
/*
if( t.surf_bones.size() ){
surf_bones.resize( t.surf_bones.size() );
if( rep->bone_tforms.size() ){
setRenderSpace( RENDER_SPACE_WORLD );
}
}
*/
}
MeshModel::~MeshModel() {
if (!--rep->ref_cnt) delete rep;
}
void MeshModel::updateNormals() {
rep->updateNormals();
}
void MeshModel::setCullBox(const Box &box) {
rep->setCullBox(box);
}
void MeshModel::setRenderBrush(const Brush &b) {
--brush_changes;
render_brush = b;
}
void MeshModel::createBones() {
setRenderSpace(RENDER_SPACE_WORLD);
const vector<Object*> &bones = getAnimator()->getObjects();
surf_bones.resize(bones.size());
rep->bone_tforms.resize(bones.size());
for (int k = 0; k < bones.size(); ++k) {
rep->bone_tforms[k] = -bones[k]->GetWorldTransform();
}
}
bool MeshModel::render(const RenderContext &rc) {
const Box &b = rep->getCullBox();
if (b.empty()) return false;
static Frustum model_frustum;
new(&model_frustum) Frustum(rc.getWorldFrustum(), -getRenderTform());
if (!model_frustum.cull(b)) return false;
if (brush_changes != rep->brush_changes) {
brushes.clear();
for (int k = 0; k < rep->surfaces.size(); ++k) {
Surface *s = rep->surfaces[k];
brushes.push_back(Brush(s->getBrush(), render_brush));
}
brush_changes = rep->brush_changes;
}
if (!surf_bones.size()) {
for (int k = 0; k < rep->surfaces.size(); ++k) {
Surface *s = rep->surfaces[k];
if (gxMesh *mesh = s->getMesh()) {
enqueue(mesh, 0, s->numVertices(), 0, s->numTriangles(), brushes[k]);
}
}
return false;
}
//OK, its boned!
const vector<Object*> &bones = getAnimator()->getObjects();
int k;
for (k = 0; k < bones.size(); ++k) {
Transform t =
bones[k]->getRenderTform() * rep->bone_tforms[k];
surf_bones[k].coord_tform = t;
surf_bones[k].normal_tform = t.m.cofactor();
}
bool trans = false;
for (k = 0; k < rep->surfaces.size(); ++k) {
Surface *s = rep->surfaces[k];
if (brushes[k].getBlend() == gxScene::BLEND_REPLACE) {
if (gxMesh *mesh = s->getMesh(surf_bones)) {
enqueue(mesh, 0, s->numVertices(), 0, s->numTriangles(), brushes[k]);
}
} else {
trans = true;
}
}
return trans;
}
void MeshModel::renderQueue(int type) {
if (type == QUEUE_TRANSPARENT && surf_bones.size()) {
for (int k = 0; k < rep->surfaces.size(); ++k) {
Surface *s = rep->surfaces[k];
if (brushes[k].getBlend() != gxScene::BLEND_REPLACE) {
if (gxMesh *mesh = s->getMesh(surf_bones)) {
enqueue(mesh, 0, s->numVertices(), 0, s->numTriangles(), brushes[k]);
}
}
}
}
Model::renderQueue(type);
}
Surface *MeshModel::createSurface(const Brush &b) {
return rep->createSurface(b);
--brush_changes;
}
void MeshModel::flipTriangles() {
rep->flip();
}
void MeshModel::transform(const Transform &t) {
rep->transform(t);
}
void MeshModel::add(const MeshModel &t) {
rep->add(t.rep);
}
const MeshModel::SurfaceList &MeshModel::getSurfaces()const {
return rep->surfaces;
}
void MeshModel::paint(const Brush &b) {
rep->paint(b);
}
const Box &MeshModel::getBox()const {
return rep->getBox();
}
MeshCollider *MeshModel::getCollider()const {
return rep->getCollider();
}
Surface *MeshModel::findSurface(const Brush &b)const {
return rep->findSurface(b);
}
bool MeshModel::collide(const Line &line, float radius, Collision *curr_coll, const Transform &t) {
return getCollider()->collide(line, radius, curr_coll, t);
}
bool MeshModel::intersects(const MeshModel &m)const {
return getCollider()->intersects(*m.getCollider(), -m.GetWorldTransform()*GetWorldTransform());
}
+63
View File
@@ -0,0 +1,63 @@
#ifndef MESHMODEL_H
#define MESHMODEL_H
#include "model.hpp"
#include "surface.hpp"
class MeshCollider;
class MeshModel : public Model{
public:
typedef vector<Surface*> SurfaceList;
MeshModel();
MeshModel( const MeshModel &t );
~MeshModel();
//Entity interface
virtual MeshModel *getMeshModel(){ return this; }
virtual Entity *clone(){ return new MeshModel( *this ); }
//Object interface
virtual bool collide( const Line &line,float radius,Collision *curr_coll,const Transform &t );
//Model interface
virtual void setRenderBrush( const Brush &b );
virtual bool render( const RenderContext &rc );
virtual void renderQueue( int type );
//boned mesh!
void createBones();
//MeshModel interface
Surface *createSurface( const Brush &b );
void setCullBox( const Box &box );
void updateNormals();
void flipTriangles();
void transform( const Transform &t );
void paint( const Brush &b );
void add( const MeshModel &t );
void optimize();
//accessors
const SurfaceList &getSurfaces()const;
Surface *findSurface( const Brush &b )const;
bool intersects( const MeshModel &m )const;
MeshCollider *getCollider()const;
const Box &getBox()const;
private:
struct Rep;
Rep *rep;
int brush_changes;
Brush render_brush;
vector<Brush> brushes;
vector<Surface::Bone> surf_bones;
MeshModel &operator=(const MeshModel &);
};
#endif
+24
View File
@@ -0,0 +1,24 @@
#ifndef MESHRENDERER_H
#define MESHRENDERER_H
#include "mesh.hpp"
#include "model.hpp"
class MeshRenderer{
public:
MeshRenderer( const Mesh &t );
~MeshRenderer();
void render( Camera *c,Model *m )const;
private:
Box box;
struct Surface;
vector<Surface*> surfs;
MeshRenderer( const MeshRenderer &t );
MeshRenderer &operator=(const MeshRenderer&);
};
#endif
+229
View File
@@ -0,0 +1,229 @@
#include "std.hpp"
#include "meshutil.hpp"
MeshModel *MeshUtil::createCube( const Brush &b ){
static Vector norms[]={
Vector(0,0,-1),Vector(1,0,0),Vector(0,0,1),
Vector(-1,0,0),Vector(0,1,0),Vector(0,-1,0)
};
static Vector tex_coords[]={
Vector(0,0,1),Vector(1,0,1),Vector(1,1,1),Vector(0,1,1)
};
static int verts[]={
2,3,1,0,3,7,5,1,7,6,4,5,6,2,0,4,6,7,3,2,0,1,5,4
};
static Box box( Vector(-1,-1,-1),Vector(1,1,1) );
MeshModel *m=new MeshModel();
Surface *s=m->createSurface( b );
Surface::Vertex v;
Surface::Triangle t;
for( int k=0;k<24;k+=4 ){
const Vector &normal=norms[k/4];
for( int j=0;j<4;++j ){
v.coords=box.corner( verts[k+j] );
v.normal=normal;
v.tex_coords[0][0]=v.tex_coords[1][0]=tex_coords[j].x;
v.tex_coords[0][1]=v.tex_coords[1][1]=tex_coords[j].y;
s->addVertex( v );
}
t.verts[0]=k;t.verts[1]=k+1;t.verts[2]=k+2;s->addTriangle(t);
t.verts[1]=k+2;t.verts[2]=k+3;s->addTriangle(t);
}
return m;
}
MeshModel *MeshUtil::createSphere( const Brush &b,int segs ){
int h_segs=segs*2,v_segs=segs;
MeshModel *m=new MeshModel();
Surface *s=m->createSurface( b );
Surface::Vertex v;
Surface::Triangle t;
v.coords=v.normal=Vector(0,1,0);
int k;
for( k=0;k<h_segs;++k ){
v.tex_coords[0][0]=v.tex_coords[1][0]=(k+.5f)/h_segs;
v.tex_coords[0][1]=v.tex_coords[1][1]=0;
s->addVertex( v );
}
for( k=1;k<v_segs;++k ){
float pitch=k*PI/v_segs-HALFPI;
for( int j=0;j<=h_segs;++j ){
float yaw=(j%h_segs)*TWOPI/h_segs;
v.coords=v.normal=rotationMatrix( pitch,yaw,0 ).k;
v.tex_coords[0][0]=v.tex_coords[1][0]=float(j)/float(h_segs);
v.tex_coords[0][1]=v.tex_coords[1][1]=float(k)/float(v_segs);
s->addVertex( v );
}
}
v.coords=v.normal=Vector(0,-1,0);
for( k=0;k<h_segs;++k ){
v.tex_coords[0][0]=v.tex_coords[1][0]=(k+.5f)/h_segs;
v.tex_coords[0][1]=v.tex_coords[1][1]=1;
s->addVertex( v );
}
for( k=0;k<h_segs;++k ){
t.verts[0]=k;
t.verts[1]=t.verts[0]+h_segs+1;
t.verts[2]=t.verts[1]-1;
s->addTriangle( t );
}
for( k=1;k<v_segs-1;++k ){
for( int j=0;j<h_segs;++j ){
t.verts[0]=k*(h_segs+1)+j-1;
t.verts[1]=t.verts[0]+1;
t.verts[2]=t.verts[1]+h_segs+1;
s->addTriangle( t );
t.verts[1]=t.verts[2];
t.verts[2]=t.verts[1]-1;
s->addTriangle( t );
}
}
for( k=0;k<h_segs;++k ){
t.verts[0]=(h_segs+1)*(v_segs-1)+k-1;
t.verts[1]=t.verts[0]+1;
t.verts[2]=t.verts[1]+h_segs;
s->addTriangle( t );
}
return m;
}
MeshModel *MeshUtil::createCylinder( const Brush &b,int segs,bool solid ){
MeshModel *m=new MeshModel();
Surface::Vertex v;
Surface::Triangle t;
Surface *s=m->createSurface( b );
int k;
for( k=0;k<=segs;++k ){
float yaw=(k%segs)*TWOPI/segs;
v.coords=rotationMatrix( 0,yaw,0 ).k;
v.coords.y=1;
v.normal=Vector(v.coords.x,0,v.coords.z);
v.tex_coords[0][0]=v.tex_coords[1][0]=float(k)/segs;
v.tex_coords[0][1]=v.tex_coords[1][1]=0;
s->addVertex( v );
v.coords.y=-1;
v.tex_coords[0][0]=v.tex_coords[1][0]=float(k)/segs;
v.tex_coords[0][1]=v.tex_coords[1][1]=1;
s->addVertex( v );
}
for( k=0;k<segs;++k ){
t.verts[0]=k*2;
t.verts[1]=t.verts[0]+2;
t.verts[2]=t.verts[1]+1;
s->addTriangle( t );
t.verts[1]=t.verts[2];
t.verts[2]=t.verts[1]-2;
s->addTriangle( t );
}
if( !solid ) return m;
s=m->createSurface( b );
for( k=0;k<segs;++k ){
float yaw=k*TWOPI/segs;
v.coords=rotationMatrix( 0,yaw,0 ).k;
v.coords.y=1;v.normal=Vector(0,1,0);
v.tex_coords[0][0]=v.tex_coords[1][0]=v.coords.x*.5f+.5f;
v.tex_coords[0][1]=v.tex_coords[1][1]=v.coords.z*.5f+.5f;
s->addVertex(v);
v.coords.y=-1;v.normal=Vector( 0,-1,0 );
s->addVertex(v);
}
for( k=2;k<segs;++k ){
t.verts[0]=0;
t.verts[1]=k*2;
t.verts[2]=(k-1)*2;
s->addTriangle( t );
t.verts[0]=1;
t.verts[1]=(k-1)*2+1;
t.verts[2]=k*2+1;
s->addTriangle( t );
}
return m;
}
MeshModel *MeshUtil::createCone( const Brush &b,int segs,bool solid ){
MeshModel *m=new MeshModel();
Surface::Vertex v;
Surface::Triangle t;
Surface *s;
s=m->createSurface( b );
int k;
v.coords=v.normal=Vector(0,1,0);
for( k=0;k<segs;++k ){
v.tex_coords[0][0]=v.tex_coords[1][0]=(k+.5f)/segs;
v.tex_coords[0][1]=v.tex_coords[1][1]=0;
s->addVertex( v );
}
for( k=0;k<=segs;++k ){
float yaw=(k%segs)*TWOPI/segs;
v.coords=yawMatrix( yaw ).k;v.coords.y=-1;
v.normal=Vector( v.coords.x,0,v.coords.z );
v.tex_coords[0][0]=v.tex_coords[1][0]=float(k)/segs;
v.tex_coords[0][1]=v.tex_coords[1][1]=1;
s->addVertex( v );
}
for( k=0;k<segs;++k ){
t.verts[0]=k;
t.verts[1]=k+segs+1;
t.verts[2]=k+segs;
s->addTriangle( t );
}
if( !solid ) return m;
s=m->createSurface( b );
for( k=0;k<segs;++k ){
float yaw=k*TWOPI/segs;
v.coords=yawMatrix( yaw ).k;v.coords.y=-1;
v.normal=Vector( v.coords.x,0,v.coords.z );
v.tex_coords[0][0]=v.tex_coords[1][0]=v.coords.x*.5f+.5f;
v.tex_coords[0][1]=v.tex_coords[1][1]=v.coords.z*.5f+.5f;
s->addVertex( v );
}
t.verts[0]=0;
for( k=2;k<segs;++k ){
t.verts[1]=k-1;
t.verts[2]=k;
s->addTriangle( t );
}
return m;
}
void MeshUtil::lightMesh( MeshModel *m,const Vector &pos,const Vector &rgb,float range ){
if( range ){
float att=1.0f/range;
const MeshModel::SurfaceList &surfs=m->getSurfaces();
for( int k=0;k<surfs.size();++k ){
Surface *s=surfs[k];
for( int j=0;j<s->numVertices();++j ){
const Surface::Vertex &v=s->getVertex( j );
Vector lv=pos-v.coords;
float dp=v.normal.normalized().dot( lv );
if( dp<=0 ) continue;
float d=lv.length();
float i=1/(d*att)*(dp/d);
s->setColor( j,s->getColor(j)+rgb*i );
}
}
}else{
const MeshModel::SurfaceList &surfs=m->getSurfaces();
for( int k=0;k<surfs.size();++k ){
Surface *s=surfs[k];
for( int j=0;j<s->numVertices();++j ){
const Surface::Vertex &v=s->getVertex( j );
s->setColor( j,s->getColor(j)+rgb );
}
}
}
}
+30
View File
@@ -0,0 +1,30 @@
#ifndef MESHUTIL_H
#define MESHUTIL_H
#include "meshmodel.hpp"
struct MeshUtil{
static MeshModel *createCube( const Brush &b );
static MeshModel *createSphere( const Brush &b,int segs );
static MeshModel *createCylinder( const Brush &b,int segs,bool solid );
static MeshModel *createCone( const Brush &b,int segs,bool solid );
static void lightMesh( MeshModel *m,const Vector &pos,const Vector &rgb,float range );
/*
static void flipMesh( Mesh *m );
static void fitMesh( Mesh *m,const Box &b );
static void paintMesh( Mesh *m,const Brush &b );
static void transformMesh( Mesh *m,const Transform &t );
static void lightMesh( Mesh *m,const Vector &pos,const Vector &rgb,float range );
static void lightMapMesh( Mesh *m,const Mesh &l );
static Mesh createCube( const Brush &b,int segs );
static Mesh createSphere( const Brush &b,int segs );
static Mesh createCylinder( const Brush &b,int segs );
*/
};
#endif
+13
View File
@@ -0,0 +1,13 @@
#include "std.hpp"
#include "mirror.hpp"
Mirror::Mirror(){
}
Mirror::Mirror( const Mirror &t ):
Object(t){
}
Mirror::~Mirror(){
}
+18
View File
@@ -0,0 +1,18 @@
#ifndef MIRROR_H
#define MIRROR_H
#include "object.hpp"
class Mirror : public Object{
public:
Mirror();
Mirror( const Mirror &t );
~Mirror();
//Entity interface
Entity *clone(){ return new Mirror( *this ); }
Mirror *getMirror(){ return this; }
};
#endif
+129
View File
@@ -0,0 +1,129 @@
#include "std.hpp"
#include "model.hpp"
extern gxScene *gx_scene;
class Model::MeshQueue{
union{
gxMesh *mesh;
MeshQueue *next;
};
int fv,vc,ft,tc;
Brush brush;
int q_type;
// bool opaque;
static MeshQueue *pool;
public:
MeshQueue(){}
MeshQueue( gxMesh *m,int fv,int vc,int ft,int tc,const Brush &b ):
mesh(m),fv(fv),vc(vc),ft(ft),tc(tc),brush(b){
int n=brush.getBlend();
q_type=(n==gxScene::BLEND_REPLACE) ? QUEUE_OPAQUE : QUEUE_TRANSPARENT;
}
int getQueueType()const{
return q_type;
}
void render(){
gx_scene->setRenderState( brush.getRenderState() );
gx_scene->render( mesh,fv,vc,ft,tc );
}
void *operator new( size_t sz ){
static const int GROW=256;
if( !pool ){
pool=new MeshQueue[GROW];
for( int k=0;k<GROW-1;++k ) pool[k].next=&pool[k+1];
pool[GROW-1].next=0;
}
MeshQueue *t=pool;
pool=t->next;
return t;
}
void operator delete( void *q ){
MeshQueue *t=(MeshQueue*)q;
t->next=pool;
pool=t;
}
};
Model::MeshQueue *Model::MeshQueue::pool;
Model::Model():
space( RENDER_SPACE_LOCAL ),
auto_fade(false),
captured_alpha(1),w_brush(true){
}
Model::Model( const Model &t ):Object(t),
space(t.space),brush(t.brush),
auto_fade(t.auto_fade),auto_fade_nr(t.auto_fade_nr),auto_fade_fr(t.auto_fade_fr),
captured_alpha(t.captured_alpha),w_brush(true){
}
void Model::capture(){
Object::capture();
captured_alpha=brush.getAlpha();
}
bool Model::beginRender( float t ){
Object::beginRender( t );
tweened_alpha=brush.getAlpha();
if( t!=1 && tweened_alpha!=captured_alpha ){
//
//render tweening of alpha
//
tweened_alpha=(tweened_alpha-captured_alpha)*t+captured_alpha;
}
return tweened_alpha>0;
}
bool Model::doAutoFade( const Vector &eye ){
float alpha=tweened_alpha;
if( auto_fade ){
//
//autofading of alpha
//
float d=eye.distance( getRenderTform().v );
if( d>=auto_fade_fr ) return false;
if( d>=auto_fade_nr ){
float t=1-(d-auto_fade_nr)/(auto_fade_fr-auto_fade_nr );
alpha*=t;if( alpha<=0 ) return false;
}
}
if( w_brush ) render_brush=brush;
if( alpha!=render_brush.getAlpha() ){
render_brush.setAlpha( alpha );
}else if( !w_brush ){
return true;
}
setRenderBrush( render_brush );
w_brush=false;
return true;
}
void Model::enqueue( MeshQueue *q ){
queues[q->getQueueType()].push_back( q );
}
void Model::enqueue( gxMesh *mesh,int fv,int vc,int ft,int tc ){
enqueue( new MeshQueue( mesh,fv,vc,ft,tc,render_brush ) );
}
void Model::enqueue( gxMesh *mesh,int fv,int vc,int ft,int tc,const Brush &brush ){
enqueue( new MeshQueue( mesh,fv,vc,ft,tc,brush ) );
}
void Model::renderQueue( int type ){
vector<MeshQueue*> *que=&queues[type];
for( ;que->size();que->pop_back() ){
MeshQueue *q=que->back();
q->render();
delete q;
}
}
+92
View File
@@ -0,0 +1,92 @@
#ifndef MODEL_H
#define MODEL_H
#include "brush.hpp"
#include "object.hpp"
#include "rendercontext.hpp"
class Sprite;
class Terrain;
class PlaneModel;
class Q3BSPModel;
class Model : public Object{
public:
enum{
RENDER_SPACE_LOCAL=0,
RENDER_SPACE_WORLD=1
};
enum{
COLLISION_GEOMETRY_DEFAULT=0,
COLLISION_GEOMETRY_TRIS=1,
COLLISION_GEOMETRY_BOX=2,
COLLISION_GEOMETRY_SPHERE=3
};
enum{
QUEUE_OPAQUE=0,
QUEUE_TRANSPARENT=1
};
Model();
Model( const Model &m );
//Entity interface
Model *getModel(){ return this; }
//Object interface
void capture();
bool beginRender( float tween );
//Model interface
virtual void setRenderBrush( const Brush &b ){}
virtual bool render( const RenderContext &rc ){ return false; }
virtual void renderQueue( int type );
virtual Sprite *getSprite(){ return 0; }
virtual Terrain *getTerrain(){ return 0; }
virtual PlaneModel *getPlaneModel(){ return 0; }
virtual MeshModel *getMeshModel(){ return 0; }
virtual MD2Model *getMD2Model(){ return 0; }
virtual Q3BSPModel *getBSPModel(){ return 0; }
virtual void setBrush( const Brush &b ){ brush=b;w_brush=true; }
virtual void setColor( const Vector &c ){ brush.setColor(c);w_brush=true; }
virtual void setAlpha( float a ){ brush.setAlpha(a);w_brush=true; }
virtual void setShininess( float t ){ brush.setShininess(t);w_brush=true; }
virtual void setTexture( int i,const Texture &t,int f ){ brush.setTexture(i,t,f);w_brush=true; }
virtual void setBlend( int n ){ brush.setBlend(n);w_brush=true; }
virtual void setFX( int n ){ brush.setFX(n);w_brush=true; }
const Brush &getBrush()const{ return brush; }
void setRenderSpace( int n ){ space=n; }
int getRenderSpace()const{ return space; }
void setAutoFade( float nr,float fr ){ auto_fade_nr=nr;auto_fade_fr=fr;auto_fade=true; }
bool doAutoFade( const Vector &eye );
void enqueue( gxMesh *mesh,int first_vert,int vert_cnt,int first_tri,int tri_cnt );
void enqueue( gxMesh *mesh,int first_vert,int vert_cnt,int first_tri,int tri_cnt,const Brush &b );
int queueSize( int type )const{ return queues[type].size(); }
private:
class MeshQueue;
int space;
Brush brush,render_brush;
mutable bool w_brush;
float captured_alpha,tweened_alpha;
bool auto_fade;
float auto_fade_nr,auto_fade_fr;
vector<MeshQueue*> queues[2];
void enqueue( MeshQueue *q );
};
#endif
View File
View File
+162
View File
@@ -0,0 +1,162 @@
#include "std.hpp"
#include "object.hpp"
extern gxRuntime *gx_runtime;
Object::Object():
order(0),animator(0),last_copy(0),
coll_type(0),coll_radii(Vector(1,1,1)),coll_box(Box(Vector(-1,-1,-1),Vector(1,1,1))),
pick_geom(0),obscurer(false),captured(false){
reset();
}
Object::Object( const Object &o ):
Entity(o),
order(o.order),animator(0),last_copy(0),
coll_type(o.coll_type),coll_radii(o.coll_radii),coll_box(o.coll_box),
pick_geom(o.pick_geom),obscurer(o.obscurer),captured(false){
reset();
}
Object::~Object(){
delete animator;
velocity=Vector();
updateSounds();
}
Object *Object::copy(){
last_copy=clone()->getObject();
for( Entity *e=GetChildren();e;e=e->GetSuccessor() ){
Object *cpy=e->getObject()->copy();
cpy->SetParent( last_copy );
}
if( animator ) last_copy->setAnimator( new Animator( animator ) );
return last_copy;
}
void Object::reset(){
colls.clear();
velocity=Vector();
prev_tform=GetWorldTransform();
}
void Object::setCollisionType( int type ){
coll_type=type;
}
void Object::setCollisionRadii( const Vector &radii ){
coll_radii=radii;
}
void Object::setCollisionBox( const Box &box ){
coll_box=box;
}
void Object::setAnimator( Animator *t ){
if( animator ) delete animator;
animator=t;
}
void Object::beginUpdate( float e ){
elapsed=e;
colls.clear();
animate( e );
}
void Object::animate( float e ){
if( animator ) animator->update( e );
}
void Object::addCollision( const ObjCollision *c ){
colls.push_back( c );
}
void Object::endUpdate(){
velocity=(GetWorldTransform().v-prev_tform.v)/elapsed;
prev_tform=GetWorldTransform();
}
void Object::capture(){
capt_pos=GetLocalPosition();
capt_scl=GetLocalScale();
capt_rot=GetLocalRotation();
captured=true;
}
bool Object::beginRender( float tween ){
updateSounds();
if( tween==1 || !captured ){
render_tform=GetWorldTransform();
render_tform_valid=true;
}else{
Vector pos=(GetLocalPosition()-capt_pos)*tween+capt_pos;
Vector scl=(GetLocalScale()-capt_scl)*tween+capt_scl;
Quat rot=capt_rot.slerpTo( GetLocalRotation(),tween );
tween_tform.m=Matrix( rot );
tween_tform.m.i*=scl.x;
tween_tform.m.j*=scl.y;
tween_tform.m.k*=scl.z;
tween_tform.v=pos;
render_tform_valid=false;
}
return true;
}
void Object::endRender(){
}
int Object::getCollisionType()const{
return coll_type;
}
const Vector &Object::getCollisionRadii()const{
return coll_radii;
}
const Box &Object::getCollisionBox()const{
return coll_box;
}
const Vector &Object::getVelocity()const{
return velocity;
}
const Object::Collisions &Object::getCollisions()const{
return colls;
}
const Transform &Object::getRenderTform()const{
if( render_tform_valid ) return render_tform;
Object *parent=(Object*)getParent();
render_tform=parent ? parent->getRenderTform() * tween_tform : tween_tform;
render_tform_valid=true;
return render_tform;
}
const Transform &Object::getPrevWorldTform()const{
return prev_tform;
}
gxChannel *Object::emitSound( gxSound *sound ){
if( !sound ) return 0;
gxChannel *chan=sound->play3d( &GetWorldTransform().v.x,&velocity.x );
for( int k=0;k<channels.size();++k ){
if( chan==channels[k] ) return chan;
if( !channels[k] ) return channels[k]=chan;
}
channels.push_back( chan );
return chan;
}
void Object::updateSounds(){
for( int k=0;k<channels.size();++k ){
if( gxChannel *chan=channels[k] ){
if( chan->isPlaying() ) chan->set3d( &GetWorldTransform().v.x,&velocity.x );
else channels[k]=0;
}
}
}
+101
View File
@@ -0,0 +1,101 @@
#ifndef OBJECT_H
#define OBJECT_H
#include <vector>
#include "entity.hpp"
#include "animator.hpp"
#include "collision.hpp"
class gxSound;
struct ObjCollision{
Object *with;
Vector coords;
Collision collision;
};
class Object : public Entity{
public:
typedef std::vector<const ObjCollision*> Collisions;
Object();
Object( const Object &object );
~Object();
//Entity interface
Object *getObject(){ return this; }
Entity *clone(){ return new Object( *this ); }
//deep object copy!
Object *copy();
//called by user
void reset();
void setCollisionType( int type );
void setCollisionRadii( const Vector &radii );
void setCollisionBox( const Box &box );
void setOrder( int n ){ order=n; }
void setPickGeometry( int n ){ pick_geom=n; }
void setObscurer( bool t ){ obscurer=t; }
void setAnimation( const Animation &t ){ anim=t; }
void setAnimator( Animator *t );
gxChannel *emitSound( gxSound *sound );
//overridables!
virtual bool collide( const Line &line,float radius,::Collision *curr_coll,const Transform &t ){ return false; }
virtual void capture();
virtual void animate( float e );
virtual bool beginRender( float tween );
virtual void endRender();
//for use by world
void beginUpdate( float elapsed );
void addCollision( const ObjCollision *c );
void endUpdate();
//accessors
int getCollisionType()const;
const Vector &getCollisionRadii()const;
const Box &getCollisionBox()const;
int getOrder()const{ return order; }
const Vector &getVelocity()const;
const Collisions &getCollisions()const;
const Transform &getRenderTform()const;
const Transform &getPrevWorldTform()const;
int getPickGeometry()const{ return pick_geom; }
int getObscurer()const{ return obscurer; }
Animation getAnimation()const{ return anim; }
Animator *getAnimator()const{ return animator; }
Object *getLastCopy()const{ return last_copy; }
private:
int coll_type;
int order;
Vector coll_radii;
Collisions colls;
bool captured;
Box coll_box;
int pick_geom;
bool obscurer;
float elapsed;
Vector velocity;
vector<gxChannel*> channels;
Vector capt_pos,capt_scl;
Quat capt_rot;
mutable Object *last_copy;
Transform prev_tform;
Transform captured_tform,tween_tform;
mutable Transform render_tform;
mutable bool render_tform_valid;
Animation anim;
Animator *animator;
void updateSounds();
};
#endif
+10
View File
@@ -0,0 +1,10 @@
#include "std.hpp"
#include "pivot.hpp"
Pivot::Pivot(){
}
Pivot::Pivot( const Object &t ):
Object(t){
}
+16
View File
@@ -0,0 +1,16 @@
#ifndef PIVOT_H
#define PIVOT_H
#include "object.hpp"
class Pivot : public Object{
public:
Pivot();
Pivot( const Object &t );
//Entity interface
Entity *clone(){ return new Pivot( *this ); }
};
#endif
+136
View File
@@ -0,0 +1,136 @@
#include "std.hpp"
#include "planemodel.hpp"
#include "frustum.hpp"
#include "camera.hpp"
static Vector vts[17][17];
extern gxGraphics *gx_graphics;
struct PlaneModel::Rep{
int ref_cnt;
gxMesh *mesh;
int sub_divs;
Rep( int n ):
ref_cnt(1),sub_divs(n){
mesh=gx_graphics->createMesh( 5*sub_divs*sub_divs,3*sub_divs*sub_divs,0 );
}
~Rep(){
gx_graphics->freeMesh( mesh );
}
void render( PlaneModel *model,const RenderContext &rc ){
static Frustum f;
new( &f ) Frustum( rc.getWorldFrustum(),-model->getRenderTform() );
const Vector &eye=f.getVertex( Frustum::VERT_EYE );
if( eye.y<=0 ) return;
const Vector &tl=f.getVertex( Frustum::VERT_TLFAR );
const Vector &tr=f.getVertex( Frustum::VERT_TRFAR );
const Vector &br=f.getVertex( Frustum::VERT_BRFAR );
const Vector &bl=f.getVertex( Frustum::VERT_BLFAR );
Transform tex_t( model->getRenderTform().m );
int x;
for( x=0;x<=sub_divs;++x ){
float t=float(x)/float(sub_divs);
Vector tx=(tr-tl)*t+tl;
Vector bx=(br-bl)*t+bl;
for( int y=0;y<=sub_divs;++y ){
float t=float(y)/float(sub_divs);
vts[x][y]=(bx-tx)*t+tx;
}
}
Plane plane( Vector( 0,1,0 ),0 );
mesh->lock( true );
int v_cnt=0,t_cnt=0;
for( x=0;x<sub_divs;++x ){
for( int y=0;y<sub_divs;++y ){
Vector in_verts[4],out_verts[5];
in_verts[0]=vts[x][y];
in_verts[1]=vts[x+1][y];
in_verts[2]=vts[x+1][y+1];
in_verts[3]=vts[x][y+1];
int k,out_cnt=0;
for( k=0;k<4;++k ){
const Vector &vert=in_verts[k];
const Vector &prev_vert=in_verts[(k-1)&3];
if( vert.y>0 ){
if( prev_vert.y<=0 ){
float t=prev_vert.y/(prev_vert.y-vert.y);
out_verts[out_cnt++]=(vert-prev_vert)*t+prev_vert;
}
}else{
if( prev_vert.y>0 ){
float t=prev_vert.y/(prev_vert.y-vert.y);
out_verts[out_cnt++]=(vert-prev_vert)*t+prev_vert;
}
out_verts[out_cnt++]=plane.intersect( Line( eye,vert-eye ) );
}
}
if( out_cnt<3 || out_cnt>5 ) continue;
for( k=0;k<out_cnt;++k ){
const Vector &v=out_verts[k];
float tex_coords[2][2]={ {v.x,v.z},{v.x,v.z} };
mesh->setVertex( v_cnt+k,&v.x,&plane.n.x,tex_coords );
}
for( k=2;k<out_cnt;++k ){
mesh->setTriangle( t_cnt++,v_cnt,v_cnt+k-1,v_cnt+k );
}
v_cnt+=out_cnt;
}
}
mesh->unlock();
if( v_cnt<3 ) return;
model->enqueue( mesh,0,v_cnt,0,t_cnt );
}
};
PlaneModel::PlaneModel( int sub_divs ):
rep( new Rep(sub_divs) ){
}
PlaneModel::PlaneModel( const PlaneModel &t ):
Model( t ),rep( t.rep ){
++rep->ref_cnt;
}
PlaneModel::~PlaneModel(){
if( !--rep->ref_cnt ) delete rep;
}
Plane PlaneModel::getRenderPlane()const{
return Plane( getRenderTform().v,getRenderTform().m.j.normalized() );
}
bool PlaneModel::render( const RenderContext &rc ){
rep->render( this,rc );
return false;
}
bool PlaneModel::collide( const Line &l,float radius,Collision *curr_coll,const Transform &tf ){
Line line( -tf * l );
Plane p( Vector( 0,1,0 ),0 );
p.d-=radius;
float t=p.t_intersect( line );
if( t>=curr_coll->time ) return false;
Vector n=(tf.m.cofactor() * p.n).normalized();
return curr_coll->update( l,t,n );
}
+31
View File
@@ -0,0 +1,31 @@
#ifndef PLANEMODEL_H
#define PLANEMODEL_H
#include "model.hpp"
#include "brush.hpp"
class PlaneModel : public Model{
public:
PlaneModel( int sub_divs );
PlaneModel( const PlaneModel &t );
~PlaneModel();
Entity *clone(){ return new PlaneModel( *this ); }
//model interface
bool render( const RenderContext &rc );
//object interface
bool collide( const Line &line,float radius,Collision *curr_coll,const Transform &tf );
Plane getRenderPlane()const;
private:
struct Rep;
Rep *rep;
virtual PlaneModel *getPlaneModel(){ return this; }
};
#endif
+46
View File
@@ -0,0 +1,46 @@
#include "std.hpp"
#include "q3bspmodel.hpp"
#include "q3bsprep.hpp"
struct Q3BSPModel::Rep : public Q3BSPRep{
int ref_cnt;
Rep( const string &f,float gam ):Q3BSPRep( f,gam ),
ref_cnt(1){
}
};
Q3BSPModel::Q3BSPModel( const string &f,float gam ):
rep( new Rep( f,gam ) ){
}
Q3BSPModel::Q3BSPModel( const Q3BSPModel &t ):Model(t),
rep( t.rep ){
++rep->ref_cnt;
}
Q3BSPModel::~Q3BSPModel(){
if( !--rep->ref_cnt ) delete rep;
}
bool Q3BSPModel::collide( const Line &line,float radius,Collision *curr_coll,const Transform &t ){
return rep->collide( line,radius,curr_coll,t );
}
bool Q3BSPModel::render( const RenderContext &rc ){
rep->render( this,rc );
return false;
}
void Q3BSPModel::setAmbient( const Vector &t ){
rep->setAmbient( t );
}
void Q3BSPModel::setLighting( bool l ){
rep->setLighting( l );
}
bool Q3BSPModel::isValid()const{
return rep->isValid();
}
+34
View File
@@ -0,0 +1,34 @@
#ifndef Q3BSPMODEL_H
#define Q3BSPMODEL_H
#include "model.hpp"
class Q3BSPModel : public Model{
public:
Q3BSPModel( const string &f,float gamma_adj );
Q3BSPModel( const Q3BSPModel &m );
~Q3BSPModel();
//Entity interface
Entity *clone(){ return new Q3BSPModel( *this ); }
//Object interface
virtual bool collide( const Line &line,float radius,Collision *curr_coll,const Transform &t );
//Model interface
Q3BSPModel *getBSPModel(){ return this; }
bool render( const RenderContext &rc );
void setAmbient( const Vector &t );
void setLighting( bool use_lmap );
bool isValid()const;
private:
struct Rep;
Rep *rep;
};
#endif
+762
View File
@@ -0,0 +1,762 @@
#include "std.hpp"
#include "q3bsprep.hpp"
/* Quake3 File format types */
#pragma pack(push,1)
struct q3_plane {
Vector normal;
float distance;
};
struct q3_tex {
char name[64];
int flags, contents;
};
struct q3_vertex {
Vector coords;
float tex_coords[4];
Vector normal;
unsigned char color[4];
};
struct q3_node {
int plane;
int children[2];
int mins[3];
int maxs[3];
};
struct q3_face {
int texture;
int effect;
int type;
int vertex;
int n_verts;
int meshvert;
int n_meshverts;
int lm_index;
int lm_start[2];
int lm_size[2];
float lm_origin[3];
float lm_vecs[2][3];
float normal[3];
int patch_size[2];
};
struct q3_leaf {
int cluster;
int area;
int mins[3];
int maxs[3];
int leafface;
int n_leaffaces;
int leafbrush;
int n_leafbrushes;
};
struct q3_brush {
int brushside;
int n_brushsides;
int texture;
};
struct q3_brushside {
int plane;
int texture;
};
struct q3_direntry {
union {
int offset;
void *lump;
};
int length;
};
struct q3_header {
unsigned magic;
int version;
q3_direntry dir[17];
};
#pragma pack(pop)
/* Loading reps */
struct Surf {
Q3BSPSurf *surf;
int texture, lm_index;
vector<int> verts, tris;
};
struct FaceCmp {
bool operator()(const q3_face *a, const q3_face *b)const {
if (a->texture < b->texture) return true;
if (b->texture < a->texture) return false;
if (a->lm_index < b->lm_index) return true;
return false;
}
};
typedef map<q3_face*, Surf*, FaceCmp> FaceMap;
/* render reps */
struct Q3BSPFace;
struct Q3BSPSurf {
Brush brush;
gxMesh *mesh;
vector<Q3BSPFace*> r_faces;
int texture, lm_index;
};
struct Q3BSPFace {
union {
Surf *t_surf;
Q3BSPSurf *surf;
};
int vert, n_verts, tri, n_tris;
};
struct Q3BSPBrush {
vector<Plane> planes;
};
struct Q3BSPLeaf {
int cluster;
Box box;
vector<Q3BSPFace*> faces;
};
struct Q3BSPNode {
Box box;
Plane plane;
Q3BSPNode *nodes[2];
Q3BSPLeaf *leafs[2];
~Q3BSPNode() { delete nodes[0]; delete nodes[1]; delete leafs[0]; delete leafs[1]; }
};
static q3_header header;
static FaceMap face_map;
static vector<Surf*> t_surfs;
static vector<q3_vertex> p_verts; //patch vertices
static vector<Vector> p_coll_verts;
static vector<MeshCollider::Triangle> coll_tris;
static float gamma_adj;
static Vector r_eye;
static int r_cluster;
static Frustum r_frustum;
static Vector r_frustedges[12];
static map<int, Q3BSPFace*> q3face_map;
extern gxScene *gx_scene;
extern gxRuntime *gx_runtime;
extern gxGraphics *gx_graphics;
//#define SWAPTRIS
Vector static tf(const Vector &v) {
return Vector(-v.y, v.z, v.x);
}
#ifdef BETA
static void debuglog(const string &t) {
gx_runtime->debugLog(t.c_str());
}
#else
static void debuglog(const string &t) {}
#endif
static Surf *findSurf(q3_face *f) {
FaceMap::const_iterator it = face_map.find(f);
if (it != face_map.end()) return it->second;
Surf *s = new Surf;
s->texture = f->texture;
s->lm_index = f->lm_index;
face_map.insert(make_pair(f, s));
t_surfs.push_back(s);
return s;
}
void Q3BSPRep::createTextures() {
int n_texs = header.dir[1].length / sizeof(q3_tex);
q3_tex *q3tex = (q3_tex*)header.dir[1].lump;
for (int k = 0; k < n_texs; ++k) {
string t = string(q3tex->name);
char fl[32], co[32];
_itoa(q3tex->flags, fl, 16);
_itoa(q3tex->contents, co, 16);
debuglog(t + ", flags=0x" + fl + ", contents=0x" + co);
Texture tex(t + ".tga", 1);
if (!tex.getCanvas(0)) {
tex = Texture(t + ".jpg", 1);
if (!tex.getCanvas(0)) {
tex = Texture(t + ".png", 1);
if (!tex.getCanvas(0)) {
tex = Texture(t + ".dds", 1);
if (!tex.getCanvas(0)) debuglog("Failed!");
}
}
}
tex.setFlags(1);
textures.push_back(tex);
++q3tex;
}
}
void Q3BSPRep::createLightMaps() {
int n_lmaps = header.dir[14].length / (128 * 128 * 3);
unsigned char *rgb = (unsigned char*)header.dir[14].lump;
unsigned char adj[256];
int k;
for (k = 0; k < 256; ++k) adj[k] = pow(k / 255.0f, gamma_adj)*255.0f;
for (k = 0; k < n_lmaps; ++k) {
Texture tex(128, 128, 1 + 8 + 16 + 32, 1);
tex.setBlend(gxScene::BLEND_ADD);
gxCanvas *c = tex.getCanvas(0);
c->lock();
for (int y = 0; y < 128; ++y) {
for (int x = 0; x < 128; ++x) {
unsigned argb = 0xff000000 | (adj[rgb[0]] << 16) | (adj[rgb[1]] << 8) | adj[rgb[2]];
c->setPixelFast(x, y, argb);
rgb += 3;
}
}
c->unlock();
light_maps.push_back(tex);
}
}
void Q3BSPRep::createVis() {
int *vis = (int*)header.dir[16].lump;
int n_vecs = *vis++;
vis_sz = *vis++;
debuglog("vis: " + itoa(n_vecs) + "," + itoa(vis_sz));
vis_data = new char[n_vecs*vis_sz];
memcpy(vis_data, vis, n_vecs*vis_sz);
}
void Q3BSPRep::createCollider() {
vector<MeshCollider::Vertex> coll_verts;
int n_verts = header.dir[10].length / sizeof(q3_vertex);
q3_vertex *t = (q3_vertex*)header.dir[10].lump;
MeshCollider::Vertex cv;
int k;
for (k = 0; k < n_verts; ++k) {
cv.coords = tf(t->coords);
coll_verts.push_back(cv);
++t;
}
for (k = 0; k < p_coll_verts.size(); ++k) {
cv.coords = p_coll_verts[k];
coll_verts.push_back(cv);
}
#ifdef SWAPTRIS
for (k = 0; k < coll_tris.size(); ++k) {
std::swap(coll_tris[k].verts[1], coll_tris[k].verts[2]);
}
#endif
collider = new MeshCollider(coll_verts, coll_tris);
p_coll_verts.clear();
coll_verts.clear();
coll_tris.clear();
}
void Q3BSPRep::createSurfs() {
int k;
for (k = 0; k < t_surfs.size(); ++k) {
Surf *s = t_surfs[k];
gxMesh *mesh = gx_graphics->createMesh(s->verts.size(), s->tris.size() / 3, 0);
mesh->lock(true);
int j;
for (j = 0; j < s->verts.size(); ++j) {
q3_vertex *t;
int n = s->verts[j];
if (n >= 0) {
t = (q3_vertex*)header.dir[10].lump + n;
} else {
t = &p_verts[-n - 1];
}
float tex_coords[2][2] = { {t->tex_coords[2],t->tex_coords[3]},{t->tex_coords[0],t->tex_coords[1]} };
unsigned argb = 0xff000000 | (t->color[0] << 16) | (t->color[1] << 8) | t->color[2];
mesh->setVertex(j, tf(t->coords), tf(t->normal), argb, tex_coords);
}
for (j = 0; j < s->tris.size(); j += 3) {
#ifdef SWAPTRIS
mesh->setTriangle(j / 3, s->tris[j], s->tris[j + 2], s->tris[j + 1]);
#else
mesh->setTriangle(j / 3, s->tris[j], s->tris[j + 1], s->tris[j + 2]);
#endif
}
mesh->unlock();
Q3BSPSurf *surf = new Q3BSPSurf;
surf->texture = s->texture;
surf->lm_index = s->lm_index;
surf->mesh = mesh;
surfs.push_back(surf);
s->surf = surf;
}
for (k = 0; k < faces.size(); ++k) {
Q3BSPFace *f = faces[k];
f->surf = f->t_surf->surf;
f->tri /= 3; f->n_tris /= 3;
}
for (k = 0; k < t_surfs.size(); ++k) {
delete t_surfs[k];
}
face_map.clear();
t_surfs.clear();
p_verts.clear();
}
static void average(const q3_vertex &a, const q3_vertex &b, q3_vertex *c) {
c->coords = (a.coords + b.coords)*.5f;
c->normal = (a.normal + b.normal)*.5f;
for (int k = 0; k < 4; ++k) {
c->color[k] = (a.color[k] + b.color[k] + 1) / 2;
c->tex_coords[k] = (a.tex_coords[k] + b.tex_coords[k])*.5f;
}
}
static void subdivide(vector<q3_vertex> &verts, int level, int index, int step) {
if (!level) {
q3_vertex t1, t2;
average(verts[index], verts[index + step], &t1);
average(verts[index + step], verts[index + step * 2], &t2);
average(t1, t2, &verts[index + step]);
return;
}
average(verts[index], verts[index + step], &verts[index + step / 2]);
average(verts[index + step], verts[index + step * 2], &verts[index + step + step / 2]);
average(verts[index + step / 2], verts[index + step + step / 2], &verts[index + step]);
subdivide(verts, level - 1, index, step / 2);
subdivide(verts, level - 1, index + step, step / 2);
}
static void patchFace(Q3BSPFace *face, q3_face *q3face, bool draw, bool solid, int level) {
int k, x, y;
vector<q3_vertex> verts;
if (draw) {
int step = 1 << level;
int size_x = (q3face->patch_size[0] - 1)*step + 1;
int size_y = (q3face->patch_size[1] - 1)*step + 1;
verts.resize(size_x*size_y);
//seed initial verts
q3_vertex *t = (q3_vertex*)header.dir[10].lump + q3face->vertex;
for (y = 0; y < size_y; y += step) {
for (x = 0; x < size_x; x += step) {
verts[y*size_x + x] = *t++;
}
}
//subdivide!
for (y = 0; y < size_y; y += step) {
for (x = 0; x < size_x - 1; x += step * 2) {
subdivide(verts, level, y*size_x + x, step);
}
}
for (x = 0; x < size_x; ++x) {
for (y = 0; y < size_y - 1; y += step * 2) {
subdivide(verts, level, y*size_x + x, size_x*step);
}
}
Surf *surf = face->t_surf;
int vert = surf->verts.size() - face->vert;
//generate patch verts
for (k = 0; k < size_x*size_y; ++k) {
p_verts.push_back(verts[k]);
surf->verts.push_back(p_verts.size()); // Why was there a - here
}
face->n_verts += size_x*size_y;
//generate tris...
for (y = 0; y < size_y - 1; ++y) {
int n = y*size_x + vert;
for (x = 0; x < size_x - 1; ++n, ++x) {
surf->tris.push_back(n);
surf->tris.push_back(n + size_x);
surf->tris.push_back(n + 1);
surf->tris.push_back(n + size_x + 1);
surf->tris.push_back(n + 1);
surf->tris.push_back(n + size_x);
}
}
face->n_tris += (size_x - 1)*(size_y - 1) * 6;
}
if (solid) {
vector<q3_vertex> verts;
int step = 1;
int size_x = q3face->patch_size[0];
int size_y = q3face->patch_size[1];
verts.resize(size_x*size_y);
//seed initial verts
q3_vertex *t = (q3_vertex*)header.dir[10].lump + q3face->vertex;
for (k = 0; k < size_x*size_y; ++k) verts[k] = *t++;
//subdivide!
for (y = 0; y < size_y; y += step) {
for (x = 0; x < size_x - 1; x += step * 2) {
subdivide(verts, 0, y*size_x + x, step);
}
}
for (x = 0; x < size_x; ++x) {
for (y = 0; y < size_y - 1; y += step * 2) {
subdivide(verts, 0, y*size_x + x, size_x*step);
}
}
int vert = header.dir[10].length / sizeof(q3_vertex) + p_coll_verts.size();
//generate patch verts
for (k = 0; k < size_x*size_y; ++k) p_coll_verts.push_back(tf(verts[k].coords));
MeshCollider::Triangle ct;
ct.surface = 0; ct.index = 0;
//generate tris...
for (y = 0; y < size_y - 1; ++y) {
int n = y*size_x + vert;
for (x = 0; x < size_x - 1; ++n, ++x) {
ct.verts[0] = n;
ct.verts[1] = n + size_x;
ct.verts[2] = n + 1;
coll_tris.push_back(ct);
ct.verts[0] = n + size_x + 1;
ct.verts[1] = n + 1;
ct.verts[2] = n + size_x;
coll_tris.push_back(ct);
}
}
}
}
static void meshFace(Q3BSPFace *face, q3_face *q3face, bool draw, bool solid) {
static map<int, int> vert_map;
vert_map.clear();
int *meshverts = (int*)header.dir[11].lump + q3face->meshvert;
MeshCollider::Triangle ct;
ct.surface = 0; ct.index = 0;
for (int j = 0; j < q3face->n_meshverts; j += 3) {
for (int q = 0; q < 3; ++q) {
int n = meshverts[j + q] + q3face->vertex;
if (draw) {
if (!vert_map.count(n)) {
vert_map[n] = face->t_surf->verts.size() - face->vert;
face->t_surf->verts.push_back(n);
++face->n_verts;
}
face->t_surf->tris.push_back(vert_map[n]);
++face->n_tris;
}
ct.verts[q] = n;
}
if (solid) coll_tris.push_back(ct);
}
}
static Q3BSPBrush *createBrush(int n) {
Q3BSPBrush *brush = new Q3BSPBrush;
q3_brush *q3brush = (q3_brush*)header.dir[8].lump + n;
q3_brushside *q3brushside = (q3_brushside*)header.dir[9].lump + q3brush->brushside;
Plane p;
for (int j = 0; j < q3brush->n_brushsides; ++j) {
q3_plane *q3plane = (q3_plane*)header.dir[2].lump + q3brushside[j].plane;
p.n = tf(q3plane->normal);
p.d = -q3plane->distance;
brush->planes.push_back(p);
}
return brush;
}
Q3BSPLeaf *Q3BSPRep::createLeaf(int n) {
q3_leaf *q3leaf = (q3_leaf*)header.dir[4].lump + n;
Q3BSPLeaf *leaf = new Q3BSPLeaf;
leaf->cluster = q3leaf->cluster;
Vector mins(q3leaf->mins[0], q3leaf->mins[1], q3leaf->mins[2]);
Vector maxs(q3leaf->maxs[0], q3leaf->maxs[1], q3leaf->maxs[2]);
leaf->box = Box(tf(mins));
leaf->box.update(tf(maxs));
int *leaffaces = (int*)header.dir[5].lump + q3leaf->leafface;
for (int k = 0; k < q3leaf->n_leaffaces; ++k) {
int face_n = leaffaces[k];
map<int, Q3BSPFace*>::const_iterator it = q3face_map.find(face_n);
if (it != q3face_map.end()) {
if (it->second) leaf->faces.push_back(it->second);
continue;
}
q3_face *q3face = (q3_face*)header.dir[13].lump + leaffaces[k];
if (q3face->type == 1 || q3face->type == 3) {
if (!q3face->n_meshverts || (q3face->n_meshverts % 3)) continue;
} else if (q3face->type != 2) continue;
bool draw = true, solid = true;
if (q3face->texture >= 0) {
q3_tex *q3tex = (q3_tex*)header.dir[1].lump + q3face->texture;
if (!(q3tex->contents & 1)) continue;
if (q3tex->flags & 0x84) draw = false;
}
if (!draw && !solid) continue;
Q3BSPFace *face = 0;
if (draw) {
Surf *surf = findSurf(q3face);
face = new Q3BSPFace;
face->t_surf = surf;
face->vert = surf->verts.size();
face->tri = surf->tris.size();
face->n_verts = face->n_tris = 0;
leaf->faces.push_back(face);
faces.push_back(face);
q3face_map.insert(make_pair(face_n, face));
}
if (q3face->type == 2) {
patchFace(face, q3face, draw, solid, 1);
} else {
meshFace(face, q3face, draw, solid);
}
}
return leaf;
}
Q3BSPNode *Q3BSPRep::createNode(int n) {
q3_node *q3node = (q3_node*)header.dir[3].lump + n;
q3_plane *q3plane = (q3_plane*)header.dir[2].lump + q3node->plane;
Q3BSPNode *node = new Q3BSPNode;
Vector mins(q3node->mins[0], q3node->mins[1], q3node->mins[2]);
Vector maxs(q3node->maxs[0], q3node->maxs[1], q3node->maxs[2]);
node->box = Box(tf(mins));
node->box.update(tf(maxs));
node->plane.n = tf(q3plane->normal);
node->plane.d = -q3plane->distance;
for (int k = 0; k < 2; ++k) {
if (q3node->children[k] >= 0) {
node->nodes[k] = createNode(q3node->children[k]);
node->leafs[k] = 0;
} else {
node->leafs[k] = createLeaf(-q3node->children[k] - 1);
node->nodes[k] = 0;
}
}
return node;
}
Q3BSPRep::Q3BSPRep(const string &f, float gam) :root_node(0), vis_sz(0), vis_data(0), use_lmap(true) {
gamma_adj = 1 - gam;
FILE *buf = fopen(f.c_str(), "rb"); if (!buf) return;
fread(&header, sizeof(header), 1, buf);
if (header.magic != 'PSBI' || header.version != 0x2e) {
fclose(buf); return;
}
debuglog("Header OK");
int k;
//load all lumps...
for (k = 0; k < 17; ++k) {
if (header.dir[k].offset && header.dir[k].length) {
fseek(buf, header.dir[k].offset, SEEK_SET);
header.dir[k].lump = new char[header.dir[k].length];
fread(header.dir[k].lump, header.dir[k].length, 1, buf);
} else {
header.dir[k].lump = 0;
}
}
//create root of BSP tree
root_node = createNode(0);
createCollider();
createTextures();
createLightMaps();
createSurfs();
createVis();
//unload all lumps...
for (k = 0; k < 17; ++k) {
delete[] header.dir[k].lump;
}
fclose(buf);
use_lmap = false;
setLighting(true);
q3face_map.clear();
}
Q3BSPRep::~Q3BSPRep() {
delete root_node;
delete[] vis_data;
int k;
for (k = 0; k < surfs.size(); ++k) {
gx_graphics->freeMesh(surfs[k]->mesh);
delete surfs[k];
}
for (k = 0; k < faces.size(); ++k) {
delete faces[k];
}
}
void Q3BSPRep::vis(Q3BSPNode *n) {
int i = n->plane.distance(r_eye) < 0;
if (n->nodes[i]) vis(n->nodes[i]);
else r_cluster = n->leafs[i]->cluster;
}
static bool cull(const Box &b, int *clip) {
for (int n = 0; n < 6; ++n) {
int mask = 1 << n;
if (!(*clip & mask)) continue;
const Plane &p = r_frustum.getPlane(n);
int q =
(p.distance(b.corner(0)) >= 0) + (p.distance(b.corner(1)) >= 0) +
(p.distance(b.corner(2)) >= 0) + (p.distance(b.corner(3)) >= 0) +
(p.distance(b.corner(4)) >= 0) + (p.distance(b.corner(5)) >= 0) +
(p.distance(b.corner(6)) >= 0) + (p.distance(b.corner(7)) >= 0);
if (!q) return false;
if (q == 8) *clip &= ~mask;
}
return true;
}
void Q3BSPRep::render(Q3BSPLeaf *l, int clip) {
int cluster = l->cluster;
if (cluster < 0) return;
if (r_cluster >= 0) {
if (!(vis_data[cluster*vis_sz + r_cluster / 8] & (1 << (r_cluster & 7)))) return;
}
if (clip && !cull(l->box, &clip)) return;
for (int k = 0; k < l->faces.size(); ++k) {
Q3BSPFace *f = l->faces[k];
if (Q3BSPSurf *s = f->surf) {
if (!s->r_faces.size()) r_surfs.push_back(s);
s->r_faces.push_back(f);
f->surf = 0;
}
}
}
void Q3BSPRep::render(Q3BSPNode *n, int clip) {
if (clip && !cull(n->box, &clip)) return;
//draw front to back...
int i = n->plane.distance(r_eye) < 0;
if (n->nodes[i]) render(n->nodes[i], clip);
else render(n->leafs[i], clip);
i ^= 1;
if (n->nodes[i]) render(n->nodes[i], clip);
else render(n->leafs[i], clip);
}
void Q3BSPRep::render(Model *model, const RenderContext &rc) {
r_eye = -model->getRenderTform() * rc.getCameraTform().v;
new(&r_frustum) Frustum(rc.getWorldFrustum(), -model->getRenderTform());
vis(root_node);
if (r_cluster == -1) debuglog("No cluster!");
render(root_node, 0x3f);
if (!r_surfs.size()) return;
gx_scene->setAmbient2(&ambient.x);
gx_scene->setWorldMatrix((gxScene::Matrix*)&model->getRenderTform());
int k;
for (k = 0; k < r_surfs.size(); ++k) {
Q3BSPSurf *s = r_surfs[k];
gx_scene->setRenderState(s->brush.getRenderState());
int j;
for (j = 0; j < s->r_faces.size(); ++j) {
Q3BSPFace *f = s->r_faces[j];
gx_scene->render(s->mesh, f->vert, f->n_verts, f->tri, f->n_tris);
f->surf = s;
}
s->r_faces.clear();
}
r_surfs.clear();
}
bool Q3BSPRep::collide(const Line &line, float radius, Collision *curr_coll, const Transform &t) {
return collider->collide(line, radius, curr_coll, t);
}
void Q3BSPRep::setAmbient(const Vector &t) {
ambient = t;
}
void Q3BSPRep::setLighting(bool lmap) {
if (lmap == use_lmap) return;
int fx = gxScene::FX_CONDLIGHT;
if (use_lmap = lmap) {
int k;
for (k = 0; k < surfs.size(); ++k) {
Q3BSPSurf *s = surfs[k];
if (s->lm_index >= 0) {
//has a lightmap...
s->brush.setFX(fx);
s->brush.setTexture(0, light_maps[s->lm_index], 0);
if (s->texture >= 0) {
s->brush.setTexture(1, textures[s->texture], 0);
}
} else {
s->brush.setFX(fx | gxScene::FX_EMISSIVE);
if (s->texture >= 0) {
s->brush.setTexture(0, textures[s->texture], 0);
}
}
}
} else {
int k;
Texture tex;
for (k = 0; k < surfs.size(); ++k) {
Q3BSPSurf *s = surfs[k];
s->brush.setFX(fx | gxScene::FX_EMISSIVE);
if (s->texture >= 0) {
s->brush.setTexture(0, textures[s->texture], 0);
} else {
s->brush.setTexture(0, tex, 0);
}
s->brush.setTexture(1, tex, 0);
}
}
}
+56
View File
@@ -0,0 +1,56 @@
#ifndef Q3BSPREP_H
#define Q3BSPREP_H
#include "model.hpp"
#include "meshcollider.hpp"
struct Q3BSPSurf;
struct Q3BSPFace;
struct Q3BSPLeaf;
struct Q3BSPNode;
class Q3BSPRep{
public:
//constructor
Q3BSPRep( const string &f,float gamma_adj );
~Q3BSPRep();
void render( Model *model,const RenderContext &rc );
bool collide( const Line &line,float radius,Collision *curr_coll,const Transform &t );
void setAmbient( const Vector &t );
void setLighting( bool use_lmap );
bool isValid()const{ return root_node!=0; }
private:
Q3BSPNode *root_node;
Vector ambient;
vector<Q3BSPFace*> faces;
vector<Q3BSPSurf*> surfs,r_surfs;
vector<Texture> textures,light_maps;
int vis_sz;
char *vis_data;
bool use_lmap;
MeshCollider *collider;
void createVis();
void createSurfs();
void createCollider();
void createTextures();
void createLightMaps();
Q3BSPLeaf *createLeaf( int n );
Q3BSPNode *createNode( int n );
void vis( Q3BSPNode *node );
void render( Q3BSPLeaf *l,int clip );
void render( Q3BSPNode *n,int clip );
};
#endif
+25
View File
@@ -0,0 +1,25 @@
#ifndef RENDERCONTEXT_H
#define RENDERCONTEXT_H
#include "frustum.hpp"
class RenderContext{
public:
RenderContext( const Transform &t,const Frustum &f,bool r ):
camera_tform( t ),camera_frustum(f),ref(r){
new( &world_frustum ) Frustum( f,t );
}
bool isReflected()const{ return ref; }
const Transform &getCameraTform()const{ return camera_tform; }
const Frustum &getWorldFrustum()const{ return world_frustum; }
const Frustum &getCameraFrustum()const{ return camera_frustum; }
private:
Transform camera_tform;
Frustum world_frustum,camera_frustum;
bool ref;
};
#endif
+31
View File
@@ -0,0 +1,31 @@
#include "std.hpp"
#include "skinmodel.hpp"
SkinModel::SkinModel(){
setRenderSpace( RENDER_SPACE_WORLD );
}
void SkinModel::setBones( const vector<Object*> &bones ){
_bones=bones;
_surf_bones.resize( _bones.size() );
}
void SkinModel::render( const RenderContext &rc ){
int k;
for( k=0;k<_bones.size();++k ){
Object *obj=_bones[k];
_surf_bones[k].coord_tform=obj->getRenderTform();
_surf_bones[k].normal_tform=_surf_bones[k].coord_tform.m.cofactor();
}
const MeshModel::SurfaceList &_surfs=getSurfaces();
for( k=0;k<_surfs.size();++k ){
Surface *surf=_surfs[k];
if( gxMesh *mesh=surf->getMesh( _surf_bones ) ){
enqueue( mesh,0,surf->numVertices(),0,surf->numTriangles(),surf->getBrush() );
}
}
}
+19
View File
@@ -0,0 +1,19 @@
#ifndef SKINMODEL_H
#define SKINMODEL_H
#include "meshmodel.hpp"
class SkinModel : public MeshModel{
vector<Object*> _bones;
vector<Surface::Bone> _surf_bones;
public:
SkinModel();
void setBones( const vector<Object*> &bones );
//Model interface
virtual void render( const RenderContext &rc );
};
#endif
+139
View File
@@ -0,0 +1,139 @@
#include "std.hpp"
#include "sprite.hpp"
extern float stats3d[];
static float null[]={0,0,0};
static float tex_coords0[2][2]={ {0,0},{0,0} };
static float tex_coords1[2][2]={ {1,0},{1,0} };
static float tex_coords2[2][2]={ {1,1},{1,1} };
static float tex_coords3[2][2]={ {0,1},{0,1} };
extern gxRuntime *gx_runtime;
extern gxGraphics *gx_graphics;
static gxMesh *mesh;
static int mesh_size;
static vector<int> mesh_indices;
static int allocIndex(){
if( !mesh_indices.size() ){
if( mesh_size ) gx_graphics->freeMesh( mesh );
for( int k=0;k<256;++k ){
mesh_indices.push_back( mesh_size++ );
}
mesh=gx_graphics->createMesh( mesh_size*4,mesh_size*2,0 );
}
int n=mesh_indices.back();
mesh_indices.pop_back();
return n;
}
static void freeIndex( int n ){
mesh_indices.push_back( n );
if( mesh_indices.size()!=mesh_size ) return;
gx_graphics->freeMesh( mesh );
mesh_indices.clear();
mesh_size=0;
}
Sprite::Sprite():
view_mode(VIEW_MODE_FREE),
xhandle(0),yhandle(0),
rot(0),xscale(1),yscale(1),captured(false){
setRenderSpace( RENDER_SPACE_WORLD );
mesh_index=allocIndex();
}
Sprite::Sprite( const Sprite &t ):
Model(t),
view_mode(t.view_mode),
xhandle(t.xhandle),yhandle(t.yhandle),
rot(t.rot),xscale(t.xscale),yscale(t.yscale),captured(false){
mesh_index=allocIndex();
}
Sprite::~Sprite(){
freeIndex( mesh_index );
}
void Sprite::setRotation( float angle ){
rot=angle;
}
void Sprite::setScale( float x,float y ){
xscale=x;yscale=y;
}
void Sprite::setHandle( float x,float y ){
xhandle=x;yhandle=y;
}
void Sprite::setViewmode( int mode ){
view_mode=mode;
}
void Sprite::capture(){
Model::capture();
r_rot=rot;
r_xscale=xscale;
r_yscale=yscale;
captured=true;
}
bool Sprite::beginRender( float tween ){
Model::beginRender( tween );
if( tween==1 || !captured ){
r_rot=rot;
r_xscale=xscale;
r_yscale=yscale;
}else{
r_rot=(rot-r_rot)*tween+r_rot;
r_xscale=(xscale-r_xscale)*tween+r_xscale;
r_yscale=(yscale-r_yscale)*tween+r_yscale;
}
return true;
}
bool Sprite::render( const RenderContext &rc ){
Transform t=getRenderTform();
if( view_mode==VIEW_MODE_FREE ){
t.m=rc.getCameraTform().m;
}else if( view_mode==VIEW_MODE_UPRIGHT ){
t.m.k=rc.getCameraTform().m.k;t.m.orthogonalize();
}else if( view_mode==VIEW_MODE_UPRIGHT2 ){
t.m=yawMatrix( matrixYaw( rc.getCameraTform().m ) ) * t.m;
}
t.m=t.m * rollMatrix( r_rot ) * scaleMatrix( r_xscale,r_yscale,1 );
static Vector verts[4];
verts[0]=t * Vector( -1-xhandle, 1-yhandle,0 );
verts[1]=t * Vector( 1-xhandle, 1-yhandle,0 );
verts[2]=t * Vector( 1-xhandle,-1-yhandle,0 );
verts[3]=t * Vector( -1-xhandle,-1-yhandle,0 );
if( !rc.getWorldFrustum().cull( verts,4 ) ) return false;
mesh->lock( false );
int fv=mesh_index*4,ft=mesh_index*2;
mesh->setVertex( fv+0,&verts[0].x,null,tex_coords0 );
mesh->setVertex( fv+1,&verts[1].x,null,tex_coords1 );
mesh->setVertex( fv+2,&verts[2].x,null,tex_coords2 );
mesh->setVertex( fv+3,&verts[3].x,null,tex_coords3 );
if( rc.isReflected() ){
mesh->setTriangle( ft+0,0,2,1 );
mesh->setTriangle( ft+1,0,3,2 );
}else{
mesh->setTriangle( ft+0,0,1,2 );
mesh->setTriangle( ft+1,0,2,3 );
}
mesh->unlock();
enqueue( mesh,fv,4,ft,2 );
return false;
}
+44
View File
@@ -0,0 +1,44 @@
#ifndef SPRITE_H
#define SPRITE_H
#include "model.hpp"
#include "brush.hpp"
#include "../gxruntime/gxmesh.hpp"
class Sprite : public Model{
public:
enum{
VIEW_MODE_FREE=1, //visible from any angle
VIEW_MODE_FIXED=2, //visible only from front
VIEW_MODE_UPRIGHT=3, //upright tree-style
VIEW_MODE_UPRIGHT2=4 //better upright tree-style
};
Sprite();
Sprite( const Sprite &t );
~Sprite();
Sprite *getSprite(){ return this; }
Entity *clone(){ return new Sprite( *this ); }
void capture();
bool beginRender( float tween );
void setRotation( float angle );
void setScale( float x_scale,float y_scale );
void setHandle( float x,float y );
void setViewmode( int mode );
bool render( const RenderContext &rc );
private:
float xhandle,yhandle;
float rot,xscale,yscale;
float r_rot,r_xscale,r_yscale;
int view_mode,mesh_index;
bool captured;
};
#endif
+2
View File
@@ -0,0 +1,2 @@
#include "std.hpp"
+20
View File
@@ -0,0 +1,20 @@
#ifndef STD_H
#define STD_H
#pragma warning( disable:4786 )
#include "config.hpp"
#include "stdutil.hpp"
#include "gxruntime.hpp"
#include <set>
#include <map>
#include <string>
#include <vector>
#include <iostream>
#include <fstream>
using namespace std;
#endif
+238
View File
@@ -0,0 +1,238 @@
#include "std.hpp"
#include "surface.hpp"
extern gxGraphics *gx_graphics;
static Surface::Monitor nop_mon;
Surface::Surface():
mesh(0),mesh_vs(0),mesh_ts(0),valid_vs(0),valid_ts(0),mon( &nop_mon ){
}
Surface::Surface( Monitor *m ):
mesh(0),mesh_vs(0),mesh_ts(0),valid_vs(0),valid_ts(0),mon(m){
}
Surface::~Surface(){
if( mesh ) gx_graphics->freeMesh( mesh );
}
void Surface::setBrush( const Brush &b ){
brush=b;
++mon->brush_changes;
}
void Surface::setName( const string &n ){
name=n;
}
void Surface::clear( bool verts,bool tris ){
if( verts ){ vertices.clear();valid_vs=0; }
if( tris ){ triangles.clear();valid_ts=0; }
++mon->geom_changes;
}
void Surface::addVertices( const vector<Vertex> &verts ){
vertices.insert( vertices.end(),verts.begin(),verts.end() );
++mon->geom_changes;
}
void Surface::setColor( int n,const Vector &v ){
int r=floor(v.x*255);if(r<0)r=0;else if(r>255)r=255;
int g=floor(v.y*255);if(g<0)g=0;else if(g>255)g=255;
int b=floor(v.z*255);if(b<0)b=0;else if(b>255)b=255;
unsigned argb=0xff000000|(r<<16)|(g<<8)|b;
vertices[n].color=argb;
if( n<valid_vs ) valid_vs=n;
}
Vector Surface::getColor( int n )const{
float r=(vertices[n].color&0x00ff0000)>>16;
float g=(vertices[n].color&0x0000ff00)>>8;
float b= vertices[n].color&0x000000ff;
return Vector( r/255.0f,g/255.0f,b/255.0f );
}
void Surface::addTriangles( const vector<Triangle> &tris ){
triangles.insert( triangles.end(),tris.begin(),tris.end() );
}
void Surface::updateNormals(){
int k;
map<Vector,Vector> norm_map;
for( k=0;k<triangles.size();++k ){
const Triangle &t=triangles[k];
const Vector &v0=vertices[t.verts[0]].coords;
const Vector &v1=vertices[t.verts[1]].coords;
const Vector &v2=vertices[t.verts[2]].coords;
Vector n=(v1-v0).cross(v2-v0);
if( n.length()<= FLT_EPSILON ) continue;
n.normalize();
norm_map[v0]+=n;
norm_map[v1]+=n;
norm_map[v2]+=n;
}
for( k=0;k<vertices.size();++k ){
Vertex *v=&vertices[k];
v->normal=norm_map[v->coords].normalized();
}
}
gxMesh *Surface::getMesh(){
if( mesh && mesh->dirty() ) valid_vs=0;
if( valid_vs==vertices.size() && valid_ts==triangles.size() ) return mesh;
valid_vs=valid_ts=0;
if( mesh_vs<vertices.size() || mesh_ts<triangles.size() ){
if( mesh ){
gx_graphics->freeMesh( mesh );
mesh_vs=vertices.size()+mesh_vs/2;
mesh_ts=triangles.size()+mesh_ts/2;
}else{
mesh_vs=vertices.size();
mesh_ts=triangles.size();
}
mesh=gx_graphics->createMesh( mesh_vs,mesh_ts,0 );
}
mesh->lock( true );
for( ;valid_vs<vertices.size();++valid_vs ){
mesh->setVertex( valid_vs,&vertices[valid_vs] );
}
for( ;valid_ts<triangles.size();++valid_ts ){
const Triangle &t=triangles[valid_ts];
mesh->setTriangle( valid_ts,t.verts[0],t.verts[1],t.verts[2] );
}
mesh->unlock();
return mesh;
}
gxMesh *Surface::getMesh( const vector<Bone> &bones ){
valid_vs=valid_ts=0;
if( mesh_vs<vertices.size() || mesh_ts<triangles.size() ){
if( mesh ) gx_graphics->freeMesh( mesh );
mesh_vs=vertices.size();
mesh_ts=triangles.size();
mesh=gx_graphics->createMesh( mesh_vs,mesh_ts,0 );
}
mesh->lock( true );
for( ;valid_vs<vertices.size();++valid_vs ){
const Vertex &v=vertices[valid_vs];
if( v.bone_bones[0]==255 ){
//no bone!
const Bone &bone=bones[0];
mesh->setVertex( valid_vs,bone.coord_tform * v.coords,bone.normal_tform * v.normal,v.color,v.tex_coords );
}else if( v.bone_bones[1]==255 ){
//one bone only
const Bone &bone=bones[v.bone_bones[0]];
mesh->setVertex( valid_vs,bone.coord_tform * v.coords,bone.normal_tform * v.normal,v.color,v.tex_coords );
}else{
const Vertex &v=vertices[valid_vs];
//two or more bones
Vector tv,tn;
for( int n=0;n<MAX_SURFACE_BONES;++n ){
if( v.bone_bones[n]==255 ) break;
const Bone &bone=bones[v.bone_bones[n]];
tv+=bone.coord_tform * v.coords * v.bone_weights[n];
tn+=bone.normal_tform * v.normal * v.bone_weights[n];
}
mesh->setVertex( valid_vs,tv,tn.normalized(),v.color,v.tex_coords );
}
}
for( ;valid_ts<triangles.size();++valid_ts ){
const Triangle &t=triangles[valid_ts];
mesh->setTriangle( valid_ts,t.verts[0],t.verts[1],t.verts[2] );
}
mesh->unlock();
return mesh;
}
/*
gxMesh *Surface::getMesh(){
if( mesh && mesh->dirty() ) valid_vs=0;
if( valid_vs==vertices.size() && valid_ts==triangles.size() ) return mesh;
valid_vs=0;
valid_ts=0;
if( mesh ){
int maxvs=mesh->maxVerts(),maxts=mesh->maxTris();
if( maxvs<vertices.size() || maxts<triangles.size() ){
gx_graphics->freeMesh( mesh );
mesh=gx_graphics->createMesh( vertices.size()+maxvs/2,triangles.size()+maxts/2,0 );
}
}else if( vertices.size() || triangles.size() ){
mesh=gx_graphics->createMesh( vertices.size(),triangles.size(),0 );
}
mesh->lock( true );
for( ;valid_vs<vertices.size();++valid_vs ){
mesh->setVertex( valid_vs,&vertices[valid_vs] );
}
for( ;valid_ts<triangles.size();++valid_ts ){
const Triangle &t=triangles[valid_ts];
mesh->setTriangle( valid_ts,t.verts[0],t.verts[1],t.verts[2] );
}
mesh->unlock();
return mesh;
}
gxMesh *Surface::getMesh( const vector<Bone> &bones,gxMesh *mesh ){
valid_vs=0;
valid_ts=0;
if( mesh ){
int maxvs=mesh->maxVerts(),maxts=mesh->maxTris();
if( maxvs<vertices.size() || maxts<triangles.size() ){
gx_graphics->freeMesh( mesh );
mesh=gx_graphics->createMesh( vertices.size(),triangles.size(),0 );
}
}else if( vertices.size() || triangles.size() ){
mesh=gx_graphics->createMesh( vertices.size(),triangles.size(),0 );
}
mesh->lock( true );
for( ;valid_vs<vertices.size();++valid_vs ){
const Vertex &v=vertices[valid_vs];
if( v.bone_bones[0]==255 ){
//no bone!
const Bone &bone=bones[0];
mesh->setVertex( valid_vs,bone.coord_tform * v.coords,bone.normal_tform * v.normal,v.color,v.tex_coords );
}else if( v.bone_bones[1]==255 ){
//one bone only
const Bone &bone=bones[v.bone_bones[0]];
mesh->setVertex( valid_vs,bone.coord_tform * v.coords,bone.normal_tform * v.normal,v.color,v.tex_coords );
}else{
const Vertex &v=vertices[valid_vs];
//two or more bones
Vector tv,tn;
for( int n=0;n<MAX_SURFACE_BONES;++n ){
if( v.bone_bones[n]==255 ) break;
const Bone &bone=bones[v.bone_bones[n]];
tv+=bone.coord_tform * v.coords * v.bone_weights[n];
tn+=bone.normal_tform * v.normal * v.bone_weights[n];
}
mesh->setVertex( valid_vs,tv,tn.normalized(),v.color,v.tex_coords );
}
}
for( ;valid_ts<triangles.size();++valid_ts ){
const Triangle &t=triangles[valid_ts];
mesh->setTriangle( valid_ts,t.verts[0],t.verts[1],t.verts[2] );
}
mesh->unlock();
return mesh;
}
*/
+116
View File
@@ -0,0 +1,116 @@
#ifndef SURFACE_H
#define SURFACE_H
#include "model.hpp"
#define MAX_SURFACE_BONES 4
class Surface{
public:
struct Vertex{
Vector coords;
Vector normal;
unsigned color;
float tex_coords[2][2];
unsigned char bone_bones[MAX_SURFACE_BONES];
float bone_weights[MAX_SURFACE_BONES];
Vertex():color(~0){
bone_bones[0]=255;
memset(tex_coords,0,sizeof(tex_coords));
}
bool operator<( const Vertex &t )const{
return memcmp( this,&t,sizeof(*this) )==-1;
}
};
struct Triangle{
unsigned short verts[3];
};
struct Bone{
Transform coord_tform;
Matrix normal_tform;
};
struct Monitor{
int brush_changes,geom_changes;
};
Surface();
Surface( Monitor *mon );
~Surface();
void setName( const string &t );
void setBrush( const Brush &b );
void clear( bool verts,bool tris );
void addVertex( const Vertex &v ){
vertices.push_back(v);
++mon->geom_changes;
}
void setVertex( int n,const Vertex &v ){
vertices[n]=v;
if( n<valid_vs ) valid_vs=n;
++mon->geom_changes;
}
void setCoords( int n,const Vector &v ){
vertices[n].coords=v;
if( n<valid_vs ) valid_vs=n;
++mon->geom_changes;
}
void setNormal( int n,const Vector &v ){
vertices[n].normal=v;
if( n<valid_vs ) valid_vs=n;
}
void setColor( int n,unsigned argb ){
vertices[n].color=argb;
if( n<valid_vs ) valid_vs=n;
}
void setTexCoords( int n,const Vector &v,int i ){
vertices[n].tex_coords[i][0]=v.x;
vertices[n].tex_coords[i][1]=v.y;
if( n<valid_vs ) valid_vs=n;
}
void addTriangle( const Triangle &t ){
triangles.push_back(t);
++mon->geom_changes;
}
void setTriangle( int n,const Triangle &t ){
triangles[n]=t;
if( n<valid_ts ) valid_ts=n;
++mon->geom_changes;
}
Vector getColor( int index )const;
void setColor( int index,const Vector &v );
void addVertices( const vector<Vertex> &verts );
void addTriangles( const vector<Triangle> &tris );
void updateNormals();
gxMesh *getMesh();
gxMesh *getMesh( const vector<Bone> &bones );
string getName()const{ return name; }
const Brush &getBrush()const{ return brush; }
int numVertices()const{ return vertices.size(); }
int numTriangles()const{ return triangles.size(); }
const Vertex &getVertex( int n )const{ return vertices[n]; }
const Triangle &getTriangle( int n )const{ return triangles[n]; }
private:
Brush brush;
string name;
gxMesh *mesh;
vector<Vertex> vertices;
vector<Triangle> triangles;
int mesh_vs,mesh_ts;
int valid_vs,valid_ts;
Monitor *mon;
};
#endif
+41
View File
@@ -0,0 +1,41 @@
#include "std.hpp"
#include "terrain.hpp"
#include "terrainrep.hpp"
Terrain::Terrain( int size_shift ):
rep( new TerrainRep( size_shift ) ){
}
Terrain::~Terrain(){
delete rep;
}
void Terrain::setDetail( int n,bool m ){
rep->setDetail( n,m );
}
void Terrain::setShading( bool t ){
rep->setShading( t );
}
void Terrain::setHeight( int x,int z,float h,bool realtime ){
if( x>=0 && z>=0 && x<=rep->getSize() && z<=rep->getSize() ) rep->setHeight( x,z,h,realtime );
}
int Terrain::getSize()const{
return rep->getSize();
}
float Terrain::getHeight( int x,int z )const{
return (x>=0 && z>=0 && x<=rep->getSize() && z<=rep->getSize() ) ? rep->getHeight( x,z ) : 0;
}
bool Terrain::render( const RenderContext &rc ){
rep->render( this,rc );
return false;
}
bool Terrain::collide( const Line &line,float radius,Collision *curr_coll,const Transform &tf ){
return rep->collide( line,radius,curr_coll,tf );
}
+33
View File
@@ -0,0 +1,33 @@
#ifndef TERRAIN_H
#define TERRAIN_H
#include "model.hpp"
struct TerrainRep;
class Terrain : public Model{
public:
Terrain( int size_shift );
~Terrain();
Terrain *getTerrain(){ return this; }
void setDetail( int n,bool morph );
void setHeight( int x,int z,float h,bool realtime );
void setShading( bool shading );
int getSize()const;
float getHeight( int x,int z )const;
//model interface
bool render( const RenderContext &rc );
//object interface
bool collide( const Line &line,float radius,Collision *curr_coll,const Transform &tf );
private:
TerrainRep *rep;
};
#endif
View File
+582
View File
@@ -0,0 +1,582 @@
#include "std.hpp"
#include "terrainrep.hpp"
#include <queue>
extern gxRuntime *gx_runtime;
extern gxGraphics *gx_graphics;
extern float stats3d[10];
static Vector eye_vec;
static Plane eye_plane;
static const Vector up_normal( 0,1,0 );
static TerrainRep::Tri *tri_pool;
static const TerrainRep *curr;
static Frustum frustum;
static int out_cnt,proc_cnt,clip_cnt;
static float proj_epsilon= FLT_EPSILON; //.01f;
struct TerrainRep::Cell{
unsigned char height;
};
struct TerrainRep::Error{
unsigned char error,bound;
};
int TerrainRep::getSize()const{
return cell_size;
}
float TerrainRep::getHeight( int x,int z )const{
return cells[((z&cell_mask)<<cell_shift)|(x&cell_mask)].height/255.0f;
}
struct TerrainRep::Vert{
short x,z;
Vector v;
float src_y;
Vert(){
}
Vert( int x,int z ):x(x),z(z),v( (float)x,curr->getHeight(x,z),(float)z){
src_y=v.y;
}
Vert(int x, int z, float sy) :x(x), z(z), v((float)x, curr->getHeight(x, z), (float)z), src_y(sy) {
}
};
static int vert_cnt,max_verts;
static TerrainRep::Vert *verts,*next_vert;
struct TerrainRep::Tri{
int id;
short clip,v0,v1,v2;
Tri *e0,*e1,*e2;
float proj_err;
Tri(){
}
Tri( int id,int clip,int v0,int v1,int v2,Tri *e0=0,Tri *e1=0,Tri *e2=0 ):
id(id),clip(clip),
v0(v0),v1(v1),v2(v2),
e0(e0),e1(e1),e2(e2),proj_err(0){
}
void *operator new( size_t sz ){
static const int GROW=64;
if( !tri_pool ){
tri_pool=new Tri[GROW];
for( int k=0;k<GROW-1;++k ) tri_pool[k].e0=&tri_pool[k+1];
tri_pool[GROW-1].e0=0;
}
Tri *t=tri_pool;
tri_pool=t->e0;
return t;
}
void operator delete( void *q ){
Tri *t=(Tri*)q;
t->e0=tri_pool;
tri_pool=t;
}
void unlink(){
if( e0 ){
if( e0->e0==this ) e0->e0=0;
else if( e0->e1==this ) e0->e1=0;
else e0->e2=0;
}
if( e1 ){
if( e1->e0==this ) e1->e0=0;
else if( e1->e1==this ) e1->e1=0;
else e1->e2=0;
}
if( e2 ){
if( e2->e0==this ) e2->e0=0;
else if( e2->e1==this ) e2->e1=0;
else e2->e2=0;
}
}
};
struct TriComp{
bool operator()( TerrainRep::Tri *a,TerrainRep::Tri *b )const{ return a->proj_err<b->proj_err; }
};
struct TriQue : public priority_queue<TerrainRep::Tri*,vector<TerrainRep::Tri*>,TriComp>{
vector<TerrainRep::Tri*> &getVector(){ return c; }
const vector<TerrainRep::Tri*> &getVector()const{ return c; }
};
static TriQue tri_que;
static vector<TerrainRep::Tri*> tris;
static bool clip( const Line &l,const Box &box ){
static const Vector normals[]={
Vector( 1,0,0 ),
Vector( 0,0,1 ),
Vector( 0,-1,0 ),
Vector( -1,0,0 ),
Vector( 0,0,-1 ),
Vector( 0,1,0 )
};
Vector v0=l.o,v1=l.o+l.d;
for( int k=0;k<6;++k ){
Vector t=box.corner(k);
const Vector &n=normals[k];
float d0=n.dot( v0-t ),d1=n.dot( v1-t );
if( d0<0 ){
if( d1<0 ) return false;
v0+=(v1-v0)*( d0/(d0-d1) );
}else if( d1<0 ){
v1+=(v0-v1)*( d1/(d1-d0) );
}
}
return true;
}
TerrainRep::TerrainRep( int n ):
cell_shift(n),cell_size(1<<n),cell_mask((1<<n)-1),
end_tri_id( (1<<n)*(1<<n)*2 ),
shading(false),mesh(0),detail(0),morph(true){
cells=new Cell[cell_size*cell_size];
errors=new Error[end_tri_id];
setDetail( 2000,false );
clear();
}
TerrainRep::~TerrainRep(){
if( mesh ) gx_graphics->freeMesh( mesh );
delete[] errors;
delete[] cells;
}
void TerrainRep::clear(){
memset( cells,0,cell_size*cell_size*sizeof(Cell) );
memset( errors,0,end_tri_id*sizeof(Error) );
errs_valid=true;
}
void TerrainRep::setDetail( int n,bool m ){
morph=m;
if( n==detail ) return;
detail=n;
n+=32;
if( n>max_verts ){
delete[] verts;
max_verts=n;
verts=new Vert[max_verts];
}
if( mesh ) gx_graphics->freeMesh( mesh );
mesh_verts=mesh_tris=n;
mesh=gx_graphics->createMesh( mesh_verts,mesh_tris,0 );
}
void TerrainRep::setShading( bool t ){
shading=t;
}
void TerrainRep::setHeight( int x,int z,float h,bool realtime ){
cells[((z&cell_mask)<<cell_shift)|(x&cell_mask)].height=h*255.0f;
if( !errs_valid ) return;
if( realtime ){
Vert v0(0,0),v1(cell_size,0),v2(cell_size,cell_size),v3(0,cell_size);
calcErr( 2,x,z,v1,v2,v0 );
calcErr( 3,x,z,v3,v0,v2 );
return;
}
errs_valid=false;
}
Vector TerrainRep::getNormal( int x,int z )const{
Vector
vt( x,getHeight(x,z),z ),
v0( x,getHeight(x,z-1),z-1 ),
v1( x+1,getHeight(x+1,z),z ),
v2( x,getHeight(x,z+1),z+1 ),
v3( x-1,getHeight(x-1,z),z );
return (
Plane( vt,v1,v0 ).n+
Plane( vt,v2,v1 ).n+
Plane( vt,v3,v2 ).n+
Plane( vt,v0,v3 ).n ).normalized();
}
void TerrainRep::insert( Tri *t ){
++proc_cnt;
//quicker clip check for 'thin' triangles...
if( t->id>=end_tri_id || !errors[t->id].error ){
if( t->clip & 63 ){
++clip_cnt;
Vector e0( verts[t->v0].v ),e1( verts[t->v1].v ),e2( verts[t->v2].v );
for( int n=0;n<6;++n ){
if( !( t->clip & (1<<n) ) ) continue;
const Plane &p=frustum.getPlane( n );
if( p.distance(e0)<0 && p.distance(e1)<0 && p.distance(e2)<0 ){
t->unlink();
delete t;
++out_cnt;
return;
}
}
}
t->clip|=128;
tris.push_back( t );
++out_cnt;
return;
}
//clip?
if( t->id<end_tri_id/2 && (t->clip & 63) ){
++clip_cnt;
Vector e0( verts[t->v0].v ),e1( verts[t->v1].v ),e2( verts[t->v2].v );
Vector e3(e0),e4(e1),e5(e2);
e0.y=e1.y=e2.y=0;
e3.y=e4.y=e5.y=errors[t->id].bound/255.0f;
for( int n=0;n<6;++n ){
int mask=1<<n;
if( !(t->clip & mask) ) continue;
const Plane &p=frustum.getPlane( n );
int q=
(p.distance( e0 )>=0)+(p.distance( e1 )>=0)+(p.distance( e2 )>=0)+
(p.distance( e3 )>=0)+(p.distance( e4 )>=0)+(p.distance( e5 )>=0);
if( !q ){
t->unlink();
delete t;
++out_cnt;
return;
}
if( q==6 ) t->clip&=~mask;
}
}
if( t->clip & 128 ){
t->clip|=128;
tris.push_back( t );
}else{
Vector v=Vector( verts[t->v1].v+verts[t->v2].v )/2;
// float d=eye_plane.distance( v );
float d=eye_vec.distance( v );
if( d<FLT_EPSILON ) d= FLT_EPSILON;
t->proj_err=errors[t->id].error/d;
if( t->proj_err>proj_epsilon ){
tri_que.push( t );
}else{
t->clip|=128;
tris.push_back( t );
}
}
++out_cnt;
}
void TerrainRep::split( Tri *t ){
if( t->e2 && t->e2->e2!=t ) split( t->e2 );
int tv=vert_cnt++;
if( tv>=max_verts ){
max_verts+=max_verts/2+32;
Vert *t=verts;
verts=new Vert[max_verts];
memcpy( verts,t,sizeof(Vert)*tv );
next_vert=verts+tv;
}
Vert *vert=next_vert++;
vert->v.x=vert->x=(verts[t->v1].x+verts[t->v2].x)/2;
vert->v.z=vert->z=(verts[t->v1].z+verts[t->v2].z)/2;
vert->src_y=(verts[t->v1].v.y+verts[t->v2].v.y)/2;
vert->v.y=getHeight( vert->x,vert->z );
Tri *tl=new Tri( t->id*2,t->clip,tv,t->v2,t->v0,0,0,t->e0 );
if( Tri *p=tl->e2 ){
if( p->e0==t ) p->e0=tl;
else if( p->e1==t ) p->e1=tl;
else p->e2=tl;
}
Tri *tr=new Tri( t->id*2+1,t->clip,tv,t->v0,t->v1,0,tl,t->e1 );
tl->e0=tr;
if( Tri *p=tr->e2 ){
if( p->e0==t ) p->e0=tr;
else if( p->e1==t ) p->e1=tr;
else p->e2=tr;
}
if( Tri *b=t->e2 ){
Tri *br=new Tri( b->id*2,b->clip,tv,b->v2,b->v0,0,tr,b->e0 );
tr->e0=br;
if( Tri *p=br->e2 ){
if( p->e0==b ) p->e0=br;
else if( p->e1==b ) p->e1=br;
else p->e2=br;
}
Tri *bl=new Tri( b->id*2+1,b->clip,tv,b->v0,b->v1,tl,br,b->e1 );
tl->e1=br->e0=bl;
if( Tri *p=bl->e2 ){
if( p->e0==b ) p->e0=bl;
else if( p->e1==b ) p->e1=bl;
else p->e2=bl;
}
b->id=0;
--out_cnt;
insert( br );
insert( bl );
}
t->id=0;
--out_cnt;
insert( tl );
insert( tr );
}
TerrainRep::Error TerrainRep::calcErr( int id,const Vert &v0,const Vert &v1,const Vert &v2 )const{
Error et;
float y=v0.v.y;
if( v1.v.y>y ) y=v1.v.y;
if( v2.v.y>y ) y=v2.v.y;
et.error = 0;
et.bound = y>=1 ? 255 : ceil(y*255.0f);
if( id>=end_tri_id ) return et;
Vert tv( (v1.x+v2.x)/2,(v1.z+v2.z)/2 );
float e=fabs(tv.v.y-(v1.v.y+v2.v.y)/2);
et.error= e>=1 ? 255 : ceil( (e- FLT_EPSILON)*255.0f );
Error el=calcErr( id*2,tv,v2,v0 );
Error er=calcErr( id*2+1,tv,v0,v1 );
if( el.error>et.error ) et.error=el.error;
if( er.error>et.error ) et.error=er.error;
if( el.bound>et.bound ) et.bound=el.bound;
if( er.bound>et.bound ) et.bound=er.bound;
return errors[id]=et;
}
TerrainRep::Error TerrainRep::calcErr( int id,int x,int z,const Vert &v0,const Vert &v1,const Vert &v2 )const{
Error et;
float y=v0.v.y;
if( v1.v.y>y ) y=v1.v.y;
if( v2.v.y>y ) y=v2.v.y;
et.error = 0;
et.bound = y>=1 ? 255 : ceil(y*255.0f);
if( id>=end_tri_id ) return et;
//is x/z inside this triangle?
int dx,dz;
dx=-(v1.z-v0.z);dz=(v1.x-v0.x);
if( (x-v0.x)*dx+(z-v0.z)*dz<0 ) return errors[id];
dx=-(v2.z-v1.z);dz=(v2.x-v1.x);
if( (x-v1.x)*dx+(z-v1.z)*dz<0 ) return errors[id];
dx=-(v0.z-v2.z);dz=(v0.x-v2.x);
if( (x-v2.x)*dx+(z-v2.z)*dz<0 ) return errors[id];
Vert tv( (v1.x+v2.x)/2,(v1.z+v2.z)/2 );
float e=fabs(tv.v.y-(v1.v.y+v2.v.y)/2);
et.error= e>=1 ? 255 : ceil( (e- FLT_EPSILON)*255.0f );
Error el=calcErr( id*2,x,z,tv,v2,v0 );
Error er=calcErr( id*2+1,x,z,tv,v0,v1 );
if( el.error>et.error ) et.error=el.error;
if( er.error>et.error ) et.error=er.error;
if( el.bound>et.bound ) et.bound=el.bound;
if( er.bound>et.bound ) et.bound=er.bound;
return errors[id]=et;
}
void TerrainRep::validateErrs()const{
if( errs_valid ) return;
Vert v0(0,0),v1(cell_size,0),v2(cell_size,cell_size),v3(0,cell_size);
calcErr( 2,v1,v2,v0 );
calcErr( 3,v3,v0,v2 );
errs_valid=true;
}
void TerrainRep::render( Model *model,const RenderContext &rc ){
curr=this;
validateErrs();
new( &frustum ) Frustum( rc.getWorldFrustum(),-model->getRenderTform() );
eye_plane=frustum.getPlane( Frustum::PLANE_NEAR );
eye_vec=frustum.getVertex( Frustum::VERT_EYE );
vert_cnt=4;next_vert=verts;
out_cnt=proc_cnt=clip_cnt=0;
tri_que.getVector().clear();
tris.clear();
new(next_vert++) Vert(0,0);
new(next_vert++) Vert(cell_size,0);
new(next_vert++) Vert(cell_size,cell_size);
new(next_vert++) Vert(0,cell_size);
Tri *t0=new Tri( 2,0x3f,1,2,0 );
Tri *t1=new Tri( 3,0x3f,3,0,2 );
t0->e2=t1;t1->e2=t0;
insert( t0 );
insert( t1 );
while( tri_que.size() && out_cnt<detail ){
Tri *t=tri_que.top();
tri_que.pop();
if( t->id ) split( t );
delete t;
}
int k;
const vector<Tri*> &q_tris=tri_que.getVector();
if( !mesh ) out_cnt=0;
if( !out_cnt ){
for( k=0;k<tris.size();++k ) delete tris[k];
for( k=0;k<q_tris.size();++k ) delete q_tris[k];
return;
}
int err_cnt=0;
for( k=0;k<q_tris.size();++k ){
Tri *t=q_tris[k];
if( t->id ){ tris.push_back( t );++err_cnt; }
else delete t;
}
if( morph ){
if( int morph_cnt=err_cnt/4 ){
if( morph_cnt>vert_cnt ) morph_cnt=vert_cnt;
float t=0,morph_step=1.0f/morph_cnt;
for( int vn=vert_cnt-morph_cnt;vn<vert_cnt;++vn ){
verts[vn].v.y+=(verts[vn].src_y-verts[vn].v.y)*t;
t+=morph_step;
}
}
}
int tri_cnt=tris.size();
if( vert_cnt>mesh_verts || tri_cnt>mesh_tris ){
int vc=vert_cnt+32;if( vc>mesh_verts ) mesh_verts=vc;
int tc=tri_cnt+32;if( tc>mesh_tris ) mesh_tris=tc;
if( mesh ) gx_graphics->freeMesh( mesh );
mesh=gx_graphics->createMesh( mesh_verts,mesh_tris,0 );
}
mesh->lock( true );
int tc=0,vc=0;
if( !shading ){
for( k=0;k<vert_cnt;++k ){
const Vert &t=verts[k];
const Vector &v=t.v;
float tex_coords[2][2]={ {v.x,cell_size-v.z},{v.x,cell_size-v.z} };
mesh->setVertex( vc++,&v.x,&up_normal.x,tex_coords );
}
}else{
for( k=0;k<vert_cnt;++k ){
const Vert &t=verts[k];
const Vector &v=t.v;
float tex_coords[2][2]={ {v.x,cell_size-v.z},{v.x,cell_size-v.z} };
Vector normal=getNormal( v.x,v.z );
mesh->setVertex( vc++,&v.x,&normal.x,tex_coords );
}
}
for( k=0;k<tri_cnt;++k ){
Tri *t=tris[k];
if( t->id ) mesh->setTriangle( tc++,t->v0,t->v2,t->v1 );
delete t;
}
mesh->unlock();
static int mvc,mtc;
if( vc>mvc ) mvc=vc;
if( tc>mtc ) mtc=tc;
stats3d[1]=mvc;
stats3d[2]=mtc;
model->enqueue( mesh,0,vc,0,tc );
}
bool TerrainRep::collide( const Line &line,Collision *curr_coll,const Transform &tform,int id,const Vert &v0,const Vert &v1,const Vert &v2,const Line &l )const{
Box b( v0.v );
b.update( v1.v );
b.update( v2.v );
if( id>=end_tri_id || !errors[id].error ){
return ::clip( l,b ) ?
curr_coll->triangleCollide( line,0,tform*v0.v,tform*v2.v,tform*v1.v )
: false;
}
b.a.y=0;
b.b.y=errors[id].bound/255.0f;
if( !::clip( l,b ) ) return false;
Vert tv( (v1.x+v2.x)/2,(v1.z+v2.z)/2 );
return
collide( line,curr_coll,tform,id*2,tv,v2,v0,l )|
collide( line,curr_coll,tform,id*2+1,tv,v0,v1,l );
}
bool TerrainRep::collide( const Line &line,float radius,Collision *curr_coll,const Transform &tform,int id,const Vert &v0,const Vert &v1,const Vert &v2,const Box &box )const{
Box b( v0.v );
b.update( v1.v );
b.update( v2.v );
if( id>=end_tri_id || !errors[id].error ){
if( v0.v==v1.v || v0.v==v2.v || v1.v==v2.v ){
gx_runtime->debugLog( "OUCH!" );
}
return b.overlaps(box) ?
curr_coll->triangleCollide( line,radius,tform*v0.v,tform*v2.v,tform*v1.v )
: false;
}
b.a.y=0;
b.b.y=errors[id].bound/255.0f;
if( !b.overlaps( box ) ) return false;
Vert tv( (v1.x+v2.x)/2,(v1.z+v2.z)/2 );
return
collide( line,radius,curr_coll,tform,id*2,tv,v2,v0,box )|
collide( line,radius,curr_coll,tform,id*2+1,tv,v0,v1,box );
}
bool TerrainRep::collide( const Line &line,float radius,Collision *curr_coll,const Transform &tform )const{
curr=this;
validateErrs();
Vert v0(0,0),v1(cell_size,0),v2(cell_size,cell_size),v3(0,cell_size);
if( !radius ){
Line l=-tform * line;
return
collide( line,curr_coll,tform,2,v1,v2,v0,l )|
collide( line,curr_coll,tform,3,v3,v0,v2,l );
}
//create local box
Box b( line );
b.expand( radius );
Box box=-tform * b;
return
collide( line,radius,curr_coll,tform,2,v1,v2,v0,box )|
collide( line,radius,curr_coll,tform,3,v3,v0,v2,box );
}
+55
View File
@@ -0,0 +1,55 @@
#ifndef TERRAINREP_H
#define TERRAINREP_H
#include <queue>
#include "model.hpp"
struct TerrainRep{
public:
TerrainRep( int cell_shift );
~TerrainRep();
void clear();
void setShading( bool shading );
void setDetail( int n,bool morph );
void setHeight( int x,int z,float h,bool realtime );
void setTile( int x,int z,const Brush &brush );
void render( Model *model,const RenderContext &rc );
int getSize()const;
float getHeight( int x,int z )const;
bool collide( const Line &line,float radius,Collision *curr_coll,const Transform &tform )const;
struct Tri;
struct Vert;
private:
struct Cell;
struct Error;
friend struct Tri;
friend struct Vert;
Cell *cells;
Error *errors;
gxMesh *mesh;
int cell_size,cell_shift,cell_mask;
int end_tri_id,detail,mesh_verts,mesh_tris;
bool morph,shading;
mutable bool errs_valid;
void insert( Tri *t );
void split( Tri *t );
void validateErrs()const;
Vector getNormal( int x,int z )const;
Error calcErr( int id,const Vert &v0,const Vert &v1,const Vert &v2 )const;
Error calcErr( int id,int x,int z,const Vert &v0,const Vert &v1,const Vert &v2 )const;
bool collide( const Line &line,Collision *curr_coll,const Transform &tform,int id,const Vert &v0,const Vert &v1,const Vert &v2,const Line &l )const;
bool collide( const Line &line,float radius,Collision *curr_coll,const Transform &tform,int id,const Vert &v0,const Vert &v1,const Vert &v2,const Box &box )const;
};
#endif
+195
View File
@@ -0,0 +1,195 @@
#include "std.hpp"
#include "geom.hpp"
#include "texture.hpp"
#include "cachedtexture.hpp"
#include "../gxruntime/gxgraphics.hpp"
extern gxScene *gx_scene;
extern gxGraphics *gx_graphics;
struct Filter {
string t;
int flags;
Filter(const string &t, int flags) :t(t), flags(flags) {
}
};
static vector<Filter> filters;
static int filterFile(const string &t, int flags) {
//check filters...
string l = tolower(t);
for (size_t k = 0; k < filters.size(); ++k) {
if (l.find(filters[k].t) != string::npos) {
flags |= filters[k].flags;
}
}
return flags;
}
struct Texture::Rep {
int ref_cnt;
CachedTextureFactory cached_tex;
vector<gxCanvas*> tex_frames;
int tex_blend, tex_flags;
bool transparent;
float sx, sy, tx, ty, rot;
bool mat_used, mat_valid;
gxScene::Matrix matrix;
Rep(int w, int h, int flags, int cnt) :
ref_cnt(1), cached_tex(w, h, flags, cnt),
tex_blend(gxScene::BLEND_MULTIPLY), tex_flags(0),
sx(1), sy(1), tx(0), ty(0), rot(0), mat_used(false) {
tex_frames = cached_tex.getFrames();
transparent =
(flags & gxCanvas::CANVAS_TEX_ALPHA) &&
!(flags & gxCanvas::CANVAS_TEX_MASK);
memset(&matrix, 0, sizeof(matrix));
}
Rep(const string &f, int flags, int w, int h, int first, int cnt) :
ref_cnt(1), cached_tex(f, flags, w, h, first, cnt),
tex_blend(gxScene::BLEND_MULTIPLY), tex_flags(0),
sx(1), sy(1), tx(0), ty(0), rot(0), mat_used(false) {
tex_frames = cached_tex.getFrames();
transparent =
(flags & gxCanvas::CANVAS_TEX_ALPHA) &&
!(flags & gxCanvas::CANVAS_TEX_MASK);
memset(&matrix, 0, sizeof(matrix));
}
Rep(const Rep &t) :
ref_cnt(1), cached_tex(t.cached_tex), tex_frames(t.tex_frames),
tex_blend(t.tex_blend), tex_flags(t.tex_flags),
sx(t.sx), sy(t.sy), tx(t.tx), ty(t.ty), rot(t.rot),
mat_used(t.mat_used), mat_valid(t.mat_valid), matrix(t.matrix),
transparent(t.transparent) {
}
};
Texture::Texture() :rep(0) {
}
Texture::Texture(const string &f, int flags) {
flags = filterFile(f, flags) | gxCanvas::CANVAS_TEXTURE;
if (flags & gxCanvas::CANVAS_TEX_MASK) flags |= gxCanvas::CANVAS_TEX_RGB | gxCanvas::CANVAS_TEX_ALPHA;
rep = new Rep(f, flags, 0, 0, 0, 1);
}
Texture::Texture(const string &f, int flags, int w, int h, int first, int cnt) {
flags = filterFile(f, flags) | gxCanvas::CANVAS_TEXTURE;
if (flags & gxCanvas::CANVAS_TEX_MASK) flags |= gxCanvas::CANVAS_TEX_RGB | gxCanvas::CANVAS_TEX_ALPHA;
rep = new Rep(f, flags, w, h, first, cnt);
}
Texture::Texture(int w, int h, int flags, int cnt) {
flags |= gxCanvas::CANVAS_TEXTURE;
if (flags & gxCanvas::CANVAS_TEX_MASK) flags |= gxCanvas::CANVAS_TEX_RGB | gxCanvas::CANVAS_TEX_ALPHA;
rep = new Rep(w, h, flags, cnt);
}
Texture::Texture(const Texture &t) :
rep(t.rep) {
if (rep) ++rep->ref_cnt;
}
Texture::~Texture() {
if (rep && !--rep->ref_cnt) delete rep;
}
Texture &Texture::operator=(const Texture &t) {
if (t.rep) ++t.rep->ref_cnt;
if (rep && !--rep->ref_cnt) delete rep;
rep = t.rep;
return *this;
}
void Texture::setScale(float u_scale, float v_scale) {
if (!rep) return;
rep->sx = u_scale; rep->sy = v_scale;
rep->mat_valid = false;
rep->mat_used = true;
}
void Texture::setRotation(float angle) {
if (!rep) return;
rep->rot = angle;
rep->mat_valid = false;
rep->mat_used = true;
}
void Texture::setPosition(float u_pos, float v_pos) {
if (!rep) return;
rep->tx = u_pos;
rep->ty = v_pos;
rep->mat_valid = false;
rep->mat_used = true;
}
void Texture::setBlend(int blend) {
if (!rep) return;
rep->tex_blend = blend;
}
void Texture::setFlags(int flags) {
if (!rep) return;
rep->tex_flags = flags;
}
bool Texture::isTransparent()const {
return rep ? rep->transparent : false;
}
gxCanvas *Texture::getCanvas(int n)const {
return rep && n >= 0 && n < rep->tex_frames.size() ? rep->tex_frames[n] : 0;
}
int Texture::getCanvasFlags()const {
return rep && rep->tex_frames.size() ? rep->tex_frames[0]->getFlags() : 0;
}
CachedTextureFactory *Texture::getCachedTexture()const {
return rep ? &rep->cached_tex : 0;
}
int Texture::getBlend()const {
return rep ? rep->tex_blend : 0;
}
int Texture::getFlags()const {
return rep ? rep->tex_flags : 0;
}
const gxScene::Matrix *Texture::getMatrix()const {
if (!rep || !rep->mat_used) return 0;
if (!rep->mat_valid) {
float c = cos(rep->rot), s = sin(rep->rot);
rep->matrix.elements[0][0] = c*rep->sx;
rep->matrix.elements[1][0] = s*rep->sx;
rep->matrix.elements[0][1] = -s*rep->sy;
rep->matrix.elements[1][1] = c*rep->sy;
rep->matrix.elements[2][0] = rep->tx;
rep->matrix.elements[2][1] = rep->ty;
rep->mat_valid = true;
}
return &rep->matrix;
}
bool Texture::operator<(const Texture &t)const {
if (rep && t.rep) return rep->cached_tex < t.rep->cached_tex;
return rep < t.rep;
}
void Texture::clearFilters() {
filters.clear();
}
void Texture::addFilter(const string &t, int flags) {
filters.push_back(Filter(tolower(t), flags));
}
+46
View File
@@ -0,0 +1,46 @@
#ifndef TEXTURE_H
#define TEXTURE_H
#include <string>
#include "cachedtexture.hpp"
#include "../gxruntime/gxcanvas.hpp"
class Texture{
public:
Texture();
Texture( const std::string &file,int flags );
Texture( const std::string &file,int flags,int w,int h,int first,int cnt );
Texture( int width,int height,int flags,int cnt );
Texture( const Texture &texture );
~Texture();
Texture &operator=( const Texture &texture );
void setScale( float u_scale,float v_scale );
void setRotation( float rot );
void setPosition( float u_pos,float v_pos );
void setBlend( int blend );
void setFlags( int flags );
int getCanvasFlags()const;
gxCanvas *getCanvas( int frame )const;
const gxScene::Matrix *getMatrix()const;
int getBlend()const;
int getFlags()const;
CachedTextureFactory *getCachedTexture()const;
bool isTransparent()const;
bool operator<( const Texture &t )const;
static void clearFilters();
static void addFilter( const std::string &filter,int flags );
private:
struct Rep;
Rep *rep;
};
#endif
View File
+32
View File
@@ -0,0 +1,32 @@
Blitz 3D User Guide.
Welcome to the wonder world of 3D!
Entities
********
At the heart of Blitz3D lies the concept of an 'Entity'.
An Entity is a 'thing' in the 3D world that has a position and an orientation.
Blitz3D supports many kinds of entities:
* Camera
* Light
* Pivot
* Mesh
* Sprite
* Plane
* Mirror
* Terrain
+649
View File
@@ -0,0 +1,649 @@
#include "std.hpp"
#include <queue>
#include "world.hpp"
//0=tris compared for collision
//1=max proj err of terrain
float stats3d[10];
extern gxScene *gx_scene;
extern gxRuntime *gx_runtime;
static std::list<Object*> s_objectsEnabled, s_objectsVisible;
static void StaticEnumerateEnabled() {
s_objectsEnabled.clear();
for (Entity *e = Entity::GetEntityOrphans(); e; e = e->GetSuccessor()) {
e->EnumerateEnabled(s_objectsEnabled);
}
}
static void StaticEnumerateVisible() {
s_objectsVisible.clear();
for (Entity *e = Entity::GetEntityOrphans(); e; e = e->GetSuccessor()) {
e->EnumerateVisible(s_objectsVisible);
}
}
/******************************* Update *******************************/
static vector<Object*> _objsByType[1000];
static vector<ObjCollision*> free_colls, used_colls;
static ObjCollision *allocObjColl(Object *with, const Vector &coords, const Collision &coll) {
ObjCollision *c;
if (free_colls.size()) {
c = free_colls.back();
free_colls.pop_back();
} else {
c = new ObjCollision();
}
used_colls.push_back(c);
c->with = with;
c->coords = coords;
c->collision = coll;
return c;
}
static void collided(Object *src, Object *dest, const Line &line, const Collision &coll, float y_scale) {
ObjCollision *c;
const Vector &coords = line*coll.time - coll.normal*src->getCollisionRadii().x;
c = allocObjColl(dest, coords, coll);
c->coords.y *= y_scale;
src->addCollision(c);
c = allocObjColl(src, coords, coll);
c->coords.y *= y_scale;
dest->addCollision(c);
}
void World::clearCollisions() {
for (int k = 0; k < 1000; ++k) {
_collInfo[k].clear();
}
}
void World::addCollision(int src_type, int dst_type, int method, int response) {
vector<CollInfo> &info = _collInfo[src_type];
for (size_t k = 0; k < info.size(); ++k) {
const CollInfo &t = info[k];
if (dst_type == t.dst_type) return;
}
CollInfo co = { dst_type,method,response };
_collInfo[src_type].push_back(co);
}
bool World::hitTest(const Line &line, float radius, Object *obj, const Transform &tf, int method, Collision *curr_coll) {
switch (method) {
case COLLISION_METHOD_SPHERE:
return curr_coll->sphereCollide(line, radius, tf.v, obj->getCollisionRadii().x);
case COLLISION_METHOD_POLYGON:
return obj->collide(line, radius, curr_coll, tf);
case COLLISION_METHOD_BOX:
Transform t = tf;
t.m.i.normalize(); t.m.j.normalize(); t.m.k.normalize();
if (curr_coll->boxCollide(~t*line, radius, obj->getCollisionBox())) {
curr_coll->normal = t.m*curr_coll->normal;
return true;
}
}
return false;
}
bool World::CheckLineOfSight(Object *src, Object *dest) {
StaticEnumerateEnabled();
Object *coll_obj = 0;
Collision curr_coll;
Line line(src->GetWorldPosition(), dest->GetWorldPosition() - src->GetWorldPosition());
for (Object* obj : s_objectsEnabled) {
if (obj == src || obj == dest || !obj->getPickGeometry() || !obj->getObscurer()) continue;
if (hitTest(line, 0, obj, obj->GetWorldTransform(), obj->getPickGeometry(), &curr_coll)) {
return false;
}
}
return true;
}
Object *World::traceRay(const Line &line, float radius, ObjCollision *curr_coll) {
StaticEnumerateEnabled();
Object *coll_obj = 0;
for (Object* obj : s_objectsEnabled) {
if (!obj->getPickGeometry()) continue;
if (hitTest(line, radius, obj, obj->GetWorldTransform(), obj->getPickGeometry(), &curr_coll->collision)) {
coll_obj = obj;
}
}
if (curr_coll->with = coll_obj) {
curr_coll->coords = line*curr_coll->collision.time - curr_coll->collision.normal*radius;
}
return coll_obj;
}
//
// NEW VERSION
//
void World::collide(Object *src) {
Vector dv = src->GetWorldTransform().v;
Vector sv = src->getPrevWorldTform().v;
if (sv == dv) {
if (dv.x != sv.x || dv.y != sv.y || dv.z != sv.z) {
src->SetWorldPosition(sv);
}
return;
}
Vector panic = sv;
static Transform y_tform;
const Vector &radii = src->getCollisionRadii();
float radius = radii.x, inv_y_scale;
float y_scale = inv_y_scale = y_tform.m.j.y = 1;
if (radii.x != radii.y) {
y_scale = y_tform.m.j.y = radius / radii.y;
inv_y_scale = 1 / y_scale;
sv.y *= y_scale;
dv.y *= y_scale;
}
int n_hit = 0;
Plane planes[2];
Line coll_line(sv, dv - sv);
Vector dir = coll_line.d;
float td = coll_line.d.length();
float td_xz = Vector(coll_line.d.x, 0, coll_line.d.z).length();
const vector<CollInfo> &collinfos = _collInfo[src->getCollisionType()];
int hits = 0;
for (;;) {
Collision coll;
Object *coll_obj = 0;
vector<CollInfo>::const_iterator coll_it, coll_info;
for (coll_it = collinfos.begin(); coll_it != collinfos.end(); ++coll_it) {
// const std::list<Object*> &dst_objs = _objsByType[coll_it->dst_type];
for (Object* dst : /*dst_objs*/_objsByType[coll_it->dst_type]) {
if (src == dst) continue;
const Transform &dst_tform = dst->getPrevWorldTform();
if (y_scale == 1) {
if (hitTest(
coll_line, radius, dst, dst_tform,
coll_it->method, &coll)) {
coll_obj = dst;
coll_info = coll_it;
}
} else {
if (hitTest(
coll_line, radius, dst, y_tform * dst_tform,
coll_it->method, &coll)) {
coll_obj = dst;
coll_info = coll_it;
}
}
}
}
if (!coll_obj) break;
//register collision
if (++hits == WORLD_COLLISION_HITS) {
// exit(0);
break;
}
collided(src, coll_obj, coll_line, coll, inv_y_scale);
Plane coll_plane(coll_line*coll.time, coll.normal);
coll_plane.d -= COLLISION_FLT_EPSILON;
coll.time = coll_plane.t_intersect(coll_line);
if (coll.time > 0) {// && fabs(coll.normal.dot( coll_line.d ))>FLT_EPSILON ){
//update source position - ONLY IF AHEAD!
sv = coll_line*coll.time;
td *= 1 - coll.time;
td_xz *= 1 - coll.time;
}
if (coll_info->response == COLLISION_RESPONSE_STOP) {
dv = sv;
break;
}
//find nearest point on plane to dest
Vector nv = coll_plane.nearest(dv);
if (n_hit == 0) {
dv = nv;
} else if (n_hit == 1) {
if (planes[0].distance(nv) >= 0) {
dv = nv; n_hit = 0;
} else if (fabs(planes[0].n.dot(coll_plane.n)) < 1 - FLT_EPSILON) {
dv = coll_plane.intersect(planes[0]).nearest(dv);
} else {
//SQUISHED!
//exit(0);
hits = WORLD_COLLISION_HITS; break;
}
} else if (planes[0].distance(nv) >= 0 && planes[1].distance(nv) >= 0) {
dv = nv; n_hit = 0;
} else {
dv = sv; break;
}
Vector dd(dv - sv);
//going behind initial direction? really necessary?
if (dd.dot(dir) <= 0) { dv = sv; break; }
if (coll_info->response == COLLISION_RESPONSE_SLIDE) {
float d = dd.length();
if (d <= FLT_EPSILON) { dv = sv; break; }
if (d > td) dd *= td / d;
} else if (coll_info->response == COLLISION_RESPONSE_SLIDEXZ) {
float d = Vector(dd.x, 0, dd.z).length();
if (d <= FLT_EPSILON) { dv = sv; break; }
if (d > td_xz) dd *= td_xz / d;
}
coll_line.o = sv;
coll_line.d = dd; dv = sv + dd;
planes[n_hit++] = coll_plane;
}
if (hits) {
if (hits < WORLD_COLLISION_HITS) {
dv.y *= inv_y_scale;
src->SetWorldPosition(dv);
} else {
src->SetWorldPosition(panic);
}
}
}
/*
//
// OLD VERSION
//
void World::collide( Object *src ){
static const int MAX_HITS=100;
Vector dv=src->getWorldTform().v;
Vector sv=src->getPrevWorldTform().v;
if( sv==dv ){
if( dv.x!=sv.x || dv.y!=sv.y || dv.z!=sv.z ){
src->setWorldPosition( sv );
}
return;
}
Vector panic=sv;
static Transform y_tform;
const Vector &radii=src->getCollisionRadii();
float radius=radii.x,inv_y_scale;
float y_scale=inv_y_scale=y_tform.m.j.y=1;
if( radii.x!=radii.y ){
y_scale=y_tform.m.j.y=radius/radii.y;
inv_y_scale=1/y_scale;
sv.y*=y_scale;
dv.y*=y_scale;
}
int n_hit=0;
Plane planes[2];
Line coll_line( sv,dv-sv );
Vector dir=coll_line.d;
float td=coll_line.d.length();
float td_xz=Vector( coll_line.d.x,0,coll_line.d.z ).length();
const vector<CollInfo> &collinfos=_collInfo[src->getCollisionType()];
int hits=0;
while( hits<MAX_HITS ){
Collision coll;
Object *coll_obj=0;
vector<CollInfo>::const_iterator coll_it,coll_info;
for( coll_it=collinfos.begin();coll_it!=collinfos.end();++coll_it ){
vector<Object*>::const_iterator dst_it;
const vector<Object*> &dst_objs=_objsByType[coll_it->dst_type];
for( dst_it=dst_objs.begin();dst_it!=dst_objs.end();++dst_it ){
Object *dst=*dst_it;
if( src==dst ) continue;
const Transform &dst_tform=dst->getPrevWorldTform();
if( y_scale==1 ){
if( hitTest(
coll_line,radius,dst,dst_tform,
coll_it->method,&coll ) ){
coll_obj=dst;
coll_info=coll_it;
}
}else{
if( hitTest(
coll_line,radius,dst,y_tform * dst_tform,
coll_it->method,&coll ) ){
coll_obj=dst;
coll_info=coll_it;
}
}
}
}
if( !coll_obj ) break;
//register collision
++hits;
collided( src,coll_obj,coll_line,coll,inv_y_scale );
//create collision plane
Plane coll_plane( coll_line*coll.time,coll.normal );
//move plane out a bit (cough)
coll_plane.d-=.001f;
if( fabs(coll.normal.dot( coll_line.d ))>FLT_EPSILON ){
float t=coll_plane.t_intersect( coll_line );
//update source position - ONLY IF AHEAD!
if( t>0 ){
sv=coll_line*t;
td*=1-coll.time;
td_xz*=1-coll.time;
}
}
//STOP?
if( coll_info->response==COLLISION_RESPONSE_STOP ){
dv=sv;
break;
}
//find nearest point on plane to dest
Vector nv=coll_plane.nearest( dv );
//SLIDE!
if( n_hit==0 ){
dv=nv;
}else if( n_hit==1 ){
if( planes[0].distance(nv)>=0 ){
dv=nv;n_hit=0;
}else if( fabs( planes[0].n.dot( coll_plane.n ) )<1-FLT_EPSILON ){
dv=coll_plane.intersect( planes[0] ).nearest( dv );
}else{
hits=MAX_HITS;break;
}
}else if( planes[0].distance(nv)>=0 && planes[1].distance(nv)>=0 ){
dv=nv;n_hit=0;
}else{
dv=sv;break;
}
Vector dd( dv-sv );
//going behind initial direction? really necessary?
if( dd.dot( dir )<=0 ){ dv=sv;break; }
if( coll_info->response==COLLISION_RESPONSE_SLIDE ){
float d=dd.length();
if( d<=FLT_EPSILON ){ dv=sv;break; }
if( d>td ) dd*=td/d;
}else if( coll_info->response==COLLISION_RESPONSE_SLIDEXZ ){
float d=Vector( dd.x,0,dd.z ).length();
if( d<=FLT_EPSILON ){ dv=sv;break; }
if( d>td_xz ) dd*=td_xz/d;
}
coll_line.o=sv;
coll_line.d=dd;dv=sv+dd;
planes[n_hit++]=coll_plane;
}
if( hits ){
if( hits<MAX_HITS ){
dv.y*=inv_y_scale;
src->setWorldPosition( dv );
}else{
src->setWorldPosition( panic );
}
}
}
*/
void World::update(float elapsed) {
stats3d[0] = 0;
for (; used_colls.size(); used_colls.pop_back()) {
free_colls.push_back(used_colls.back());
}
StaticEnumerateEnabled();
for (Object* o : s_objectsEnabled) {
if (int n = o->getCollisionType()) {
_objsByTypeSwappable[n].push_back(o);
}
o->beginUpdate(elapsed);
if (o->getCollisionType()) collide(o);
o->endUpdate();
}
for (int k = 0; k < WORLD_COLLISION_TYPES; ++k) {
_objsByTypeSwappable[k].swap(_objsByType[k]);
_objsByTypeSwappable[k].clear();
}
}
/****************************** Render *********************************/
static Transform cam_tform; //current camera transform
static vector<gxLight*> _lights;
static vector<Mirror*> _mirrors;
static vector<Listener*> _listeners;
struct OrderComp {
bool operator()(Object *a, Object *b) {
return a->getOrder() < b->getOrder();
}
};
struct TransComp {
bool operator()(Model *a, Model *b)const {
return
cam_tform.v.distance(a->getRenderTform().v) <
cam_tform.v.distance(b->getRenderTform().v);
}
};
static vector<Model*> ord_mods, unord_mods;
static priority_queue<Model*, vector<Model*>, OrderComp> ord_que;
static priority_queue<Camera*, vector<Camera*>, OrderComp> cam_que;
static priority_queue<Model*, vector<Model*>, TransComp> transparents;
void World::capture() {
StaticEnumerateVisible();
for (Object* o : s_objectsVisible) {
o->capture();
}
}
void World::render(float tween) {
//set render tweens, and build ordered and unordered model lists...
ord_mods.clear();
unord_mods.clear();
s_objectsVisible.clear();
_lights.clear();
_mirrors.clear();
_listeners.clear();
StaticEnumerateVisible();
for (Object* o : s_objectsVisible) {
if (!o->beginRender(tween)) continue;
if (Light *t = o->getLight()) _lights.push_back(t->getGxLight());
else if (Camera *t = o->getCamera()) cam_que.push(t);
else if (Mirror *t = o->getMirror()) _mirrors.push_back(t);
else if (Listener *t = o->getListener()) _listeners.push_back(t);
else if (Model *t = o->getModel()) {
if (t->getOrder()) ord_que.push(t);
else unord_mods.push_back(t);
}
}
for (; ord_que.size(); ord_que.pop()) ord_mods.push_back(ord_que.top());
// gx_runtime->debugLog( "RenderWorld" );
if (!gx_scene->begin(_lights)) return;
for (; cam_que.size(); cam_que.pop()) {
Camera *cam = cam_que.top();
if (!cam->beginRenderFrame()) continue;
vector<Mirror*>::const_iterator mir_it;
for (mir_it = _mirrors.begin(); mir_it != _mirrors.end(); ++mir_it) {
render(cam, *mir_it);
}
render(cam, 0);
}
gx_scene->end();
// gx_runtime->debugLog( "End RenderWorld" );
vector<Listener*>::const_iterator lis_it;
for (lis_it = _listeners.begin(); lis_it != _listeners.end(); ++lis_it) {
(*lis_it)->renderListener();
}
}
void World::render(Camera *cam, Mirror *mirror) {
if (mirror) {
const Transform &t = mirror->getRenderTform();
cam_tform = t * Transform(scaleMatrix(1, -1, 1)) * -t * cam->getRenderTform();
gx_scene->setFlippedTris(true);
} else {
cam_tform = cam->getRenderTform();
gx_scene->setFlippedTris(false);
}
//set camera matrix
gx_scene->setViewMatrix((gxScene::Matrix*)&(-cam_tform));
//initialize render context
RenderContext rc(cam_tform, cam->getFrustum(), mirror != 0);
//draw everything in order
size_t ord = 0;
gx_scene->setZMode(gxScene::ZMODE_DISABLE);
while (ord < ord_mods.size() && ord_mods[ord]->getOrder()>0) {
Model *mod = ord_mods[ord++];
if (!mod->doAutoFade(cam_tform.v)) continue;
render(mod, rc);
flushTransparent();
}
gx_scene->setZMode(gxScene::ZMODE_NORMAL);
for (size_t k = 0; k < unord_mods.size(); ++k) {
Model *mod = unord_mods[k];
if (!mod->doAutoFade(cam_tform.v)) continue;
render(mod, rc);
}
gx_scene->setZMode(gxScene::ZMODE_CMPONLY);
flushTransparent();
gx_scene->setZMode(gxScene::ZMODE_DISABLE);
while (ord < ord_mods.size()) {
Model *mod = ord_mods[ord++];
if (!mod->doAutoFade(cam_tform.v)) continue;
render(mod, rc);
flushTransparent();
}
}
void World::render(Model *mod, const RenderContext &rc) {
bool trans = mod->render(rc);
if (mod->queueSize(Model::QUEUE_OPAQUE)) {
if (mod->getRenderSpace() == Model::RENDER_SPACE_LOCAL) {
gx_scene->setWorldMatrix((gxScene::Matrix*)&mod->getRenderTform());
} else {
gx_scene->setWorldMatrix(0);
}
mod->renderQueue(Model::QUEUE_OPAQUE);
}
if (trans || mod->queueSize(Model::QUEUE_TRANSPARENT)) {
transparents.push(mod);
}
}
void World::flushTransparent() {
bool local = true;
for (; transparents.size(); transparents.pop()) {
Model *mod = transparents.top();
if (mod->getRenderSpace() == Model::RENDER_SPACE_LOCAL) {
gx_scene->setWorldMatrix((gxScene::Matrix*)&mod->getRenderTform());
local = true;
} else if (local) {
gx_scene->setWorldMatrix(0);
local = false;
}
mod->renderQueue(Model::QUEUE_TRANSPARENT);
}
}
+60
View File
@@ -0,0 +1,60 @@
#ifndef WORLD_H
#define WORLD_H
#include <list>
#include "model.hpp"
#include "camera.hpp"
#include "light.hpp"
#include "mirror.hpp"
#include "listener.hpp"
#define WORLD_COLLISION_TYPES 16
#define WORLD_COLLISION_HITS 4
class World{
public:
//collision methods
enum{
COLLISION_METHOD_SPHERE=1,
COLLISION_METHOD_POLYGON=2,
COLLISION_METHOD_BOX=3
};
//collision actions
enum{
COLLISION_RESPONSE_NONE=0,
COLLISION_RESPONSE_STOP=1,
COLLISION_RESPONSE_SLIDE=2,
COLLISION_RESPONSE_SLIDEXZ=3,
};
void clearCollisions();
void addCollision( int src_type,int dest_type,int method,int response );
void update( float elapsed );
void capture();
void render( float tween );
bool CheckLineOfSight( Object *src,Object *dest );
bool hitTest( const Line &line,float radius,Object *obj,const Transform &tf,int method,Collision *curr_coll );
Object *traceRay( const Line &line,float radius,ObjCollision *curr_coll );
private:
struct CollInfo{
int dst_type,method,response;
};
vector<CollInfo> _collInfo[WORLD_COLLISION_TYPES];
std::list<Object*> _objsByType[WORLD_COLLISION_TYPES];
std::list<Object*> _objsByTypeSwappable[WORLD_COLLISION_TYPES];
void collide( Object *src );
void render( Camera *c,Mirror *m );
void render( Model *m,const RenderContext &rc );
void flushTransparent();
};
#endif