Doesn't belong here either

This commit is contained in:
Henrik Rydgard 2012-03-25 00:13:34 +01:00
parent 04b9e6d16c
commit 8c3a48df74
3 changed files with 0 additions and 386 deletions

View File

@ -1,9 +0,0 @@
set(SRCS
collision.cpp
)
add_library(collision STATIC ${SRCS})
if(UNIX)
add_definitions(-fPIC)
endif(UNIX)

View File

@ -1,288 +0,0 @@
#include "base/logging.h"
#include "math/lin/vec3.h"
#include "ray_intersections.h"
#include "collision.h"
#include <string>
// #define COLLISION_ENABLE_SCALING
static std::string V(const Vec3 &v) {
char temp[100];
sprintf(temp, "(%f %f %f)", v.x, v.y, v.z);
return std::string(temp);
}
Collidable::Collidable() {
}
Collidable::~Collidable() {
}
void Collision::Init(const Vec3 &source, const Vec3 &dest, const Vec3 &size) {
this->size = size;
scale.Set(1.0f / size.x, 1.0f / size.y, 1.0f / size.z);
sourcePoint = source.scaledBy(scale);
velocity = (dest - source).scaledBy(scale);
velocityLength = velocity.length();
normalizedVelocity = velocity / velocityLength;
stuck = false;
foundCollision = false;
nearestDistance = velocityLength;
}
void Collision::BeginTransform(const Matrix4x4 &inverse, const Matrix4x4 &transform) {
this->transform = &transform;
untransformed_sourcePoint = sourcePoint;
untransformed_velocity = velocity;
sourcePoint = sourcePoint * inverse;
velocity = velocity.rotatedBy(inverse);
normalizedVelocity = untransformed_velocity.normalized();
}
void Collision::EndTransform() {
sourcePoint = untransformed_sourcePoint;
velocity = untransformed_velocity;
normalizedVelocity = untransformed_velocity.normalized();
this->transform = 0;
}
bool Collision::registerCollision(float t, const Vec3 &polyIPoint) {
// Special handling for multiple contact points
if (t < 0.0f) {
return false;
}
Vec3 ipoint;
if (transform) ipoint = polyIPoint * (*transform);
else ipoint = polyIPoint;
if (t >= -0.0001 && t <= 0.0001 && num_contact_points < MAX_CONTACT_POINTS) {
for (int i = 0; i < num_contact_points; i++) {
// Check if identical - can happen on dupe edges (which we'll later remove).
// TODO: Let's see if we can get rid of this fuzziness.
if (ipoint.distance2To(contact_points[i]) < 0.00001)
goto skip;
}
contact_points[num_contact_points++] = ipoint;
}
skip:
if ((t >= 0.0) && (t <= velocityLength) && (t <= this->nearestDistance)) {
// If we are hit we have a closest hit so far. We save the information
this->nearestDistance = t;
this->nearestPolygonIntersectionPoint = ipoint;
this->foundCollision = true;
//ILOG("Collision registered, t=%f", t);
return true;
} else {
// Already had a closer collision.
return false;
}
}
bool Collision::Triangle(Vec3 p1, Vec3 p2, Vec3 p3) {
Vec3 pNormal = (p2 - p1) % (p3 - p1);
pNormal.normalize();
return Triangle(p1, p2, p3, pNormal);
}
bool Collision::Triangle(Vec3 p1, Vec3 p2, Vec3 p3, const Vec3 &pNormal) {
#ifdef COLLISION_ENABLE_SCALING
p1.scaleBy(scale);
p2.scaleBy(scale);
p3.scaleBy(scale);
#endif
// Backface
// Early out optimization, lose a square root
if (dot(velocity, pNormal) >= 0.0f)
return false;
// ILOG("normal. %s", V(pNormal).c_str());
// find the plane intersection point
float t;
Vec3 polyIPoint, sIPoint = sourcePoint - pNormal;
if (pointPlaneDistance(sIPoint, p1, pNormal) > 0.0f) {
// Plane is embedded in ellipsoid.
// Find plane intersection point by shooting a ray from the sphere intersection
// point along the plane normal.
t = intersectRayPlanePerp(sIPoint, pNormal, p1);
polyIPoint = sIPoint + pNormal * t; // calculate plane intersection point
} else {
// shoot ray along the velocity vector
t = intersectRayPlane(sIPoint, normalizedVelocity, p1, pNormal);
if (t > velocityLength) {
// Did not hit plane. No point in doing triangle tests.
return false;
}
// calculate plane intersection point
polyIPoint = sIPoint + normalizedVelocity * t;
}
if (isPointInTriangle(polyIPoint, p1, p2, p3)) {
// ILOG("Hit triangle! %s %s %s %s", V(polyIPoint).c_str(), V(p1).c_str(), V(p2).c_str(), V(p3).c_str());
float depth2 = (polyIPoint - sourcePoint).length2();
if (depth2 < 0.9999f) {
// Here we do the error checking to see if we got ourself stuck last frame
ELOG("Stuck in triangle! depth2 = %f", depth2);
// ILOG("%s %s %s", V(p1).c_str(), V(p2).c_str(), V(p3).c_str());
stuck = true;
}
return registerCollision(t, polyIPoint);
} else {
// ILOG("Missed this triangle - returning");
return false;
}
}
bool Collision::EdgeLine(Vec3 p1, Vec3 p2) {
// This ellipsoid scaling stuff will result in the edge not being really cylindric.
// Should work for many purposes though.
#ifdef COLLISION_ENABLE_SCALING
p1.scaleBy(scale);
p2.scaleBy(scale);
#endif
float t = 1000000000.0;
Vec3 polyIPoint;
if (intersectCylinder(sourcePoint, normalizedVelocity,
p1, p2, &t, &polyIPoint)) {
polyIPoint = closestPointOnLine(p1, p2, polyIPoint);
return registerCollision(t, polyIPoint);
} else {
return false;
}
}
bool Collision::Corner(Vec3 p1) {
#ifdef COLLISION_ENABLE_SCALING
p1.scaleBy(scale);
#endif
float t = intersectRayUnitSphere(sourcePoint, normalizedVelocity, p1);
if (t != -1.0f) {
return registerCollision(t, p1);
} else {
return false;
}
}
bool Collision::Quad(const Vec3 &o, const Vec3 &dx, const Vec3 &dy) {
bool hit = Triangle(o, o + dx, o + dx + dy);
hit |= Triangle(o + dx + dy, o + dy, o);
return hit;
}
bool Collide(Collision *collision, Collidable *scene, Vec3 *out) {
bool hit_anything = false;
collision->num_contact_points = 0;
collision->lastSafePosition = collision->sourcePoint;
// Process max 8 slides to have a limit if we get into an impossible situation.
int i = 0;
for (i = 0; i < 4; i++) {
// ILOG("Try %i! %s", i, V(collision->sourcePoint).c_str());
scene->Collide(collision);
if (!collision->foundCollision) {
// Nothing left to do.
collision->sourcePoint += collision->velocity;
collision->velocity.setZero();
break;
}
hit_anything = true;
if (collision->nearestDistance < 0.0f) {
ILOG("NegHit! %f %f", collision->velocityLength, collision->nearestDistance);
} else {
// ILOG("Hit! %f %f ", collision->velocityLength, collision->nearestDistance);
}
Vec3 slidePlaneOrigin;
Vec3 slidePlaneNormal;
Vec3 newSourcePoint = collision->sourcePoint;
float adjusted_hit_distance = collision->nearestDistance - 0.0001f;
if (adjusted_hit_distance < 0) adjusted_hit_distance = 0;
// Two cases - one where we have multiple contact points, one where we don't.
if (collision->num_contact_points > 1) {
ILOG("Resolve %i-ary collision, velocityLength = %f",
collision->num_contact_points, collision->velocity.length());
Vec3 avg_contact(0,0,0);
for (int i = 0; i < collision->num_contact_points; i++) {
ILOG("Contact point %s", V(collision->contact_points[i]).c_str());
avg_contact += collision->contact_points[i];
}
avg_contact /= (float)collision->num_contact_points;
// The average of the contact points should be inside the sphere.
float dist = sqrt(avg_contact.distance2To(collision->sourcePoint));
if (dist > 1.0) {
ILOG("Bogus contact points, for sure: %s vs %s, dist=%f", V(avg_contact).c_str(), V(collision->sourcePoint).c_str(), dist);
}
Vec3 avg_contact_dir = avg_contact - collision->sourcePoint;
avg_contact_dir.normalize();
avg_contact = collision->sourcePoint + avg_contact_dir;
newSourcePoint += collision->normalizedVelocity * adjusted_hit_distance;
slidePlaneOrigin = avg_contact;
slidePlaneNormal = (newSourcePoint - slidePlaneOrigin).normalized();
// Push the ball out a bit along the normal to avoid re-collision.
newSourcePoint += slidePlaneNormal * 0.01f;
//collision->velocity += slidePlaneNormal * 0.01;
//slidePlaneNormal = avg_contact_dir;
} else {
// LOG(INFO) << "Adjusting newsourcepoint to end of collision";
newSourcePoint += collision->normalizedVelocity * adjusted_hit_distance;
// LOG(INFO) << "s: " << V(collision->sourcePoint);
// LOG(INFO) << "nsp: " << V(newSourcePoint);
// LOG(INFO) << "ip: " << V(collision->nearestPolygonIntersectionPoint);
// Now we must calculate the sliding plane
slidePlaneOrigin = collision->nearestPolygonIntersectionPoint;
slidePlaneNormal = (newSourcePoint - slidePlaneOrigin).normalized();
}
// We now project the destination point onto the sliding plane
Vec3 destinationPoint = collision->sourcePoint + collision->velocity;
float l = intersectRayPlane(destinationPoint, slidePlaneNormal,
slidePlaneOrigin, -slidePlaneNormal);
// We can now calculate a new destination point on the sliding plane
Vec3 newDestinationPoint = destinationPoint + slidePlaneNormal * l;
// now we start over with the new position and velocity
collision->sourcePoint = newSourcePoint;
// Generate the slide vector, which will become our new velocity vector
Vec3 slide = newDestinationPoint - slidePlaneOrigin;
slide += slidePlaneNormal * 0.0001f;
// LOG(INFO) << "NDP: " << V(newDestinationPoint);
// ILOG("Slide: %s", V(slide).c_str());
// Recompute to get ready!
collision->velocity = slide;
collision->velocityLength = collision->velocity.length();
if (collision->velocityLength <= 0.0002) {
//ILOG("Zero-length velocity vector. Break.");
collision->velocity.setZero();
break;
} else {
// ILOG("Velocity: %s", V(collision->velocity).c_str());
}
collision->nearestDistance = collision->velocityLength;
collision->normalizedVelocity = collision->velocity / collision->velocityLength;
}
//ILOG("%i tries", i);
*out = collision->sourcePoint.scaledBy(collision->size);
if (collision->stuck) {
//ILOG("Stuck - resetting");
// *out = collision->lastSafePosition.scaledBy(collision->size);
}
return hit_anything;
}

