diff --git a/src/collider/collider.h b/src/collider/collider.h index fd09cfc..e983e41 100644 --- a/src/collider/collider.h +++ b/src/collider/collider.h @@ -11,6 +11,7 @@ struct CollisionContact { vector3d normal; double depth; int triIdx; - Geom* g1, *g2; + void* userData1, *userData2; + int geomFlag; }; diff --git a/src/collider/collision_space.cpp b/src/collider/collision_space.cpp index 7377489..dac128e 100644 --- a/src/collider/collision_space.cpp +++ b/src/collider/collision_space.cpp @@ -3,7 +3,7 @@ #include "geom_tree.h" CollisionSpace::CollisionSpace(void) { - + sphere.radius = 0; } void CollisionSpace::AddGeom(Geom* geom) { @@ -15,6 +15,35 @@ void CollisionSpace::RemoveGeom(Geom* geom) { m_geoms.remove(geom); } +void CollisionSpace::CollideRaySphere(const vector3d& start, const vector3d& dir, isect_t* isect) { + if(sphere.radius != 0) { + /* Collide with lovely sphere! */ + const vector3d v = start - sphere.pos; + const double b = -vector3d::Dot(v, dir); + double det = (b*b) - vector3d::Dot(v, v) + (sphere.radius*sphere.radius); + if(det > 0) { + det = sqrt(det); + const double i1 = b - det; + const double i2 = b + det; + if(i2 > 0) { + /*if(i1 < 0) { + if(i2 < *dist) { + *dist = i2; + //retval = INPRIM; + retval = true; + } + }*/ + if(i2 > 0) { + if(i1 < isect->dist) { + isect->dist = i1; + isect->triIdx = 0; + } + } + } + } + } +} + void CollisionSpace::TraceRay(const vector3d& start, const vector3d& dir, isect_t* isect) { for(std::list::iterator i = m_geoms.begin(); i != m_geoms.end(); ++i) { if((*i)->IsEnabled()) { @@ -26,6 +55,7 @@ void CollisionSpace::TraceRay(const vector3d& start, const vector3d& dir, isect_ (*i)->GetGeomTree()->TraceRay(modelStart, modelDir, isect); } } + CollideRaySphere(start, dir, isect); } void CollisionSpace::CollideGeoms(Geom* a, void (*callback)(CollisionContact*)) { @@ -36,6 +66,11 @@ void CollisionSpace::CollideGeoms(Geom* a, void (*callback)(CollisionContact*)) bigAabb.min += pos; bigAabb.max += pos; + /* First test this reested sh*t against the planet sphere thing. */ + if(sphere.radius != 0) { + a->CollideSphere(sphere, callback); + } + for(std::list::iterator i = m_geoms.begin(); i != m_geoms.end(); ++i) { if((*i) != a) { Aabb bigAabb2; diff --git a/src/collider/collision_space.h b/src/collider/collision_space.h index 33216db..a9203d7 100644 --- a/src/collider/collision_space.h +++ b/src/collider/collision_space.h @@ -6,6 +6,14 @@ class Geom; struct isect_t; class CollisionContact; +struct Sphere { + vector3d pos; + double radius; + void* userData; +}; + +/* Collision spaces have a bunch of geoms and at most one sphere (for a planet). */ + class CollisionSpace { public: CollisionSpace(void); @@ -13,8 +21,15 @@ public: void RemoveGeom(Geom*); void TraceRay(const vector3d& start, const vector3d& dir, isect_t* isect); void Collide(void (*callback)(CollisionContact*)); + void SetSphere(const vector3d& pos, double radius, void* user_data) { + sphere.pos = pos; sphere.radius = radius; sphere.userData = user_data; + } + private: void CollideGeoms(Geom* a, void (*callback)(CollisionContact*)); + void CollideRaySphere(const vector3d& start, const vector3d& dir, isect_t* isect); std::list m_geoms; + + Sphere sphere; }; diff --git a/src/collider/geom.cpp b/src/collider/geom.cpp index 9a59778..a6fb7c6 100644 --- a/src/collider/geom.cpp +++ b/src/collider/geom.cpp @@ -36,6 +36,47 @@ vector3d Geom::GetPosition(void) const { m_orient[m_orientIdx][14]); } +void Geom::CollideSphere(Sphere& sphere, void(*callback)(CollisionContact*)) { + for(int i = 0; i < m_geomtree->m_numVertices; i++) { + vector3d vtx(&m_geomtree->m_vertices[3*i]); + vector3d from = m_orient[!m_orientIdx] * vtx; + vector3d to = m_orient[m_orientIdx] * vtx; + vector3d dir = to - from; + const double len = dir.Length(); + dir *= 1.0f/len; + + vector3d _from(from.x, from.y, from.z); + vector3d _dir(dir.x, dir.y, dir.z); + + _dir.Normalize(); + + /* Collide with lovely sphere! */ + const vector3d v = _from - sphere.pos; + const double b = -vector3d::Dot(v, dir); + double det = (b*b) - vector3d::Dot(v,v) + (sphere.radius*sphere.radius); + if(det > 0) { + det = sqrt(det); + const double i1 = b - det; + const double i2 = b + det; + if(i2 > 0) { + if(i1 > 0) { + if(i1 < len) { + CollisionContact c; + c.pos = _from + _dir*i1; + c.normal = vector3d::Normalize(v); + c.depth = len - i1; + c.triIdx = 0; + c.userData1 = this->m_data; + c.userData2 = sphere.userData; + c.geomFlag = 0; + (*callback)(&c); + } + } + } + } + } +} + void Geom::Collide(Geom* b, void(*callback)(CollisionContact*)) { m_moved = false; for(int i = 0; i < m_geomtree->m_numVertices; i++) { @@ -66,8 +107,9 @@ void Geom::Collide(Geom* b, void(*callback)(CollisionContact*)) { c.depth = len - isect.dist; c.triIdx = isect.triIdx; - c.g1 = this; - c.g2 = b; + c.userData1 = m_data; + c.userData2 = b->m_data; + c.geomFlag = b->m_geomtree->GetTriFlag(isect.triIdx); (*callback)(&c); } } diff --git a/src/collider/geom.h b/src/collider/geom.h index 36b0767..c6d8ebd 100644 --- a/src/collider/geom.h +++ b/src/collider/geom.h @@ -5,6 +5,7 @@ class GeomTree; class isect_t; class CollisionContact; +class Sphere; class Geom { public: @@ -20,6 +21,7 @@ public: bool HasMoved(void) { return m_moved; } GeomTree* GetGeomTree(void) { return m_geomtree; } void Collide(Geom* b, void(*callback)(CollisionContact*)); + void CollideSphere(Sphere& sphere, void (*callback)(CollisionContact*)); void SetUserData(void* d) { m_data = d; } void* GetUserData(void) { return m_data; } private: diff --git a/src/collider/geom_tree.cpp b/src/collider/geom_tree.cpp index 3350eb1..cbf5c10 100644 --- a/src/collider/geom_tree.cpp +++ b/src/collider/geom_tree.cpp @@ -7,10 +7,12 @@ * You don't need a 'mailbox' as objects only appear once in the hierarchy. * single ray traversal performance is comparable to kd-tree and way faster than bvh. */ -#define MAX_LEAF_PRIMS 2 +#define MAX_LEAF_PRIMS 1 #define MAX_DEPTH 20 #define MAX_SPLITPOS_RETRIES 32 -#define MIN_SPACE_CUTOFF 0.33 +#define MIN_SPACE_CUTOFF 0.5 +//#define DO_SPACE_CUTOFF +#define NODE_ALLOC_MULT 8 #define EPSILON 0.00001 #include @@ -24,6 +26,7 @@ #define MAX(a,b) ((a)>(b) ? (a) : (b)) class DisplayList; +int GeomTree::stats_rayTriIntersections; struct tri_t { int triIdx; @@ -120,8 +123,8 @@ GeomTree::GeomTree(int numVerts, int numTris, float* vertices, int* indices, m_maxAabb.max.x, m_maxAabb.max.y, m_maxAabb.max.z); - m_nodes = new BIHNode[numTris*4]; - m_nodesAllocSize = numTris*4; + m_nodes = new BIHNode[numTris*NODE_ALLOC_MULT]; + m_nodesAllocSize = numTris*NODE_ALLOC_MULT; m_nodesAllocPos = 0; m_triAllocPos = 0; @@ -153,6 +156,7 @@ void GeomTree::BihTreeGhBuild(BIHNode* a_node, Aabb& a_box, Aabb& a_splitBox, in int s1count, s2count, splitAxis, attempt; attempt = 0; +#ifdef DO_SPACE_CUTOFF Aabb realAabb; realAabb.min = vector3d(FLT_MAX, FLT_MAX, FLT_MAX); realAabb.max = vector3d(-FLT_MAX, -FLT_MAX, -FLT_MAX); @@ -227,7 +231,7 @@ void GeomTree::BihTreeGhBuild(BIHNode* a_node, Aabb& a_box, Aabb& a_splitBox, in return; } } - +#endif /* DO_SPACE_CUTOFF */ for(;;) { splitAxis = 0; vector3d splitBoxSize = a_splitBox.max - a_splitBox.min; @@ -468,6 +472,7 @@ pop_bstack: } void GeomTree::RayTriIntersect(const vector3f& origin, const vector3f& dir, int triIdx, isect_t* isect) { + stats_rayTriIntersections++; const vector3f a(&m_vertices[3*m_indices[triIdx]]); const vector3f b(&m_vertices[3*m_indices[triIdx+1]]); const vector3f c(&m_vertices[3*m_indices[triIdx+2]]); diff --git a/src/collider/geom_tree.h b/src/collider/geom_tree.h index 40ab24a..764e62f 100644 --- a/src/collider/geom_tree.h +++ b/src/collider/geom_tree.h @@ -27,6 +27,7 @@ public: const int m_numVertices; const float* m_vertices; + static int stats_rayTriIntersections; private: friend class BIHNode; diff --git a/src/frame.cpp b/src/frame.cpp index 7d26ba5..561dc98 100644 --- a/src/frame.cpp +++ b/src/frame.cpp @@ -91,6 +91,10 @@ Frame::~Frame(void) { void Frame::AddGeom(Geom* g) { m_collisionSpace->AddGeom(g); } void Frame::RemoveGeom(Geom* g) { m_collisionSpace->RemoveGeom(g); } +void Frame::SetPlanetGeom(double radius, Body* obj) { + m_collisionSpace->SetSphere(vector3d(0,0,0), radius, static_cast(obj)); +} + void Frame::ApplyLeavingTransform(matrix4x4d& m) const { m = matrix4x4d::Translation(m_pos) * m_orient * m; } diff --git a/src/frame.h b/src/frame.h index a21fe52..7020cea 100644 --- a/src/frame.h +++ b/src/frame.h @@ -36,6 +36,7 @@ public: void _RemoveGeom(dGeomID g) { dSpaceRemove(m_dSpaceID, g); } void AddGeom(Geom*); void RemoveGeom(Geom*); + void SetPlanetGeom(double radius, Body*); dSpaceID GetSpaceID(void) const { return m_dSpaceID; } CollisionSpace* GetCollisionSpace(void) const { return m_collisionSpace; } void RotateInTimestep(double step); diff --git a/src/planet.cpp b/src/planet.cpp index 887a804..c3cf32c 100644 --- a/src/planet.cpp +++ b/src/planet.cpp @@ -911,8 +911,14 @@ void Planet::Render(const Frame* a_camFrame) { } void Planet::SetFrame(Frame* f) { - if(GetFrame()) GetFrame()->_RemoveGeom(geom); + if(GetFrame()) { + GetFrame()->_RemoveGeom(geom); + GetFrame()->SetPlanetGeom(0,0); + } Body::SetFrame(f); - if(f) f->_AddGeom(geom); + if(f) { + f->_AddGeom(geom); + GetFrame()->SetPlanetGeom(GetRadius(), this); + } } diff --git a/src/sbre_viewer.cpp b/src/sbre_viewer.cpp index 783880f..aeb09d1 100644 --- a/src/sbre_viewer.cpp +++ b/src/sbre_viewer.cpp @@ -160,6 +160,7 @@ float foo[TEXSIZE][TEXSIZE]; float aspectRatio = 1.0; float camera_zoom = 1.0; float distance = 100; +extern int stat_rayTriIntersections; static void raytraceCollMesh(vector3d camPos, vector3d camera_up, vector3d camera_forward, CollisionSpace* space) { memset(foo, 0, sizeof(float)*TEXSIZE*TEXSIZE); @@ -178,6 +179,7 @@ static void raytraceCollMesh(vector3d camPos, vector3d camera_up, vector3d camer float ystep = 1.0f / TEXSIZE; float xpos, ypos; ypos = 0.0f; + GeomTree::stats_rayTriIntersections = 0; Uint32 t = SDL_GetTicks(); for(int y = 0; y < TEXSIZE; y++, ypos += ystep) { @@ -198,7 +200,8 @@ static void raytraceCollMesh(vector3d camPos, vector3d camera_up, vector3d camer } } } - printf("%3f million rays/sec\n", (TEXSIZE*TEXSIZE)/(1000.0*(SDL_GetTicks()-t))); + printf("%.3f million rays/sec, %.2f tri isect tests per ray\n", (TEXSIZE*TEXSIZE)/(1000.0*(SDL_GetTicks()-t)), + GeomTree::stats_rayTriIntersections/(float)(TEXSIZE*TEXSIZE)); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, TEXSIZE, TEXSIZE, 0, GL_LUMINANCE, GL_FLOAT, foo); glDisable(GL_DEPTH_TEST); diff --git a/src/space.cpp b/src/space.cpp index 2320ad1..867c4af 100644 --- a/src/space.cpp +++ b/src/space.cpp @@ -326,7 +326,7 @@ static void dump_contact(const dContact* c) { static bool _OnCollision2(Object* o1, Object* o2, CollisionContact* c) { Body* pb1, *pb2; - int flags = c->g2->GetGeomTree()->GetTriFlag(c->triIdx); + int flags = c->geomFlag; //printf("Collision flags %x (triIdx %d)\n", flags, c->triIdx); /* Geom bodies point to their parents. */ if(o1->IsType(Object::GEOM)) { @@ -369,8 +369,19 @@ static void hitCallback(CollisionContact* c) { contact.fdir1[1] = 0; contact.fdir1[2] = 0; + Object* po1 = static_cast(c->userData1); + Object* po2 = static_cast(c->userData2); + + if(!_OnCollision2(po1, po2, c)) return; + + dBodyID b1 = 0; + dBodyID b2 = 0; + /* Get the dynamics body for each geom. */ + if(po1->IsType(Object::DYNAMICBODY)) b1 = static_cast(po1)->m_body; + if(po2->IsType(Object::DYNAMICBODY)) b2 = static_cast(po2)->m_body; + dJointID j = dJointCreateContact(Space::world, _contactgroup, &contact); - dJointAttach(j, (dBodyID)c->g1->GetUserData(), (dBodyID)c->g2->GetUserData()); + dJointAttach(j, b1, b2); } #if 0