mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-12-03 11:11:25 +00:00
Doesn't belong here either
This commit is contained in:
parent
04b9e6d16c
commit
8c3a48df74
@ -1,9 +0,0 @@
|
||||
set(SRCS
|
||||
collision.cpp
|
||||
)
|
||||
|
||||
add_library(collision STATIC ${SRCS})
|
||||
|
||||
if(UNIX)
|
||||
add_definitions(-fPIC)
|
||||
endif(UNIX)
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
Loading…
Reference in New Issue
Block a user