Implemented accurate bounding sphere to triangle
collision. The old code had an implementation but it missed tons of cases. For 100,000 random points, the old way would process that in about 12+ ms. The new way does it in about 18+ ms... but the old way missed 643 collisions (180 versus 823). Plus, with the new way accurate contact normal and contact point can be provided trivially... so it is.
This commit is contained in:
parent
1ad6a57b32
commit
d5e20d53d0
@ -793,26 +793,195 @@ public class BoundingSphere extends BoundingVolume {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int collideWithTri(Triangle tri, CollisionResults results) {
|
||||||
|
TempVars tvars = TempVars.get();
|
||||||
|
try {
|
||||||
|
|
||||||
|
// Much of this is based on adaptation from this algorithm:
|
||||||
|
// http://realtimecollisiondetection.net/blog/?p=103
|
||||||
|
// ...mostly the stuff about eliminating sqrts wherever
|
||||||
|
// possible.
|
||||||
|
|
||||||
|
// Math is done in center-relative space.
|
||||||
|
Vector3f a = tri.get1().subtract(center, tvars.vect1);
|
||||||
|
Vector3f b = tri.get2().subtract(center, tvars.vect2);
|
||||||
|
Vector3f c = tri.get3().subtract(center, tvars.vect3);
|
||||||
|
|
||||||
|
Vector3f ab = b.subtract(a, tvars.vect4);
|
||||||
|
Vector3f ac = c.subtract(a, tvars.vect5);
|
||||||
|
|
||||||
|
// Check the plane... if it doesn't intersect the plane
|
||||||
|
// then it doesn't intersect the triangle.
|
||||||
|
Vector3f n = ab.cross(ac, tvars.vect6);
|
||||||
|
float d = a.dot(n);
|
||||||
|
float e = n.dot(n);
|
||||||
|
if( d * d > radius * radius * e ) {
|
||||||
|
// Can't possibly intersect
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We intersect the verts, or the edges, or the face...
|
||||||
|
|
||||||
|
// First check against the face since it's the most
|
||||||
|
// specific.
|
||||||
|
|
||||||
|
// Calculate the barycentric coordinates of the
|
||||||
|
// sphere center
|
||||||
|
Vector3f v0 = ac;
|
||||||
|
Vector3f v1 = ab;
|
||||||
|
// a was P relative, so p.subtract(a) is just -a
|
||||||
|
// instead of wasting a vector we'll just negate the
|
||||||
|
// dot products below... it's all v2 is used for.
|
||||||
|
Vector3f v2 = a;
|
||||||
|
|
||||||
|
float dot00 = v0.dot(v0);
|
||||||
|
float dot01 = v0.dot(v1);
|
||||||
|
float dot02 = -v0.dot(v2);
|
||||||
|
float dot11 = v1.dot(v1);
|
||||||
|
float dot12 = -v1.dot(v2);
|
||||||
|
|
||||||
|
float invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
|
||||||
|
float u = (dot11 * dot02 - dot01 * dot12) * invDenom;
|
||||||
|
float v = (dot00 * dot12 - dot01 * dot02) * invDenom;
|
||||||
|
|
||||||
|
if( u >= 0 && v >= 0 && (u + v) < 1 ) {
|
||||||
|
// We intersect... and we even know where
|
||||||
|
Vector3f part1 = ac;
|
||||||
|
Vector3f part2 = ab;
|
||||||
|
Vector3f p = center.add(a.add(part1.mult(u)).addLocal(part2.mult(v)));
|
||||||
|
|
||||||
|
CollisionResult r = new CollisionResult();
|
||||||
|
r.setDistance((float)Math.sqrt(d) - radius);
|
||||||
|
r.setContactNormal(n.normalize());
|
||||||
|
r.setContactPoint(p);
|
||||||
|
results.addCollision(r);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the edges looking for the nearest point
|
||||||
|
// that is also less than the radius. We don't care
|
||||||
|
// about points that are farther away than that.
|
||||||
|
Vector3f nearestPt = null;
|
||||||
|
float nearestDist = radius * radius;
|
||||||
|
|
||||||
|
Vector3f base;
|
||||||
|
Vector3f edge;
|
||||||
|
float t;
|
||||||
|
|
||||||
|
// Edge AB
|
||||||
|
base = a;
|
||||||
|
edge = ab;
|
||||||
|
|
||||||
|
t = -edge.dot(base) / edge.dot(edge);
|
||||||
|
if( t >= 0 && t <= 1 ) {
|
||||||
|
Vector3f Q = base.add(edge.mult(t, tvars.vect7), tvars.vect8);
|
||||||
|
float distSq = Q.dot(Q); // distance squared to origin
|
||||||
|
if( distSq < nearestDist ) {
|
||||||
|
nearestPt = Q;
|
||||||
|
nearestDist = distSq;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Edge AC
|
||||||
|
base = a;
|
||||||
|
edge = ac;
|
||||||
|
|
||||||
|
t = -edge.dot(base) / edge.dot(edge);
|
||||||
|
if( t >= 0 && t <= 1 ) {
|
||||||
|
Vector3f Q = base.add(edge.mult(t, tvars.vect7), tvars.vect8);
|
||||||
|
float distSq = Q.dot(Q); // distance squared to origin
|
||||||
|
if( distSq < nearestDist ) {
|
||||||
|
nearestPt = Q;
|
||||||
|
nearestDist = distSq;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Edge BC
|
||||||
|
base = b;
|
||||||
|
Vector3f bc = c.subtract(b);
|
||||||
|
edge = bc;
|
||||||
|
|
||||||
|
t = -edge.dot(base) / edge.dot(edge);
|
||||||
|
if( t >= 0 && t <= 1 ) {
|
||||||
|
Vector3f Q = base.add(edge.mult(t, tvars.vect7), tvars.vect8);
|
||||||
|
float distSq = Q.dot(Q); // distance squared to origin
|
||||||
|
if( distSq < nearestDist ) {
|
||||||
|
nearestPt = Q;
|
||||||
|
nearestDist = distSq;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have a point at all then it is going to be
|
||||||
|
// closer than any vertex to center distance... so we're
|
||||||
|
// done.
|
||||||
|
if( nearestPt != null ) {
|
||||||
|
// We have a hit
|
||||||
|
float dist = FastMath.sqrt(nearestDist);
|
||||||
|
Vector3f cn = nearestPt.divide(-dist);
|
||||||
|
|
||||||
|
CollisionResult r = new CollisionResult();
|
||||||
|
r.setDistance(dist);
|
||||||
|
r.setContactNormal(cn);
|
||||||
|
r.setContactPoint(nearestPt.add(center));
|
||||||
|
results.addCollision(r);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally check each of the triangle corners
|
||||||
|
|
||||||
|
// Vert A
|
||||||
|
base = a;
|
||||||
|
t = base.dot(base); // distance squared to origin
|
||||||
|
if( t < nearestDist ) {
|
||||||
|
nearestDist = t;
|
||||||
|
nearestPt = base;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vert B
|
||||||
|
base = b;
|
||||||
|
t = base.dot(base); // distance squared to origin
|
||||||
|
if( t < nearestDist ) {
|
||||||
|
nearestDist = t;
|
||||||
|
nearestPt = base;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vert C
|
||||||
|
base = c;
|
||||||
|
t = base.dot(base); // distance squared to origin
|
||||||
|
if( t < nearestDist ) {
|
||||||
|
nearestDist = t;
|
||||||
|
nearestPt = base;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( nearestPt != null ) {
|
||||||
|
// We have a hit
|
||||||
|
float dist = FastMath.sqrt(nearestDist);
|
||||||
|
Vector3f cn = nearestPt.divide(-dist);
|
||||||
|
|
||||||
|
CollisionResult r = new CollisionResult();
|
||||||
|
r.setDistance(dist);
|
||||||
|
r.setContactNormal(cn);
|
||||||
|
r.setContactPoint(nearestPt.add(center));
|
||||||
|
results.addCollision(r);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nothing hit... oh, well
|
||||||
|
return 0;
|
||||||
|
} finally {
|
||||||
|
tvars.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public int collideWith(Collidable other, CollisionResults results) {
|
public int collideWith(Collidable other, CollisionResults results) {
|
||||||
if (other instanceof Ray) {
|
if (other instanceof Ray) {
|
||||||
Ray ray = (Ray) other;
|
Ray ray = (Ray) other;
|
||||||
return collideWithRay(ray, results);
|
return collideWithRay(ray, results);
|
||||||
} else if (other instanceof Triangle){
|
} else if (other instanceof Triangle){
|
||||||
Triangle t = (Triangle) other;
|
Triangle t = (Triangle) other;
|
||||||
|
return collideWithTri(t, results);
|
||||||
float r2 = radius * radius;
|
|
||||||
float d1 = center.distanceSquared(t.get1());
|
|
||||||
float d2 = center.distanceSquared(t.get2());
|
|
||||||
float d3 = center.distanceSquared(t.get3());
|
|
||||||
|
|
||||||
if (d1 <= r2 || d2 <= r2 || d3 <= r2) {
|
|
||||||
CollisionResult r = new CollisionResult();
|
|
||||||
r.setDistance(FastMath.sqrt(Math.min(Math.min(d1, d2), d3)) - radius);
|
|
||||||
results.addCollision(r);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
} else {
|
} else {
|
||||||
throw new UnsupportedCollisionException();
|
throw new UnsupportedCollisionException();
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user