blitz3d/blitz3d/meshcollider.cpp

200 lines
5.1 KiB
C++

#include "std.h"
#include "meshcollider.h"
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=d_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=d_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;
}