View File

@ -1,89 +0,0 @@
// Collision detection engine. You pass in a class representing a scene,
// and out comes a nice FPS-like "sliding physics" response.
// By hrydgard@gmail.com,
#ifndef _COLLISION_H
#define _COLLISION_H
#include "math/lin/vec3.h"
#include "math/lin/matrix4x4.h"
class Collision;
class Collidable {
public:
Collidable();
virtual ~Collidable();
// Apply the collision functions to collision as appropriate.
virtual void Collide(Collision *collision) = 0;
};
class Collision {
public:
void Init(const Vec3 &source, const Vec3 &dest, const Vec3 &size);
// Collision functions
// These should be called from Collidable::Collide and nowhere else
// except the unit test.
// To correctly collide with meshes, just apply these in sequence. You MUST
// collide against all external (convex) edges, otherwise you can get
// quite stuck. There's PLENTY of optimization to do here.
bool Triangle(Vec3 p1, Vec3 p2, Vec3 p3);
bool Triangle(Vec3 p1, Vec3 p2, Vec3 p3, const Vec3 &normal);
bool EdgeLine(Vec3 p1, Vec3 p2); // Cylinder
bool Quad(const Vec3 &origin, const Vec3 &dX, const Vec3 &dY);
bool UnitCube(const Vec3 &origin);
bool Corner(Vec3 p1); // Sphere
bool Corners(Vec3 *p, int count);
// There's an opportunity to provide hyper optimized versions of
// Edge for axis aligned edges. The cylinder intersection becomes trivial.
// These cannot be nested!
void BeginTransform(const Matrix4x4 &inverse, const Matrix4x4 &transform);
void EndTransform();
private:
bool registerCollision(float t, const Vec3 &polyIPoint);
public:
Vec3 sourcePoint;
float velocityLength;
Vec3 size;
Vec3 scale;
Vec3 velocity; // data about player movement
Vec3 normalizedVelocity;
Vec3 lastSafePosition; // for error handling
bool stuck;
// data for collision response
bool foundCollision;
float nearestDistance; // nearest distance to hit
Vec3 nearestPolygonIntersectionPoint; // on polygon
const Matrix4x4 *transform;
// Saved state during transformed operation
Vec3 untransformed_sourcePoint;
Vec3 untransformed_velocity;
enum {
MAX_CONTACT_POINTS = 10
};
Vec3 contact_points[MAX_CONTACT_POINTS];
int num_contact_points;
};
// Sliding physics.
bool Collide(Collision *collision, Collidable *scene, Vec3 *out);
// Bouncy physics
// void CollideBouncy( ... )
#endif // _COLLISION_H