mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-07-10 21:38:42 +02:00
capsuleCapsule() collision with preliminary tests
This commit is contained in:
parent
8a3640f016
commit
9a70a50bdb
2 changed files with 243 additions and 3 deletions
|
@ -42,9 +42,10 @@ bool sphereSphere(const SphereShape* sphereA, const SphereShape* sphereB, Collis
|
||||||
if (distanceSquared < totalRadius * totalRadius) {
|
if (distanceSquared < totalRadius * totalRadius) {
|
||||||
// normalize BA
|
// normalize BA
|
||||||
float distance = sqrtf(distanceSquared);
|
float distance = sqrtf(distanceSquared);
|
||||||
if (distanceSquared < EPSILON) {
|
if (distance < EPSILON) {
|
||||||
// the spheres are on top of each other, so we pick an arbitrary penetration direction
|
// the spheres are on top of each other, so we pick an arbitrary penetration direction
|
||||||
BA = glm::vec3(0.f, 1.f, 0.f);
|
BA = glm::vec3(0.f, 1.f, 0.f);
|
||||||
|
distance = totalRadius;
|
||||||
} else {
|
} else {
|
||||||
BA /= distance;
|
BA /= distance;
|
||||||
}
|
}
|
||||||
|
@ -166,6 +167,128 @@ bool capsuleSphere(const CapsuleShape* capsuleA, const SphereShape* sphereB, Col
|
||||||
}
|
}
|
||||||
|
|
||||||
bool capsuleCapsule(const CapsuleShape* capsuleA, const CapsuleShape* capsuleB, CollisionInfo& collision) {
|
bool capsuleCapsule(const CapsuleShape* capsuleA, const CapsuleShape* capsuleB, CollisionInfo& collision) {
|
||||||
|
glm::vec3 axisA;
|
||||||
|
capsuleA->computeNormalizedAxis(axisA);
|
||||||
|
glm::vec3 axisB;
|
||||||
|
capsuleB->computeNormalizedAxis(axisB);
|
||||||
|
glm::vec3 centerA = capsuleA->getPosition();
|
||||||
|
glm::vec3 centerB = capsuleB->getPosition();
|
||||||
|
|
||||||
|
// NOTE: The formula for closest approach between two lines is:
|
||||||
|
// d = [(B - A) . (a - (a.b)b)] / (1 - (a.b)^2)
|
||||||
|
|
||||||
|
float aDotB = glm::dot(axisA, axisB);
|
||||||
|
float denominator = 1.f - aDotB * aDotB;
|
||||||
|
float totalRadius = capsuleA->getRadius() + capsuleB->getRadius();
|
||||||
|
if (denominator > EPSILON) {
|
||||||
|
// distances to points of closest approach
|
||||||
|
float distanceA = glm::dot((centerB - centerA), (axisA - (aDotB) * axisB)) / denominator;
|
||||||
|
float distanceB = glm::dot((centerA - centerB), (axisB - (aDotB) * axisA)) / denominator;
|
||||||
|
|
||||||
|
// clamp the distances to the ends of the capsule line segments
|
||||||
|
float absDistanceA = fabs(distanceA);
|
||||||
|
if (absDistanceA > capsuleA->getHalfHeight() + capsuleA->getRadius()) {
|
||||||
|
float signA = distanceA < 0.f ? -1.f : 1.f;
|
||||||
|
distanceA = signA * capsuleA->getHalfHeight();
|
||||||
|
}
|
||||||
|
float absDistanceB = fabs(distanceB);
|
||||||
|
if (absDistanceB > capsuleB->getHalfHeight() + capsuleB->getRadius()) {
|
||||||
|
float signB = distanceB < 0.f ? -1.f : 1.f;
|
||||||
|
distanceB = signB * capsuleB->getHalfHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
// collide like spheres at closest approaches (do most of the math relative to B)
|
||||||
|
glm::vec3 BA = (centerB + distanceB * axisB) - (centerA + distanceA * axisA);
|
||||||
|
float distanceSquared = glm::dot(BA, BA);
|
||||||
|
if (distanceSquared < totalRadius * totalRadius) {
|
||||||
|
// normalize BA
|
||||||
|
float distance = sqrtf(distanceSquared);
|
||||||
|
if (distance < EPSILON) {
|
||||||
|
// the contact spheres are on top of each other, so we need to pick a penetration direction...
|
||||||
|
// try vector between the capsule centers...
|
||||||
|
BA = centerB - centerA;
|
||||||
|
distanceSquared = glm::length2(BA);
|
||||||
|
if (distanceSquared > EPSILON * EPSILON) {
|
||||||
|
distance = sqrtf(distanceSquared);
|
||||||
|
BA /= distance;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
// the capsule centers are on top of each other!
|
||||||
|
// give up on a valid penetration direction and just use the yAxis
|
||||||
|
BA = glm::vec3(0.f, 1.f, 0.f);
|
||||||
|
distance = glm::max(capsuleB->getRadius(), capsuleA->getRadius());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
BA /= distance;
|
||||||
|
}
|
||||||
|
// penetration points from A into B
|
||||||
|
collision._penetration = BA * (totalRadius - distance);
|
||||||
|
// contactPoint is on surface of A
|
||||||
|
collision._contactPoint = centerA + distanceA * axisA + capsuleA->getRadius() * BA;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// capsules are approximiately parallel but might still collide
|
||||||
|
glm::vec3 BA = centerB - centerA;
|
||||||
|
float axialDistance = glm::dot(BA, axisB);
|
||||||
|
float maxAxialDistance = totalRadius + capsuleA->getHalfHeight() + capsuleB->getHalfHeight();
|
||||||
|
if (axialDistance > totalRadius + capsuleA->getHalfHeight() + capsuleB->getHalfHeight()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
BA = BA - axialDistance * axisB; // BA now points from centerA to axisB (perp to axis)
|
||||||
|
float distanceSquared = glm::length2(BA);
|
||||||
|
if (distanceSquared < totalRadius * totalRadius) {
|
||||||
|
// We have all the info we need to compute the penetration vector...
|
||||||
|
// normalize BA
|
||||||
|
float distance = sqrtf(distanceSquared);
|
||||||
|
if (distance < EPSILON) {
|
||||||
|
// the spheres are on top of each other, so we pick an arbitrary penetration direction
|
||||||
|
BA = glm::vec3(0.f, 1.f, 0.f);
|
||||||
|
} else {
|
||||||
|
BA /= distance;
|
||||||
|
}
|
||||||
|
// penetration points from A into B
|
||||||
|
collision._penetration = BA * (totalRadius - distance);
|
||||||
|
|
||||||
|
// However we need some more world-frame info to compute the contactPoint,
|
||||||
|
// which is on the surface of capsuleA...
|
||||||
|
//
|
||||||
|
// Find the overlapping secion of the capsules --> they collide as if there were
|
||||||
|
// two spheres at the midpoint of this overlapping section.
|
||||||
|
// So we project all endpoints to axisB, find the interior pair,
|
||||||
|
// and put A's proxy sphere on axisA at the midpoint of this section.
|
||||||
|
|
||||||
|
// sort the projections as much as possible during calculation
|
||||||
|
float points[5];
|
||||||
|
points[0] = -capsuleB->getHalfHeight();
|
||||||
|
points[1] = axialDistance - capsuleA->getHalfHeight();
|
||||||
|
points[2] = axialDistance + capsuleA->getHalfHeight();
|
||||||
|
points[3] = capsuleB->getHalfHeight();
|
||||||
|
|
||||||
|
// Since there are only three comparisons to do we unroll the sort algorithm...
|
||||||
|
// and use a fifth slot as temp during swap.
|
||||||
|
if (points[4] > points[2]) {
|
||||||
|
points[5] = points[1];
|
||||||
|
points[1] = points[2];
|
||||||
|
points[2] = points[4]
|
||||||
|
}
|
||||||
|
if (points[2] > points[3]) {
|
||||||
|
points[4] = points[2];
|
||||||
|
points[2] = points[3];
|
||||||
|
points[3] = points[4];
|
||||||
|
}
|
||||||
|
if (points[0] > points[1]) {
|
||||||
|
points[4] = points[0];
|
||||||
|
points[0] = points[1];
|
||||||
|
points[1] = points[4];
|
||||||
|
}
|
||||||
|
|
||||||
|
// average the internal pair, and then do the math from centerB
|
||||||
|
collision._contactPoint = centerB + (0.5f * (points[1] + points[2])) * axisB
|
||||||
|
+ (capsuleA->getRadius() - distance) * BA
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -386,13 +386,130 @@ void ShapeColliderTests::sphereTouchesCapsule() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShapeColliderTests::capsuleMissesCapsule() {
|
void ShapeColliderTests::capsuleMissesCapsule() {
|
||||||
// TODO: implement this
|
// non-overlapping capsules
|
||||||
|
float radiusA = 2.f;
|
||||||
|
float halfHeightA = 3.f;
|
||||||
|
float radiusB = 3.f;
|
||||||
|
float halfHeightB = 4.f;
|
||||||
|
|
||||||
|
float totalRadius = radiusA + radiusB;
|
||||||
|
float totalHalfLength = totalRadius + halfHeightA + halfHeightB;
|
||||||
|
|
||||||
|
CapsuleShape capsuleA(radiusA, halfHeightA);
|
||||||
|
CapsuleShape capsuleB(radiusA, halfHeightA);
|
||||||
|
|
||||||
|
// side by side
|
||||||
|
capsuleB.setPosition((1.01f * totalRadius) * xAxis);
|
||||||
|
CollisionInfo collision;
|
||||||
|
if (ShapeCollider::shapeShape(&capsuleA, &capsuleB, collision))
|
||||||
|
{
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__
|
||||||
|
<< " ERROR: capsule and capsule should NOT touch"
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
if (ShapeCollider::shapeShape(&capsuleB, &capsuleA, collision))
|
||||||
|
{
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__
|
||||||
|
<< " ERROR: capsule and capsule should NOT touch"
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// end to end
|
||||||
|
capsuleB.setPosition((1.01f * totalHalfLength) * xAxis);
|
||||||
|
if (ShapeCollider::shapeShape(&capsuleA, &capsuleB, collision))
|
||||||
|
{
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__
|
||||||
|
<< " ERROR: capsule and capsule should NOT touch"
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
if (ShapeCollider::shapeShape(&capsuleB, &capsuleA, collision))
|
||||||
|
{
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__
|
||||||
|
<< " ERROR: capsule and capsule should NOT touch"
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// rotate B and move it to the side
|
||||||
|
glm::quat rotation = glm::angleAxis(rightAngle, zAxis);
|
||||||
|
capsuleB.setRotation(rotation);
|
||||||
|
capsuleB.setPosition((1.01f * (totalRadius + capsuleB.getHalfHeight())) * xAxis);
|
||||||
|
if (ShapeCollider::shapeShape(&capsuleA, &capsuleB, collision))
|
||||||
|
{
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__
|
||||||
|
<< " ERROR: capsule and capsule should NOT touch"
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
if (ShapeCollider::shapeShape(&capsuleB, &capsuleA, collision))
|
||||||
|
{
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__
|
||||||
|
<< " ERROR: capsule and capsule should NOT touch"
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShapeColliderTests::capsuleTouchesCapsule() {
|
void ShapeColliderTests::capsuleTouchesCapsule() {
|
||||||
// TODO: implement this
|
// overlapping capsules
|
||||||
|
float radiusA = 2.f;
|
||||||
|
float halfHeightA = 3.f;
|
||||||
|
float radiusB = 3.f;
|
||||||
|
float halfHeightB = 4.f;
|
||||||
|
|
||||||
|
float totalRadius = radiusA + radiusB;
|
||||||
|
float totalHalfLength = totalRadius + halfHeightA + halfHeightB;
|
||||||
|
|
||||||
|
CapsuleShape capsuleA(radiusA, halfHeightA);
|
||||||
|
CapsuleShape capsuleB(radiusB, halfHeightB);
|
||||||
|
|
||||||
|
// side by side
|
||||||
|
capsuleB.setPosition((0.95f * totalRadius) * xAxis);
|
||||||
|
CollisionInfo collision;
|
||||||
|
if (!ShapeCollider::shapeShape(&capsuleA, &capsuleB, collision))
|
||||||
|
{
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__
|
||||||
|
<< " ERROR: capsule and capsule should touch"
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
if (!ShapeCollider::shapeShape(&capsuleB, &capsuleA, collision))
|
||||||
|
{
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__
|
||||||
|
<< " ERROR: capsule and capsule should touch"
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// end to end
|
||||||
|
capsuleB.setPosition((0.99f * totalHalfLength) * yAxis);
|
||||||
|
if (!ShapeCollider::shapeShape(&capsuleA, &capsuleB, collision))
|
||||||
|
{
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__
|
||||||
|
<< " ERROR: capsule and capsule should touch"
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
if (!ShapeCollider::shapeShape(&capsuleB, &capsuleA, collision))
|
||||||
|
{
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__
|
||||||
|
<< " ERROR: capsule and capsule should touch"
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// rotate B and move it to the side
|
||||||
|
glm::quat rotation = glm::angleAxis(rightAngle, zAxis);
|
||||||
|
capsuleB.setRotation(rotation);
|
||||||
|
capsuleB.setPosition((0.99f * (totalRadius + capsuleB.getHalfHeight())) * xAxis);
|
||||||
|
if (!ShapeCollider::shapeShape(&capsuleA, &capsuleB, collision))
|
||||||
|
{
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__
|
||||||
|
<< " ERROR: capsule and capsule should touch"
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
if (!ShapeCollider::shapeShape(&capsuleB, &capsuleA, collision))
|
||||||
|
{
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__
|
||||||
|
<< " ERROR: capsule and capsule should touch"
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ShapeColliderTests::runAllTests() {
|
void ShapeColliderTests::runAllTests() {
|
||||||
sphereMissesSphere();
|
sphereMissesSphere();
|
||||||
sphereTouchesSphere();
|
sphereTouchesSphere();
|
||||||
|
|
Loading…
Reference in a new issue