parabola aabox fixes, refactoring some intersection math

This commit is contained in:
SamGondelman 2018-07-19 14:54:49 -07:00
parent 39fa3420ec
commit 564578aac6
5 changed files with 497 additions and 478 deletions

View file

@ -114,9 +114,7 @@ bool AABox::contains(const Triangle& triangle) const {
}
bool AABox::contains(const glm::vec3& point) const {
return isWithin(point.x, _corner.x, _scale.x) &&
isWithin(point.y, _corner.y, _scale.y) &&
isWithin(point.z, _corner.z, _scale.z);
return aaBoxContains(point, _corner, _scale);
}
bool AABox::contains(const AABox& otherBox) const {
@ -170,30 +168,6 @@ bool AABox::expandedContains(const glm::vec3& point, float expansion) const {
isWithinExpanded(point.z, _corner.z, _scale.z, expansion);
}
// finds the intersection between a ray and the facing plane on one axis
static bool findIntersection(float origin, float direction, float corner, float size, float& distance) {
if (direction > EPSILON) {
distance = (corner - origin) / direction;
return true;
} else if (direction < -EPSILON) {
distance = (corner + size - origin) / direction;
return true;
}
return false;
}
// finds the intersection between a ray and the inside facing plane on one axis
static bool findInsideOutIntersection(float origin, float direction, float corner, float size, float& distance) {
if (direction > EPSILON) {
distance = -1.0f * (origin - (corner + size)) / direction;
return true;
} else if (direction < -EPSILON) {
distance = -1.0f * (origin - corner) / direction;
return true;
}
return false;
}
bool AABox::expandedIntersectsSegment(const glm::vec3& start, const glm::vec3& end, float expansion) const {
// handle the trivial cases where the expanded box contains the start or end
if (expandedContains(start, expansion) || expandedContains(end, expansion)) {
@ -220,219 +194,12 @@ bool AABox::expandedIntersectsSegment(const glm::vec3& start, const glm::vec3& e
bool AABox::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
BoxFace& face, glm::vec3& surfaceNormal) const {
// handle the trivial case where the box contains the origin
if (contains(origin)) {
// We still want to calculate the distance from the origin to the inside out plane
float axisDistance;
if ((findInsideOutIntersection(origin.x, direction.x, _corner.x, _scale.x, axisDistance) && axisDistance >= 0 &&
isWithin(origin.y + axisDistance*direction.y, _corner.y, _scale.y) &&
isWithin(origin.z + axisDistance*direction.z, _corner.z, _scale.z))) {
distance = axisDistance;
face = direction.x > 0 ? MAX_X_FACE : MIN_X_FACE;
surfaceNormal = glm::vec3(direction.x > 0 ? 1.0f : -1.0f, 0.0f, 0.0f);
return true;
}
if ((findInsideOutIntersection(origin.y, direction.y, _corner.y, _scale.y, axisDistance) && axisDistance >= 0 &&
isWithin(origin.x + axisDistance*direction.x, _corner.x, _scale.x) &&
isWithin(origin.z + axisDistance*direction.z, _corner.z, _scale.z))) {
distance = axisDistance;
face = direction.y > 0 ? MAX_Y_FACE : MIN_Y_FACE;
surfaceNormal = glm::vec3(0.0f, direction.y > 0 ? 1.0f : -1.0f, 0.0f);
return true;
}
if ((findInsideOutIntersection(origin.z, direction.z, _corner.z, _scale.z, axisDistance) && axisDistance >= 0 &&
isWithin(origin.y + axisDistance*direction.y, _corner.y, _scale.y) &&
isWithin(origin.x + axisDistance*direction.x, _corner.x, _scale.x))) {
distance = axisDistance;
face = direction.z > 0 ? MAX_Z_FACE : MIN_Z_FACE;
surfaceNormal = glm::vec3(0.0f, 0.0f, direction.z > 0 ? 1.0f : -1.0f);
return true;
}
// This case is unexpected, but mimics the previous behavior for inside out intersections
distance = 0;
return true;
}
// check each axis
float axisDistance;
if ((findIntersection(origin.x, direction.x, _corner.x, _scale.x, axisDistance) && axisDistance >= 0 &&
isWithin(origin.y + axisDistance*direction.y, _corner.y, _scale.y) &&
isWithin(origin.z + axisDistance*direction.z, _corner.z, _scale.z))) {
distance = axisDistance;
face = direction.x > 0 ? MIN_X_FACE : MAX_X_FACE;
surfaceNormal = glm::vec3(direction.x > 0 ? -1.0f : 1.0f, 0.0f, 0.0f);
return true;
}
if ((findIntersection(origin.y, direction.y, _corner.y, _scale.y, axisDistance) && axisDistance >= 0 &&
isWithin(origin.x + axisDistance*direction.x, _corner.x, _scale.x) &&
isWithin(origin.z + axisDistance*direction.z, _corner.z, _scale.z))) {
distance = axisDistance;
face = direction.y > 0 ? MIN_Y_FACE : MAX_Y_FACE;
surfaceNormal = glm::vec3(0.0f, direction.y > 0 ? -1.0f : 1.0f, 0.0f);
return true;
}
if ((findIntersection(origin.z, direction.z, _corner.z, _scale.z, axisDistance) && axisDistance >= 0 &&
isWithin(origin.y + axisDistance*direction.y, _corner.y, _scale.y) &&
isWithin(origin.x + axisDistance*direction.x, _corner.x, _scale.x))) {
distance = axisDistance;
face = direction.z > 0 ? MIN_Z_FACE : MAX_Z_FACE;
surfaceNormal = glm::vec3(0.0f, 0.0f, direction.z > 0 ? -1.0f : 1.0f);
return true;
}
return false;
}
void AABox::checkPossibleParabolicIntersection(float t, int i, float& minDistance,
const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, bool& hit) const {
if (t < minDistance && t > 0.0f &&
isWithin(origin[(i + 1) % 3] + velocity[(i + 1) % 3] * t + 0.5f * acceleration[(i + 1) % 3] * t * t, _corner[(i + 1) % 3], _scale[(i + 1) % 3]) &&
isWithin(origin[(i + 2) % 3] + velocity[(i + 2) % 3] * t + 0.5f * acceleration[(i + 2) % 3] * t * t, _corner[(i + 2) % 3], _scale[(i + 2) % 3])) {
minDistance = t;
hit = true;
}
return findRayAABoxIntersection(origin, direction, _corner, _scale, distance, face, surfaceNormal);
}
bool AABox::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,
float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal) const {
float minDistance = FLT_MAX;
BoxFace minFace;
glm::vec3 minNormal;
glm::vec2 possibleDistances;
float a, b, c;
// Solve the intersection for each face of the cube. As we go, keep track of the smallest, positive, real distance
// that is within the bounds of the other two dimensions
for (int i = 0; i < 3; i++) {
if (fabsf(acceleration[i]) < EPSILON) {
// Handle the degenerate case where we only have a line in this axis
if (origin[i] < _corner[i]) {
{ // min
if (velocity[i] > 0.0f) {
float possibleDistance = (_corner[i] - origin[i]) / velocity[i];
bool hit = false;
checkPossibleParabolicIntersection(possibleDistance, i, minDistance, origin, velocity, acceleration, hit);
if (hit) {
minFace = BoxFace(2 * i);
minNormal = glm::vec3(0.0f);
minNormal[i] = -1.0f;
}
}
}
} else if (origin[i] > _corner[i] + _scale[i]) {
{ // max
if (velocity[i] < 0.0f) {
float possibleDistance = (_corner[i] + _scale[i] - origin[i]) / velocity[i];
bool hit = false;
checkPossibleParabolicIntersection(possibleDistance, i, minDistance, origin, velocity, acceleration, hit);
if (hit) {
minFace = BoxFace(2 * i + 1);
minNormal = glm::vec3(0.0f);
minNormal[i] = 1.0f;
}
}
}
} else {
{ // min
if (velocity[i] < 0.0f) {
float possibleDistance = (_corner[i] - origin[i]) / velocity[i];
bool hit = false;
checkPossibleParabolicIntersection(possibleDistance, i, minDistance, origin, velocity, acceleration, hit);
if (hit) {
minFace = BoxFace(2 * i + 1);
minNormal = glm::vec3(0.0f);
minNormal[i] = 1.0f;
}
}
}
{ // max
if (velocity[i] > 0.0f) {
float possibleDistance = (_corner[i] + _scale[i] - origin[i]) / velocity[i];
bool hit = false;
checkPossibleParabolicIntersection(possibleDistance, i, minDistance, origin, velocity, acceleration, hit);
if (hit) {
minFace = BoxFace(2 * i);
minNormal = glm::vec3(0.0f);
minNormal[i] = -1.0f;
}
}
}
}
} else {
a = 0.5f * acceleration[i];
b = velocity[i];
if (origin[i] < _corner[i]) {
// If we're below _corner, we only need to check the min face
{ // min
c = origin[i] - _corner[i];
possibleDistances = { FLT_MAX, FLT_MAX };
if (computeRealQuadraticRoots(a, b, c, possibleDistances)) {
bool hit = false;
checkPossibleParabolicIntersection(possibleDistances.x, i, minDistance, origin, velocity, acceleration, hit);
checkPossibleParabolicIntersection(possibleDistances.y, i, minDistance, origin, velocity, acceleration, hit);
if (hit) {
minFace = BoxFace(2 * i);
minNormal = glm::vec3(0.0f);
minNormal[i] = -1.0f;
}
}
}
} else if (origin[i] > _corner[i] + _scale[i]) {
// If we're above _corner + _scale, we only need to check the max face
{ // max
c = origin[i] - (_corner[i] + _scale[i]);
possibleDistances = { FLT_MAX, FLT_MAX };
if (computeRealQuadraticRoots(a, b, c, possibleDistances)) {
bool hit = false;
checkPossibleParabolicIntersection(possibleDistances.x, i, minDistance, origin, velocity, acceleration, hit);
checkPossibleParabolicIntersection(possibleDistances.y, i, minDistance, origin, velocity, acceleration, hit);
if (hit) {
minFace = BoxFace(2 * i + 1);
minNormal = glm::vec3(0.0f);
minNormal[i] = 1.0f;
}
}
}
} else {
// If we're inside on this axis, we could hit either face depending on our velocity and acceleration, so we need to check both
{ // min
c = origin[i] - _corner[i];
possibleDistances = { FLT_MAX, FLT_MAX };
if (computeRealQuadraticRoots(a, b, c, possibleDistances)) {
bool hit = false;
checkPossibleParabolicIntersection(possibleDistances.x, i, minDistance, origin, velocity, acceleration, hit);
checkPossibleParabolicIntersection(possibleDistances.y, i, minDistance, origin, velocity, acceleration, hit);
if (hit) {
minFace = BoxFace(2 * i + 1);
minNormal = glm::vec3(0.0f);
minNormal[i] = 1.0f;
}
}
}
{ // max
c = origin[i] - (_corner[i] + _scale[i]);
possibleDistances = { FLT_MAX, FLT_MAX };
if (computeRealQuadraticRoots(a, b, c, possibleDistances)) {
bool hit = false;
checkPossibleParabolicIntersection(possibleDistances.x, i, minDistance, origin, velocity, acceleration, hit);
checkPossibleParabolicIntersection(possibleDistances.y, i, minDistance, origin, velocity, acceleration, hit);
if (hit) {
minFace = BoxFace(2 * i);
minNormal = glm::vec3(0.0f);
minNormal[i] = -1.0f;
}
}
}
}
}
}
if (minDistance < FLT_MAX) {
parabolicDistance = minDistance;
face = minFace;
surfaceNormal = minNormal;
return true;
}
return false;
float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal) const {
return findParabolaAABoxIntersection(origin, velocity, acceleration, _corner, _scale, parabolicDistance, face, surfaceNormal);
}
bool AABox::rayHitsBoundingSphere(const glm::vec3& origin, const glm::vec3& direction) const {

View file

@ -111,9 +111,7 @@ glm::vec3 AACube::getNearestVertex(const glm::vec3& normal) const {
}
bool AACube::contains(const glm::vec3& point) const {
return isWithin(point.x, _corner.x, _scale) &&
isWithin(point.y, _corner.y, _scale) &&
isWithin(point.z, _corner.z, _scale);
return aaBoxContains(point, _corner, glm::vec3(_scale));
}
bool AACube::contains(const AACube& otherCube) const {
@ -165,30 +163,6 @@ bool AACube::expandedContains(const glm::vec3& point, float expansion) const {
isWithinExpanded(point.z, _corner.z, _scale, expansion);
}
// finds the intersection between a ray and the facing plane on one axis
static bool findIntersection(float origin, float direction, float corner, float size, float& distance) {
if (direction > EPSILON) {
distance = (corner - origin) / direction;
return true;
} else if (direction < -EPSILON) {
distance = (corner + size - origin) / direction;
return true;
}
return false;
}
// finds the intersection between a ray and the inside facing plane on one axis
static bool findInsideOutIntersection(float origin, float direction, float corner, float size, float& distance) {
if (direction > EPSILON) {
distance = -1.0f * (origin - (corner + size)) / direction;
return true;
} else if (direction < -EPSILON) {
distance = -1.0f * (origin - corner) / direction;
return true;
}
return false;
}
bool AACube::expandedIntersectsSegment(const glm::vec3& start, const glm::vec3& end, float expansion) const {
// handle the trivial cases where the expanded box contains the start or end
if (expandedContains(start, expansion) || expandedContains(end, expansion)) {
@ -215,220 +189,12 @@ bool AACube::expandedIntersectsSegment(const glm::vec3& start, const glm::vec3&
bool AACube::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
BoxFace& face, glm::vec3& surfaceNormal) const {
// handle the trivial case where the box contains the origin
if (contains(origin)) {
// We still want to calculate the distance from the origin to the inside out plane
float axisDistance;
if ((findInsideOutIntersection(origin.x, direction.x, _corner.x, _scale, axisDistance) && axisDistance >= 0 &&
isWithin(origin.y + axisDistance*direction.y, _corner.y, _scale) &&
isWithin(origin.z + axisDistance*direction.z, _corner.z, _scale))) {
distance = axisDistance;
face = direction.x > 0 ? MAX_X_FACE : MIN_X_FACE;
surfaceNormal = glm::vec3(direction.x > 0 ? 1.0f : -1.0f, 0.0f, 0.0f);
return true;
}
if ((findInsideOutIntersection(origin.y, direction.y, _corner.y, _scale, axisDistance) && axisDistance >= 0 &&
isWithin(origin.x + axisDistance*direction.x, _corner.x, _scale) &&
isWithin(origin.z + axisDistance*direction.z, _corner.z, _scale))) {
distance = axisDistance;
face = direction.y > 0 ? MAX_Y_FACE : MIN_Y_FACE;
surfaceNormal = glm::vec3(0.0f, direction.y > 0 ? 1.0f : -1.0f, 0.0f);
return true;
}
if ((findInsideOutIntersection(origin.z, direction.z, _corner.z, _scale, axisDistance) && axisDistance >= 0 &&
isWithin(origin.y + axisDistance*direction.y, _corner.y, _scale) &&
isWithin(origin.x + axisDistance*direction.x, _corner.x, _scale))) {
distance = axisDistance;
face = direction.z > 0 ? MAX_Z_FACE : MIN_Z_FACE;
surfaceNormal = glm::vec3(0.0f, 0.0f, direction.z > 0 ? 1.0f : -1.0f);
return true;
}
// This case is unexpected, but mimics the previous behavior for inside out intersections
distance = 0;
return true;
}
// check each axis
float axisDistance;
if ((findIntersection(origin.x, direction.x, _corner.x, _scale, axisDistance) && axisDistance >= 0 &&
isWithin(origin.y + axisDistance*direction.y, _corner.y, _scale) &&
isWithin(origin.z + axisDistance*direction.z, _corner.z, _scale))) {
distance = axisDistance;
face = direction.x > 0 ? MIN_X_FACE : MAX_X_FACE;
surfaceNormal = glm::vec3(direction.x > 0 ? -1.0f : 1.0f, 0.0f, 0.0f);
return true;
}
if ((findIntersection(origin.y, direction.y, _corner.y, _scale, axisDistance) && axisDistance >= 0 &&
isWithin(origin.x + axisDistance*direction.x, _corner.x, _scale) &&
isWithin(origin.z + axisDistance*direction.z, _corner.z, _scale))) {
distance = axisDistance;
face = direction.y > 0 ? MIN_Y_FACE : MAX_Y_FACE;
surfaceNormal = glm::vec3(0.0f, direction.y > 0 ? -1.0f : 1.0f, 0.0f);
return true;
}
if ((findIntersection(origin.z, direction.z, _corner.z, _scale, axisDistance) && axisDistance >= 0 &&
isWithin(origin.y + axisDistance*direction.y, _corner.y, _scale) &&
isWithin(origin.x + axisDistance*direction.x, _corner.x, _scale))) {
distance = axisDistance;
face = direction.z > 0 ? MIN_Z_FACE : MAX_Z_FACE;
surfaceNormal = glm::vec3(0.0f, 0.0f, direction.z > 0 ? -1.0f : 1.0f);
return true;
}
return false;
}
void AACube::checkPossibleParabolicIntersection(float t, int i, float& minDistance,
const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, bool& hit) const {
if (t < minDistance && t > 0.0f &&
isWithin(origin[(i + 1) % 3] + velocity[(i + 1) % 3] * t + 0.5f * acceleration[(i + 1) % 3] * t * t, _corner[(i + 1) % 3], _scale) &&
isWithin(origin[(i + 2) % 3] + velocity[(i + 2) % 3] * t + 0.5f * acceleration[(i + 2) % 3] * t * t, _corner[(i + 2) % 3], _scale)) {
minDistance = t;
hit = true;
}
return findRayAABoxIntersection(origin, direction, _corner, glm::vec3(_scale), distance, face, surfaceNormal);
}
bool AACube::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,
float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal) const {
float minDistance = FLT_MAX;
BoxFace minFace;
glm::vec3 minNormal;
glm::vec2 possibleDistances;
float a, b, c;
// Solve the intersection for each face of the cube. As we go, keep track of the smallest, positive, real distance
// that is within the bounds of the other two dimensions
for (int i = 0; i < 3; i++) {
if (fabsf(acceleration[i]) < EPSILON) {
// Handle the degenerate case where we only have a line in this axis
if (origin[i] < _corner[i]) {
{ // min
if (velocity[i] > 0.0f) {
float possibleDistance = (_corner[i] - origin[i]) / velocity[i];
bool hit = false;
checkPossibleParabolicIntersection(possibleDistance, i, minDistance, origin, velocity, acceleration, hit);
if (hit) {
minFace = BoxFace(2 * i);
minNormal = glm::vec3(0.0f);
minNormal[i] = -1.0f;
}
}
}
} else if (origin[i] > _corner[i] + _scale) {
{ // max
if (velocity[i] < 0.0f) {
float possibleDistance = (_corner[i] + _scale - origin[i]) / velocity[i];
bool hit = false;
checkPossibleParabolicIntersection(possibleDistance, i, minDistance, origin, velocity, acceleration, hit);
if (hit) {
minFace = BoxFace(2 * i + 1);
minNormal = glm::vec3(0.0f);
minNormal[i] = 1.0f;
}
}
}
} else {
{ // min
if (velocity[i] < 0.0f) {
float possibleDistance = (_corner[i] - origin[i]) / velocity[i];
bool hit = false;
checkPossibleParabolicIntersection(possibleDistance, i, minDistance, origin, velocity, acceleration, hit);
if (hit) {
minFace = BoxFace(2 * i + 1);
minNormal = glm::vec3(0.0f);
minNormal[i] = 1.0f;
}
}
}
{ // max
if (velocity[i] > 0.0f) {
float possibleDistance = (_corner[i] + _scale - origin[i]) / velocity[i];
bool hit = false;
checkPossibleParabolicIntersection(possibleDistance, i, minDistance, origin, velocity, acceleration, hit);
if (hit) {
minFace = BoxFace(2 * i);
minNormal = glm::vec3(0.0f);
minNormal[i] = -1.0f;
}
}
}
}
} else {
a = 0.5f * acceleration[i];
b = velocity[i];
if (origin[i] < _corner[i]) {
// If we're below _corner, we only need to check the min face
{ // min
c = origin[i] - _corner[i];
possibleDistances = { FLT_MAX, FLT_MAX };
if (computeRealQuadraticRoots(a, b, c, possibleDistances)) {
bool hit = false;
checkPossibleParabolicIntersection(possibleDistances.x, i, minDistance, origin, velocity, acceleration, hit);
checkPossibleParabolicIntersection(possibleDistances.y, i, minDistance, origin, velocity, acceleration, hit);
if (hit) {
minFace = BoxFace(2 * i);
minNormal = glm::vec3(0.0f);
minNormal[i] = -1.0f;
}
}
}
} else if (origin[i] > _corner[i] + _scale) {
// If we're above _corner + _scale, we only need to check the max face
{ // max
c = origin[i] - (_corner[i] + _scale);
possibleDistances = { FLT_MAX, FLT_MAX };
if (computeRealQuadraticRoots(a, b, c, possibleDistances)) {
bool hit = false;
checkPossibleParabolicIntersection(possibleDistances.x, i, minDistance, origin, velocity, acceleration, hit);
checkPossibleParabolicIntersection(possibleDistances.y, i, minDistance, origin, velocity, acceleration, hit);
if (hit) {
minFace = BoxFace(2 * i + 1);
minNormal = glm::vec3(0.0f);
minNormal[i] = 1.0f;
}
}
}
} else {
// If we're inside on this axis, we could hit either face depending on our velocity and acceleration, so we need to check both
{ // min
c = origin[i] - _corner[i];
possibleDistances = { FLT_MAX, FLT_MAX };
if (computeRealQuadraticRoots(a, b, c, possibleDistances)) {
bool hit = false;
checkPossibleParabolicIntersection(possibleDistances.x, i, minDistance, origin, velocity, acceleration, hit);
checkPossibleParabolicIntersection(possibleDistances.y, i, minDistance, origin, velocity, acceleration, hit);
if (hit) {
minFace = BoxFace(2 * i + 1);
minNormal = glm::vec3(0.0f);
minNormal[i] = 1.0f;
}
}
}
{ // max
c = origin[i] - (_corner[i] + _scale);
possibleDistances = { FLT_MAX, FLT_MAX };
if (computeRealQuadraticRoots(a, b, c, possibleDistances)) {
bool hit = false;
checkPossibleParabolicIntersection(possibleDistances.x, i, minDistance, origin, velocity, acceleration, hit);
checkPossibleParabolicIntersection(possibleDistances.y, i, minDistance, origin, velocity, acceleration, hit);
if (hit) {
minFace = BoxFace(2 * i);
minNormal = glm::vec3(0.0f);
minNormal[i] = -1.0f;
}
}
}
}
}
}
if (minDistance < FLT_MAX) {
parabolicDistance = minDistance;
face = minFace;
surfaceNormal = minNormal;
return true;
}
return false;
float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal) const {
return findParabolaAABoxIntersection(origin, velocity, acceleration, _corner, glm::vec3(_scale), parabolicDistance, face, surfaceNormal);
}
bool AACube::touchesSphere(const glm::vec3& center, float radius) const {

View file

@ -78,9 +78,6 @@ private:
static BoxFace getOppositeFace(BoxFace face);
void checkPossibleParabolicIntersection(float t, int i, float& minDistance,
const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, bool& hit) const;
glm::vec3 _corner;
float _scale;
};

View file

@ -190,6 +190,94 @@ glm::vec3 addPenetrations(const glm::vec3& currentPenetration, const glm::vec3&
newPenetration - (currentDirection * directionalComponent);
}
// finds the intersection between a ray and the facing plane on one axis
bool findIntersection(float origin, float direction, float corner, float size, float& distance) {
if (direction > EPSILON) {
distance = (corner - origin) / direction;
return true;
} else if (direction < -EPSILON) {
distance = (corner + size - origin) / direction;
return true;
}
return false;
}
// finds the intersection between a ray and the inside facing plane on one axis
bool findInsideOutIntersection(float origin, float direction, float corner, float size, float& distance) {
if (direction > EPSILON) {
distance = -1.0f * (origin - (corner + size)) / direction;
return true;
} else if (direction < -EPSILON) {
distance = -1.0f * (origin - corner) / direction;
return true;
}
return false;
}
bool findRayAABoxIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& corner, const glm::vec3& scale, float& distance,
BoxFace& face, glm::vec3& surfaceNormal) {
// handle the trivial case where the box contains the origin
if (aaBoxContains(origin, corner, scale)) {
// We still want to calculate the distance from the origin to the inside out plane
float axisDistance;
if ((findInsideOutIntersection(origin.x, direction.x, corner.x, scale.x, axisDistance) && axisDistance >= 0 &&
isWithin(origin.y + axisDistance * direction.y, corner.y, scale.y) &&
isWithin(origin.z + axisDistance * direction.z, corner.z, scale.z))) {
distance = axisDistance;
face = direction.x > 0 ? MAX_X_FACE : MIN_X_FACE;
surfaceNormal = glm::vec3(direction.x > 0 ? 1.0f : -1.0f, 0.0f, 0.0f);
return true;
}
if ((findInsideOutIntersection(origin.y, direction.y, corner.y, scale.y, axisDistance) && axisDistance >= 0 &&
isWithin(origin.x + axisDistance * direction.x, corner.x, scale.x) &&
isWithin(origin.z + axisDistance * direction.z, corner.z, scale.z))) {
distance = axisDistance;
face = direction.y > 0 ? MAX_Y_FACE : MIN_Y_FACE;
surfaceNormal = glm::vec3(0.0f, direction.y > 0 ? 1.0f : -1.0f, 0.0f);
return true;
}
if ((findInsideOutIntersection(origin.z, direction.z, corner.z, scale.z, axisDistance) && axisDistance >= 0 &&
isWithin(origin.y + axisDistance * direction.y, corner.y, scale.y) &&
isWithin(origin.x + axisDistance * direction.x, corner.x, scale.x))) {
distance = axisDistance;
face = direction.z > 0 ? MAX_Z_FACE : MIN_Z_FACE;
surfaceNormal = glm::vec3(0.0f, 0.0f, direction.z > 0 ? 1.0f : -1.0f);
return true;
}
// This case is unexpected, but mimics the previous behavior for inside out intersections
distance = 0;
return true;
}
// check each axis
float axisDistance;
if ((findIntersection(origin.x, direction.x, corner.x, scale.x, axisDistance) && axisDistance >= 0 &&
isWithin(origin.y + axisDistance * direction.y, corner.y, scale.y) &&
isWithin(origin.z + axisDistance * direction.z, corner.z, scale.z))) {
distance = axisDistance;
face = direction.x > 0 ? MIN_X_FACE : MAX_X_FACE;
surfaceNormal = glm::vec3(direction.x > 0 ? -1.0f : 1.0f, 0.0f, 0.0f);
return true;
}
if ((findIntersection(origin.y, direction.y, corner.y, scale.y, axisDistance) && axisDistance >= 0 &&
isWithin(origin.x + axisDistance * direction.x, corner.x, scale.x) &&
isWithin(origin.z + axisDistance * direction.z, corner.z, scale.z))) {
distance = axisDistance;
face = direction.y > 0 ? MIN_Y_FACE : MAX_Y_FACE;
surfaceNormal = glm::vec3(0.0f, direction.y > 0 ? -1.0f : 1.0f, 0.0f);
return true;
}
if ((findIntersection(origin.z, direction.z, corner.z, scale.z, axisDistance) && axisDistance >= 0 &&
isWithin(origin.y + axisDistance * direction.y, corner.y, scale.y) &&
isWithin(origin.x + axisDistance * direction.x, corner.x, scale.x))) {
distance = axisDistance;
face = direction.z > 0 ? MIN_Z_FACE : MAX_Z_FACE;
surfaceNormal = glm::vec3(0.0f, 0.0f, direction.z > 0 ? -1.0f : 1.0f);
return true;
}
return false;
}
bool findRaySphereIntersection(const glm::vec3& origin, const glm::vec3& direction,
const glm::vec3& center, float radius, float& distance) {
glm::vec3 relativeOrigin = origin - center;
@ -719,6 +807,12 @@ bool isWithin(float value, float corner, float size) {
return value >= corner && value <= corner + size;
}
bool aaBoxContains(const glm::vec3& point, const glm::vec3& corner, const glm::vec3& scale) {
return isWithin(point.x, corner.x, scale.x) &&
isWithin(point.y, corner.y, scale.y) &&
isWithin(point.z, corner.z, scale.z);
}
void checkPossibleParabolicIntersectionWithZPlane(float t, float& minDistance,
const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, const glm::vec2& corner, const glm::vec2& scale) {
if (t < minDistance && t > 0.0f &&
@ -979,6 +1073,391 @@ bool findParabolaCapsuleIntersection(const glm::vec3& origin, const glm::vec3& v
return minDistance != FLT_MAX;
}
void checkPossibleParabolicIntersection(float t, int i, float& minDistance, const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,
const glm::vec3& corner, const glm::vec3& scale, bool& hit) {
if (t < minDistance && t > 0.0f &&
isWithin(origin[(i + 1) % 3] + velocity[(i + 1) % 3] * t + 0.5f * acceleration[(i + 1) % 3] * t * t, corner[(i + 1) % 3], scale[(i + 1) % 3]) &&
isWithin(origin[(i + 2) % 3] + velocity[(i + 2) % 3] * t + 0.5f * acceleration[(i + 2) % 3] * t * t, corner[(i + 2) % 3], scale[(i + 2) % 3])) {
minDistance = t;
hit = true;
}
}
inline float parabolaVelocityAtT(float velocity, float acceleration, float t) {
return velocity + acceleration * t;
}
bool findParabolaAABoxIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,
const glm::vec3& corner, const glm::vec3& scale, float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal) {
float minDistance = FLT_MAX;
BoxFace minFace;
glm::vec3 minNormal;
glm::vec2 possibleDistances;
float a, b, c;
// Solve the intersection for each face of the cube. As we go, keep track of the smallest, positive, real distance
// that is within the bounds of the other two dimensions
for (int i = 0; i < 3; i++) {
if (fabsf(acceleration[i]) < EPSILON) {
// Handle the degenerate case where we only have a line in this axis
if (origin[i] < corner[i]) {
{ // min
if (velocity[i] > 0.0f) {
float possibleDistance = (corner[i] - origin[i]) / velocity[i];
bool hit = false;
checkPossibleParabolicIntersection(possibleDistance, i, minDistance, origin, velocity, acceleration, corner, scale, hit);
if (hit) {
minFace = BoxFace(2 * i);
minNormal = glm::vec3(0.0f);
minNormal[i] = -1.0f;
}
}
}
} else if (origin[i] > corner[i] + scale[i]) {
{ // max
if (velocity[i] < 0.0f) {
float possibleDistance = (corner[i] + scale[i] - origin[i]) / velocity[i];
bool hit = false;
checkPossibleParabolicIntersection(possibleDistance, i, minDistance, origin, velocity, acceleration, corner, scale, hit);
if (hit) {
minFace = BoxFace(2 * i + 1);
minNormal = glm::vec3(0.0f);
minNormal[i] = 1.0f;
}
}
}
} else {
{ // min
if (velocity[i] < 0.0f) {
float possibleDistance = (corner[i] - origin[i]) / velocity[i];
bool hit = false;
checkPossibleParabolicIntersection(possibleDistance, i, minDistance, origin, velocity, acceleration, corner, scale, hit);
if (hit) {
minFace = BoxFace(2 * i + 1);
minNormal = glm::vec3(0.0f);
minNormal[i] = 1.0f;
}
}
}
{ // max
if (velocity[i] > 0.0f) {
float possibleDistance = (corner[i] + scale[i] - origin[i]) / velocity[i];
bool hit = false;
checkPossibleParabolicIntersection(possibleDistance, i, minDistance, origin, velocity, acceleration, corner, scale, hit);
if (hit) {
minFace = BoxFace(2 * i);
minNormal = glm::vec3(0.0f);
minNormal[i] = -1.0f;
}
}
}
}
} else {
a = 0.5f * acceleration[i];
b = velocity[i];
if (origin[i] < corner[i]) {
// If we're below corner, we have the following cases:
// - within bounds on other axes
// - if +velocity or +acceleration
// - can only hit MIN_FACE with -normal
// - else
// - if +acceleration
// - can only hit MIN_FACE with -normal
// - else if +velocity
// - can hit MIN_FACE with -normal iff velocity at intersection is +
// - else can hit MAX_FACE with +normal iff velocity at intersection is -
if (origin[(i + 1) % 3] > corner[(i + 1) % 3] && origin[(i + 1) % 3] < corner[(i + 1) % 3] + scale[(i + 1) % 3] &&
origin[(i + 2) % 3] > corner[(i + 2) % 3] && origin[(i + 2) % 3] < corner[(i + 2) % 3] + scale[(i + 2) % 3]) {
if (velocity[i] > 0.0f || acceleration[i] > 0.0f) {
{ // min
c = origin[i] - corner[i];
possibleDistances = { FLT_MAX, FLT_MAX };
if (computeRealQuadraticRoots(a, b, c, possibleDistances)) {
bool hit = false;
for (int j = 0; j < 2; j++) {
checkPossibleParabolicIntersection(possibleDistances[j], i, minDistance, origin, velocity, acceleration, corner, scale, hit);
}
if (hit) {
minFace = BoxFace(2 * i);
minNormal = glm::vec3(0.0f);
minNormal[i] = -1.0f;
}
}
}
}
} else {
if (acceleration[i] > 0.0f) {
{ // min
c = origin[i] - corner[i];
possibleDistances = { FLT_MAX, FLT_MAX };
if (computeRealQuadraticRoots(a, b, c, possibleDistances)) {
bool hit = false;
for (int j = 0; j < 2; j++) {
checkPossibleParabolicIntersection(possibleDistances[j], i, minDistance, origin, velocity, acceleration, corner, scale, hit);
}
if (hit) {
minFace = BoxFace(2 * i);
minNormal = glm::vec3(0.0f);
minNormal[i] = -1.0f;
}
}
}
} else if (velocity[i] > 0.0f) {
bool hit = false;
{ // min
c = origin[i] - corner[i];
possibleDistances = { FLT_MAX, FLT_MAX };
if (computeRealQuadraticRoots(a, b, c, possibleDistances)) {
for (int j = 0; j < 2; j++) {
if (parabolaVelocityAtT(velocity[i], acceleration[i], possibleDistances[j]) > 0.0f) {
checkPossibleParabolicIntersection(possibleDistances[j], i, minDistance, origin, velocity, acceleration, corner, scale, hit);
}
}
if (hit) {
minFace = BoxFace(2 * i);
minNormal = glm::vec3(0.0f);
minNormal[i] = -1.0f;
}
}
}
if (!hit) { // max
c = origin[i] - (corner[i] + scale[i]);
possibleDistances = { FLT_MAX, FLT_MAX };
if (computeRealQuadraticRoots(a, b, c, possibleDistances)) {
for (int j = 0; j < 2; j++) {
if (parabolaVelocityAtT(velocity[i], acceleration[i], possibleDistances[j]) < 0.0f) {
checkPossibleParabolicIntersection(possibleDistances[j], i, minDistance, origin, velocity, acceleration, corner, scale, hit);
}
}
if (hit) {
minFace = BoxFace(2 * i + 1);
minNormal = glm::vec3(0.0f);
minNormal[i] = 1.0f;
}
}
}
}
}
} else if (origin[i] > corner[i] + scale[i]) {
// If we're above corner + scale, we have the following cases:
// - within bounds on other axes
// - if -velocity or -acceleration
// - can only hit MAX_FACE with +normal
// - else
// - if -acceleration
// - can only hit MAX_FACE with +normal
// - else if -velocity
// - can hit MAX_FACE with +normal iff velocity at intersection is -
// - else can hit MIN_FACE with -normal iff velocity at intersection is +
if (origin[(i + 1) % 3] > corner[(i + 1) % 3] && origin[(i + 1) % 3] < corner[(i + 1) % 3] + scale[(i + 1) % 3] &&
origin[(i + 2) % 3] > corner[(i + 2) % 3] && origin[(i + 2) % 3] < corner[(i + 2) % 3] + scale[(i + 2) % 3]) {
if (velocity[i] < 0.0f || acceleration[i] < 0.0f) {
{ // max
c = origin[i] - (corner[i] + scale[i]);
possibleDistances = { FLT_MAX, FLT_MAX };
if (computeRealQuadraticRoots(a, b, c, possibleDistances)) {
bool hit = false;
for (int j = 0; j < 2; j++) {
checkPossibleParabolicIntersection(possibleDistances[j], i, minDistance, origin, velocity, acceleration, corner, scale, hit);
}
if (hit) {
minFace = BoxFace(2 * i + 1);
minNormal = glm::vec3(0.0f);
minNormal[i] = 1.0f;
}
}
}
}
} else {
if (acceleration[i] < 0.0f) {
{ // max
c = origin[i] - (corner[i] + scale[i]);
possibleDistances = { FLT_MAX, FLT_MAX };
if (computeRealQuadraticRoots(a, b, c, possibleDistances)) {
bool hit = false;
for (int j = 0; j < 2; j++) {
checkPossibleParabolicIntersection(possibleDistances[j], i, minDistance, origin, velocity, acceleration, corner, scale, hit);
}
if (hit) {
minFace = BoxFace(2 * i + 1);
minNormal = glm::vec3(0.0f);
minNormal[i] = 1.0f;
}
}
}
} else if (velocity[i] < 0.0f) {
bool hit = false;
{ // max
c = origin[i] - (corner[i] + scale[i]);
possibleDistances = { FLT_MAX, FLT_MAX };
if (computeRealQuadraticRoots(a, b, c, possibleDistances)) {
for (int j = 0; j < 2; j++) {
if (parabolaVelocityAtT(velocity[i], acceleration[i], possibleDistances[j]) < 0.0f) {
checkPossibleParabolicIntersection(possibleDistances[j], i, minDistance, origin, velocity, acceleration, corner, scale, hit);
}
}
if (hit) {
minFace = BoxFace(2 * i + 1);
minNormal = glm::vec3(0.0f);
minNormal[i] = 1.0f;
}
}
}
if (!hit) { // min
c = origin[i] - corner[i];
possibleDistances = { FLT_MAX, FLT_MAX };
if (computeRealQuadraticRoots(a, b, c, possibleDistances)) {
for (int j = 0; j < 2; j++) {
if (parabolaVelocityAtT(velocity[i], acceleration[i], possibleDistances[j]) > 0.0f) {
checkPossibleParabolicIntersection(possibleDistances[j], i, minDistance, origin, velocity, acceleration, corner, scale, hit);
}
}
if (hit) {
minFace = BoxFace(2 * i);
minNormal = glm::vec3(0.0f);
minNormal[i] = -1.0f;
}
}
}
}
}
} else {
// If we're between corner and corner + scale, we have the following cases:
// - within bounds on other axes
// - if -velocity and -acceleration
// - can only hit MIN_FACE with +normal
// - else if +velocity and +acceleration
// - can only hit MAX_FACE with -normal
// - else
// - can hit MIN_FACE with +normal iff velocity at intersection is -
// - can hit MAX_FACE with -normal iff velocity at intersection is +
// - else
// - if -velocity and +acceleration
// - can hit MIN_FACE with -normal iff velocity at intersection is +
// - else if +velocity and -acceleration
// - can hit MAX_FACE with +normal iff velocity at intersection is -
if (origin[(i + 1) % 3] > corner[(i + 1) % 3] && origin[(i + 1) % 3] < corner[(i + 1) % 3] + scale[(i + 1) % 3] &&
origin[(i + 2) % 3] > corner[(i + 2) % 3] && origin[(i + 2) % 3] < corner[(i + 2) % 3] + scale[(i + 2) % 3]) {
if (velocity[i] < 0.0f && acceleration[i] < 0.0f) {
{ // min
c = origin[i] - corner[i];
possibleDistances = { FLT_MAX, FLT_MAX };
if (computeRealQuadraticRoots(a, b, c, possibleDistances)) {
bool hit = false;
for (int j = 0; j < 2; j++) {
checkPossibleParabolicIntersection(possibleDistances[j], i, minDistance, origin, velocity, acceleration, corner, scale, hit);
}
if (hit) {
minFace = BoxFace(2 * i);
minNormal = glm::vec3(0.0f);
minNormal[i] = 1.0f;
}
}
}
} else if (velocity[i] > 0.0f && acceleration[i] > 0.0f) {
{ // max
c = origin[i] - (corner[i] + scale[i]);
possibleDistances = { FLT_MAX, FLT_MAX };
if (computeRealQuadraticRoots(a, b, c, possibleDistances)) {
bool hit = false;
for (int j = 0; j < 2; j++) {
checkPossibleParabolicIntersection(possibleDistances[j], i, minDistance, origin, velocity, acceleration, corner, scale, hit);
}
if (hit) {
minFace = BoxFace(2 * i + 1);
minNormal = glm::vec3(0.0f);
minNormal[i] = -1.0f;
}
}
}
} else {
{ // min
c = origin[i] - corner[i];
possibleDistances = { FLT_MAX, FLT_MAX };
bool hit = false;
if (computeRealQuadraticRoots(a, b, c, possibleDistances)) {
for (int j = 0; j < 2; j++) {
if (parabolaVelocityAtT(velocity[i], acceleration[i], possibleDistances[j]) < 0.0f) {
checkPossibleParabolicIntersection(possibleDistances[j], i, minDistance, origin, velocity, acceleration, corner, scale, hit);
}
}
if (hit) {
minFace = BoxFace(2 * i);
minNormal = glm::vec3(0.0f);
minNormal[i] = 1.0f;
}
}
}
{ // max
c = origin[i] - (corner[i] + scale[i]);
possibleDistances = { FLT_MAX, FLT_MAX };
bool hit = false;
if (computeRealQuadraticRoots(a, b, c, possibleDistances)) {
for (int j = 0; j < 2; j++) {
if (parabolaVelocityAtT(velocity[i], acceleration[i], possibleDistances[j]) > 0.0f) {
checkPossibleParabolicIntersection(possibleDistances[j], i, minDistance, origin, velocity, acceleration, corner, scale, hit);
}
}
if (hit) {
minFace = BoxFace(2 * i + 1);
minNormal = glm::vec3(0.0f);
minNormal[i] = -1.0f;
}
}
}
}
} else {
if (velocity[i] < 0.0f && acceleration[i] > 0.0f) {
{ // min
c = origin[i] - corner[i];
possibleDistances = { FLT_MAX, FLT_MAX };
if (computeRealQuadraticRoots(a, b, c, possibleDistances)) {
bool hit = false;
for (int j = 0; j < 2; j++) {
if (parabolaVelocityAtT(velocity[i], acceleration[i], possibleDistances[j]) > 0.0f) {
checkPossibleParabolicIntersection(possibleDistances[j], i, minDistance, origin, velocity, acceleration, corner, scale, hit);
}
}
if (hit) {
minFace = BoxFace(2 * i);
minNormal = glm::vec3(0.0f);
minNormal[i] = -1.0f;
}
}
}
} else if (velocity[i] > 0.0f && acceleration[i] < 0.0f) {
{ // max
c = origin[i] - (corner[i] + scale[i]);
possibleDistances = { FLT_MAX, FLT_MAX };
if (computeRealQuadraticRoots(a, b, c, possibleDistances)) {
bool hit = false;
for (int j = 0; j < 2; j++) {
if (parabolaVelocityAtT(velocity[i], acceleration[i], possibleDistances[j]) < 0.0f) {
checkPossibleParabolicIntersection(possibleDistances[j], i, minDistance, origin, velocity, acceleration, corner, scale, hit);
}
}
if (hit) {
minFace = BoxFace(2 * i + 1);
minNormal = glm::vec3(0.0f);
minNormal[i] = 1.0f;
}
}
}
}
}
}
}
}
if (minDistance < FLT_MAX) {
parabolicDistance = minDistance;
face = minFace;
surfaceNormal = minNormal;
return true;
}
return false;
}
void swingTwistDecomposition(const glm::quat& rotation,
const glm::vec3& direction,
glm::quat& swing,

View file

@ -14,6 +14,7 @@
#include <glm/glm.hpp>
#include <vector>
#include "BoxBase.h"
class Plane;
@ -73,6 +74,11 @@ bool findCapsulePlanePenetration(const glm::vec3& capsuleStart, const glm::vec3&
glm::vec3 addPenetrations(const glm::vec3& currentPenetration, const glm::vec3& newPenetration);
bool findIntersection(float origin, float direction, float corner, float size, float& distance);
bool findInsideOutIntersection(float origin, float direction, float corner, float size, float& distance);
bool findRayAABoxIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& corner, const glm::vec3& scale, float& distance,
BoxFace& face, glm::vec3& surfaceNormal);
bool findRaySphereIntersection(const glm::vec3& origin, const glm::vec3& direction,
const glm::vec3& center, float radius, float& distance);
@ -100,6 +106,9 @@ bool findParabolaTriangleIntersection(const glm::vec3& origin, const glm::vec3&
bool findParabolaCapsuleIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,
const glm::vec3& start, const glm::vec3& end, float radius, const glm::quat& rotation, float& parabolicDistance);
bool findParabolaAABoxIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,
const glm::vec3& corner, const glm::vec3& scale, float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal);
/// \brief decomposes rotation into its components such that: rotation = swing * twist
/// \param rotation[in] rotation to decompose
/// \param direction[in] normalized axis about which the twist happens (typically original direction before rotation applied)
@ -202,6 +211,7 @@ bool solve_quartic(float a, float b, float c, float d, glm::vec4& roots);
bool computeRealQuarticRoots(float a, float b, float c, float d, float e, glm::vec4& roots);
bool isWithin(float value, float corner, float size);
bool aaBoxContains(const glm::vec3& point, const glm::vec3& corner, const glm::vec3& scale);
void checkPossibleParabolicIntersectionWithZPlane(float t, float& minDistance,
const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, const glm::vec2& corner, const glm::vec2& scale);