Solved Sphere VS Convex in local space

This commit is contained in:
Diren D Bharwani 2023-03-01 04:34:06 +08:00
parent f620ef226e
commit 3a454953ce
9 changed files with 143 additions and 177 deletions

View File

@ -1,4 +1,4 @@
Start in Fullscreen: false Start in Fullscreen: false
Starting Scene ID: 92914350 Starting Scene ID: 97402985
Window Size: {x: 1920, y: 1080} Window Size: {x: 1920, y: 1080}
Window Title: SHADE Engine Window Title: SHADE Engine

View File

@ -28,9 +28,9 @@
NumberOfChildren: 0 NumberOfChildren: 0
Components: Components:
Camera Component: Camera Component:
Position: {x: 0, y: 2, z: 10} Position: {x: 5, y: 5, z: 0}
Pitch: 0 Pitch: 0
Yaw: 0 Yaw: 90
Roll: 0 Roll: 0
Width: 1920 Width: 1920
Height: 1080 Height: 1080
@ -45,7 +45,7 @@
NumberOfChildren: 0 NumberOfChildren: 0
Components: Components:
Transform Component: Transform Component:
Translate: {x: 2, y: 7.5, z: 0} Translate: {x: 0, y: 7.5, z: 0.75}
Rotate: {x: -0, y: 0, z: 0.785398185} Rotate: {x: -0, y: 0, z: 0.785398185}
Scale: {x: 1, y: 1, z: 1} Scale: {x: 1, y: 1, z: 1}
IsActive: true IsActive: true
@ -60,7 +60,7 @@
Interpolate: true Interpolate: true
Sleeping Enabled: true Sleeping Enabled: true
Freeze Position X: false Freeze Position X: false
Freeze Position Y: true Freeze Position Y: false
Freeze Position Z: false Freeze Position Z: false
Freeze Rotation X: false Freeze Rotation X: false
Freeze Rotation Y: false Freeze Rotation Y: false
@ -176,72 +176,3 @@
Rotation Offset: {x: 0, y: 0, z: 0} Rotation Offset: {x: 0, y: 0, z: 0}
IsActive: true IsActive: true
Scripts: ~ Scripts: ~
- EID: 7
Name: Default
IsActive: true
NumberOfChildren: 0
Components:
Transform Component:
Translate: {x: 0, y: 2, z: 3}
Rotate: {x: 0, y: 0.785398185, z: 0}
Scale: {x: 0.999988496, y: 0.999994099, z: 0.999984443}
IsActive: true
RigidBody Component:
Type: Dynamic
Auto Mass: false
Mass: 1
Drag: 0.00999999978
Angular Drag: 0
Use Gravity: true
Gravity Scale: 1
Interpolate: true
Sleeping Enabled: true
Freeze Position X: false
Freeze Position Y: false
Freeze Position Z: false
Freeze Rotation X: false
Freeze Rotation Y: false
Freeze Rotation Z: false
IsActive: true
Collider Component:
DrawColliders: false
Colliders:
- Is Trigger: false
Collision Tag: 1
Type: Box
Half Extents: {x: 1, y: 1, z: 1}
Friction: 0.400000006
Bounciness: 0
Density: 1
Position Offset: {x: 0, y: 0, z: 0}
Rotation Offset: {x: 0, y: 0, z: 0}
IsActive: true
Scripts:
- Type: PhysicsTestObj
Enabled: true
forceAmount: 200
torqueAmount: 5
- EID: 8
Name: Default
IsActive: true
NumberOfChildren: 0
Components:
Transform Component:
Translate: {x: 0, y: 0, z: 3}
Rotate: {x: -0, y: 0, z: -0}
Scale: {x: 5, y: 1, z: 5}
IsActive: true
Collider Component:
DrawColliders: false
Colliders:
- Is Trigger: false
Collision Tag: 1
Type: Box
Half Extents: {x: 1, y: 1, z: 1}
Friction: 0.400000006
Bounciness: 0
Density: 1
Position Offset: {x: 0, y: 0, z: 0}
Rotation Offset: {x: 0, y: 0, z: 0}
IsActive: true
Scripts: ~

View File

@ -4,7 +4,7 @@
NumberOfChildren: 0 NumberOfChildren: 0
Components: Components:
Camera Component: Camera Component:
Position: {x: 0, y: 2, z: 7} Position: {x: 0, y: 1, z: 3}
Pitch: 0 Pitch: 0
Yaw: 0 Yaw: 0
Roll: 0 Roll: 0
@ -21,17 +21,34 @@
NumberOfChildren: 0 NumberOfChildren: 0
Components: Components:
Transform Component: Transform Component:
Translate: {x: 0.5, y: 0, z: 0} Translate: {x: 0, y: 0, z: 0}
Rotate: {x: 0, y: 0, z: 0} Rotate: {x: 0, y: 0, z: 0}
Scale: {x: 1, y: 1, z: 1} Scale: {x: 2, y: 1, z: 1}
IsActive: true
RigidBody Component:
Type: Static
Auto Mass: false
Mass: .inf
Drag: 0.00999999978
Angular Drag: 0.00999999978
Use Gravity: false
Gravity Scale: 1
Interpolate: true
Sleeping Enabled: true
Freeze Position X: false
Freeze Position Y: false
Freeze Position Z: false
Freeze Rotation X: false
Freeze Rotation Y: false
Freeze Rotation Z: false
IsActive: true IsActive: true
Collider Component: Collider Component:
DrawColliders: true DrawColliders: false
Colliders: Colliders:
- Is Trigger: false - Is Trigger: false
Collision Tag: 1 Collision Tag: 1
Type: Sphere Type: Box
Radius: 3 Half Extents: {x: 1, y: 1, z: 1}
Friction: 0.400000006 Friction: 0.400000006
Bounciness: 0 Bounciness: 0
Density: 1 Density: 1
@ -45,7 +62,7 @@
NumberOfChildren: 0 NumberOfChildren: 0
Components: Components:
Transform Component: Transform Component:
Translate: {x: 0, y: 3, z: 0} Translate: {x: 1.14999998, y: 3, z: 0.550000012}
Rotate: {x: 0, y: 0, z: 0} Rotate: {x: 0, y: 0, z: 0}
Scale: {x: 1, y: 1, z: 1} Scale: {x: 1, y: 1, z: 1}
IsActive: true IsActive: true
@ -56,7 +73,7 @@
Drag: 0.00999999978 Drag: 0.00999999978
Angular Drag: 0.00999999978 Angular Drag: 0.00999999978
Use Gravity: true Use Gravity: true
Gravity Scale: 1 Gravity Scale: 0.25
Interpolate: true Interpolate: true
Sleeping Enabled: true Sleeping Enabled: true
Freeze Position X: false Freeze Position X: false
@ -67,7 +84,7 @@
Freeze Rotation Z: false Freeze Rotation Z: false
IsActive: true IsActive: true
Collider Component: Collider Component:
DrawColliders: true DrawColliders: false
Colliders: Colliders:
- Is Trigger: false - Is Trigger: false
Collision Tag: 1 Collision Tag: 1

View File

@ -28,10 +28,10 @@ namespace SHADE
/* Public Member Functions Definitions */ /* Public Member Functions Definitions */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
void SHCollisionUtils::ShapeTransform::Invert() noexcept SHCollisionUtils::ShapeTransform SHCollisionUtils::ShapeTransform::GetInverse() const noexcept
{ {
orientation.Invert(); const SHQuaternion INV_ORIENTATION = SHQuaternion::Inverse(orientation);
position = SHVec3::Rotate(-position, orientation); return ShapeTransform { SHVec3::Rotate(-position, INV_ORIENTATION), INV_ORIENTATION };
} }

View File

@ -51,7 +51,7 @@ namespace SHADE
/* Member Functions */ /* Member Functions */
/*-------------------------------------------------------- ----------------------*/ /*-------------------------------------------------------- ----------------------*/
void Invert() noexcept; ShapeTransform GetInverse() const noexcept;
}; };
struct FaceQuery struct FaceQuery

View File

@ -90,106 +90,109 @@ namespace SHADE
{ {
// Convert to underlying types // Convert to underlying types
// For the convex, we only need the convex polyhedron shape since the get vertex is pure virtual. // For the convex, we only need the convex polyhedron shape since the get vertex is pure virtual.
//const SHSphere& SPHERE = dynamic_cast<const SHSphere&>(A); const SHSphere& SPHERE = dynamic_cast<const SHSphere&>(A);
//const SHConvexPolyhedron& POLYHEDRON = dynamic_cast<const SHConvexPolyhedron&>(B); const SHConvexPolyhedron& POLY = dynamic_cast<const SHConvexPolyhedron&>(B);
//const SHVec3 CENTER = SPHERE.Center; const SHVec3 CENTER = SPHERE.Center;
//const float RADIUS = SPHERE.GetWorldRadius(); const float RADIUS = SPHERE.GetWorldRadius();
//const SHCollisionUtils::FaceQuery FACE_QUERY = findClosestFace(SPHERE, POLYHEDRON);
//if (!FACE_QUERY.colliding)
// return false;
//uint32_t numContacts = 0;
//const float PENETRATION = RADIUS - FACE_QUERY.bestDistance;
//SHContact contact;
//contact.featurePair.key = 0;
//// Check if center is inside polyhedron (below the face)
//if (FACE_QUERY.bestDistance < SHMath::EPSILON)
//{
// manifold.normal = -POLYHEDRON.GetNormal(FACE_QUERY.closestFace);
// contact.penetration = PENETRATION;
// contact.position = CENTER;
// manifold.contacts[numContacts++] = contact;
// manifold.numContacts = numContacts;
// return true;
//}
//// Find closest face of polygon to circle
//const int32_t CLOSEST_POINT = findClosestPoint(SPHERE, POLYHEDRON, FACE_QUERY.closestFace);
//const auto& FACE = POLYHEDRON.GetFace(FACE_QUERY.closestFace);
//const SHVec3& FACE_NORMAL = POLYHEDRON.GetNormal(FACE_QUERY.closestFace);
//const int32_t NUM_VERTICES = static_cast<int32_t>(FACE.vertexIndices.size());
//// Get points and build tangents
//const int32_t P2_INDEX = (CLOSEST_POINT + 1) % NUM_VERTICES;
//const int32_t P3_INDEX = CLOSEST_POINT == 0 ? NUM_VERTICES - 1 : CLOSEST_POINT - 1;
//const SHVec3 P1 = POLYHEDRON.GetVertex(FACE.vertexIndices[CLOSEST_POINT].index);
//const SHVec3 P2 = POLYHEDRON.GetVertex(FACE.vertexIndices[P2_INDEX].index);
//const SHVec3 P3 = POLYHEDRON.GetVertex(FACE.vertexIndices[P3_INDEX].index);
//const SHVec3 TANGENT_1 = SHVec3::Normalise(P2 - P1);
//const SHVec3 TANGENT_2 = SHVec3::Normalise(P3 - P1);
//// Get the voronoi region it belongs in
//const int32_t REGION = findVoronoiRegion(SPHERE, P1, FACE_NORMAL, TANGENT_1, TANGENT_2);
//if (REGION == 0)
// return false;
//// Create contact information based on region
//const SHVec3 P1_TO_CENTER = CENTER - P1;
//switch (REGION)
//{
// case 1: // Region A
// case 2: // Region B
// {
// // Find closest point
// const SHVec3& TANGENT = REGION == 1 ? TANGENT_1 : TANGENT_2;
// const SHVec3 CP = P1 + TANGENT * SHVec3::Dot(P1_TO_CENTER, TANGENT);
// const SHVec3 CP_TO_CENTER = CENTER - CP;
// manifold.normal = -SHVec3::Normalise(CP_TO_CENTER);
// contact.penetration = RADIUS - SHVec3::Dot(CP_TO_CENTER, -manifold.normal);
// contact.position = CP;
// break;
// }
// case 3: // Region C
// {
// manifold.normal = -SHVec3::Normalise(P1_TO_CENTER);
// contact.penetration = RADIUS - P1_TO_CENTER.Length();
// contact.position = P1;
// break;
// }
// case 4: // Region D
// {
// manifold.normal = -FACE_NORMAL;
// contact.penetration = PENETRATION;
// contact.position = CENTER - FACE_NORMAL * RADIUS;
// break;
// }
// default: return false; // Should never happen
//}
//manifold.contacts[numContacts++] = contact;
//manifold.numContacts = numContacts;
//return true;
const SHCollisionUtils::FaceQuery FACE_QUERY = findClosestFace(SPHERE, POLY);
if (!FACE_QUERY.colliding)
return false; return false;
const SHCollisionUtils::ShapeTransform TF_A = { SPHERE.GetWorldCentroid(), SPHERE.GetWorldOrientation() };
const SHCollisionUtils::ShapeTransform TF_B = { POLY.GetWorldCentroid(), POLY.GetWorldOrientation() };
const SHCollisionUtils::ShapeTransform INV_TF_A = TF_A.GetInverse();
const SHCollisionUtils::ShapeTransform INV_TF_B = TF_B.GetInverse();
uint32_t numContacts = 0;
const float PENETRATION = RADIUS - FACE_QUERY.bestDistance;
SHContact contact;
contact.featurePair.key = 0;
// Check if center is inside polyhedron (below the face)
if (FACE_QUERY.bestDistance < SHMath::EPSILON)
{
manifold.normal = -POLY.GetNormal(FACE_QUERY.closestFace);
contact.penetration = PENETRATION;
// A is the sphere, B is the polyhedron
contact.localPointA = SHVec3::Rotate(manifold.normal * RADIUS, INV_TF_A.orientation);
contact.localPointB = (INV_TF_B * CENTER) + manifold.normal * (PENETRATION - RADIUS);
manifold.contacts[numContacts++] = contact;
manifold.numContacts = numContacts;
return true;
}
// Find closest face of polygon to circle
const int32_t CLOSEST_POINT = findClosestPoint(SPHERE, POLY, FACE_QUERY.closestFace);
const auto& FACE = POLY.GetFace(FACE_QUERY.closestFace);
const SHVec3& FACE_NORMAL = POLY.GetNormal(FACE_QUERY.closestFace);
const int32_t NUM_VERTICES = static_cast<int32_t>(FACE.vertexIndices.size());
// Get points and build tangents
const int32_t P2_INDEX = (CLOSEST_POINT + 1) % NUM_VERTICES;
const int32_t P3_INDEX = CLOSEST_POINT == 0 ? NUM_VERTICES - 1 : CLOSEST_POINT - 1;
const SHVec3 P1 = POLY.GetVertex(FACE.vertexIndices[CLOSEST_POINT].index);
const SHVec3 P2 = POLY.GetVertex(FACE.vertexIndices[P2_INDEX].index);
const SHVec3 P3 = POLY.GetVertex(FACE.vertexIndices[P3_INDEX].index);
const SHVec3 TANGENT_1 = SHVec3::Normalise(P2 - P1);
const SHVec3 TANGENT_2 = SHVec3::Normalise(P3 - P1);
// Get the voronoi region it belongs in
const int32_t REGION = findVoronoiRegion(SPHERE, P1, FACE_NORMAL, TANGENT_1, TANGENT_2);
if (REGION == 0)
return false;
// Create contact information based on region
const SHVec3 P1_TO_CENTER = CENTER - P1;
switch (REGION)
{
case 1: // Region A
case 2: // Region B
{
// Find closest point
const SHVec3& TANGENT = REGION == 1 ? TANGENT_1 : TANGENT_2;
const SHVec3 CP = P1 + TANGENT * SHVec3::Dot(P1_TO_CENTER, TANGENT);
const SHVec3 CP_TO_CENTER = CENTER - CP;
manifold.normal = -SHVec3::Normalise(CP_TO_CENTER);
contact.penetration = RADIUS - SHVec3::Dot(CP_TO_CENTER, -manifold.normal);
break;
}
case 3: // Region C
{
manifold.normal = -SHVec3::Normalise(P1_TO_CENTER);
contact.penetration = RADIUS - P1_TO_CENTER.Length();
break;
}
case 4: // Region D
{
manifold.normal = -FACE_NORMAL;
contact.penetration = PENETRATION;
break;
}
default: return false; // Should never happen
}
contact.localPointA = SHVec3::Rotate(manifold.normal * RADIUS, INV_TF_A.orientation);
contact.localPointB = (INV_TF_B * CENTER) + manifold.normal * (RADIUS - contact.penetration);
manifold.contacts[numContacts++] = contact;
manifold.numContacts = numContacts;
return true;
} }
bool SHCollision::ConvexVsSphere(SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept bool SHCollision::ConvexVsSphere(SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept
@ -197,6 +200,10 @@ namespace SHADE
if (SphereVsConvex(manifold, B, A)) if (SphereVsConvex(manifold, B, A))
{ {
manifold.normal = -manifold.normal; manifold.normal = -manifold.normal;
// There can only be one contact in this scenario, so we swap the first
std::swap(manifold.contacts[0].localPointA, manifold.contacts[0].localPointB);
return true; return true;
}; };

View File

@ -66,11 +66,10 @@ namespace SHADE
if (DISTANCE_BETWEEN_CENTERS_SQUARED > COMBINED_RADIUS_SQUARED) if (DISTANCE_BETWEEN_CENTERS_SQUARED > COMBINED_RADIUS_SQUARED)
return false; return false;
SHCollisionUtils::ShapeTransform inverseTransformA = { SPHERE_A.GetWorldCentroid(), SPHERE_A.GetWorldOrientation() }; const SHCollisionUtils::ShapeTransform TF_A = { SPHERE_A.GetWorldCentroid(), SPHERE_A.GetWorldOrientation() };
SHCollisionUtils::ShapeTransform inverseTransformB = { SPHERE_B.GetWorldCentroid(), SPHERE_B.GetWorldOrientation() }; const SHCollisionUtils::ShapeTransform TF_B = { SPHERE_B.GetWorldCentroid(), SPHERE_B.GetWorldOrientation() };
inverseTransformA.Invert();
inverseTransformB.Invert();
// Only populate the manifold if there is a collision // Only populate the manifold if there is a collision
@ -84,16 +83,16 @@ namespace SHADE
if (SHMath::CompareFloat(DISTANCE_BETWEEN_CENTERS_SQUARED, 0.0f)) if (SHMath::CompareFloat(DISTANCE_BETWEEN_CENTERS_SQUARED, 0.0f))
{ {
manifold.normal = SHVec3::UnitY; manifold.normal = SHVec3::UnitY;
contact.localPointA = RADIUS_A * SHVec3::Rotate(manifold.normal, inverseTransformA.orientation); contact.localPointA = RADIUS_A * SHVec3::Rotate(manifold.normal, TF_A.GetInverse().orientation);
contact.localPointB = RADIUS_B * SHVec3::Rotate(manifold.normal, inverseTransformB.orientation); contact.localPointB = RADIUS_B * SHVec3::Rotate(manifold.normal, TF_B.GetInverse().orientation);
manifold.contacts[numContacts++] = contact; manifold.contacts[numContacts++] = contact;
} }
else else
{ {
manifold.normal = SHVec3::Normalise(A_TO_B); manifold.normal = SHVec3::Normalise(A_TO_B);
contact.localPointA = RADIUS_A * SHVec3::Normalise(inverseTransformA * CENTER_B); contact.localPointA = RADIUS_A * SHVec3::Normalise(TF_A.GetInverse() * CENTER_B);
contact.localPointB = RADIUS_B * SHVec3::Normalise(inverseTransformB * CENTER_A); contact.localPointB = RADIUS_B * SHVec3::Normalise(TF_B.GetInverse() * CENTER_A);
manifold.contacts[numContacts++] = contact; manifold.contacts[numContacts++] = contact;

View File

@ -53,11 +53,18 @@ namespace SHADE
}; };
const auto* SHAPE_A = manifold.shapeA; const auto* SHAPE_A = manifold.shapeA;
const auto* SHAPE_B = manifold.shapeB;
const SHCollisionUtils::ShapeTransform TF_A = { SHAPE_A->GetWorldCentroid(), SHAPE_A->GetWorldOrientation() }; const SHCollisionUtils::ShapeTransform TF_A = { SHAPE_A->GetWorldCentroid(), SHAPE_A->GetWorldOrientation() };
const SHCollisionUtils::ShapeTransform TF_B = { SHAPE_B->GetWorldCentroid(), SHAPE_B->GetWorldOrientation() };
for (uint32_t i = 0; i < manifold.numContacts; ++i) for (uint32_t i = 0; i < manifold.numContacts; ++i)
collisionEvent.contactPoints[i] = TF_A * manifold.contacts[i].localPointA; {
const SHVec3 WORLD_POINT_A = TF_A * manifold.contacts[i].localPointA;
const SHVec3 WORLD_POINT_B = TF_B * manifold.contacts[i].localPointB;
collisionEvent.contactPoints[i] = SHVec3::ClampedLerp(WORLD_POINT_A, WORLD_POINT_B, 0.5f);
}
collisionEvents.emplace_back(collisionEvent); collisionEvents.emplace_back(collisionEvent);
} }
@ -77,15 +84,20 @@ namespace SHADE
continue; continue;
const auto* SHAPE_A = manifold.shapeA; const auto* SHAPE_A = manifold.shapeA;
const auto* SHAPE_B = manifold.shapeB;
const SHCollisionUtils::ShapeTransform TF_A = { SHAPE_A->GetWorldCentroid(), SHAPE_A->GetWorldOrientation() }; const SHCollisionUtils::ShapeTransform TF_A = { SHAPE_A->GetWorldCentroid(), SHAPE_A->GetWorldOrientation() };
const SHCollisionUtils::ShapeTransform TF_B = { SHAPE_B->GetWorldCentroid(), SHAPE_B->GetWorldOrientation() };
for (uint32_t i = 0; i < manifold.numContacts; ++i) for (uint32_t i = 0; i < manifold.numContacts; ++i)
{ {
// Contact position will be the world position of localPointA // Contact position will be the middle of worldPointA & worldPointB
const SHVec3 WORLD_POINT_A = TF_A * manifold.contacts[i].localPointA;
const SHVec3 WORLD_POINT_B = TF_B * manifold.contacts[i].localPointB;
const ContactInfo INFO const ContactInfo INFO
{ {
.position = TF_A * manifold.contacts[i].localPointA .position = SHVec3::ClampedLerp(WORLD_POINT_A, WORLD_POINT_B, 0.5f)
, .normal = manifold.normal , .normal = manifold.normal
}; };

View File

@ -80,7 +80,7 @@ namespace SHADE
for (auto& contactPoint : CONTACT_POINTS) for (auto& contactPoint : CONTACT_POINTS)
{ {
const SHMatrix TRS = SHMatrix::Transform(contactPoint.position, SHQuaternion::Identity, SHVec3{ 0.1f }); const SHMatrix TRS = SHMatrix::Transform(contactPoint.position, SHQuaternion::Identity, SHVec3{ 0.1f });
debugDrawSystem->DrawCube(TRS, CONTACT_COLOUR); debugDrawSystem->DrawSphere(TRS, CONTACT_COLOUR);
debugDrawSystem->DrawLine(contactPoint.position, contactPoint.position + contactPoint.normal * 0.5f, CONTACT_COLOUR, true); debugDrawSystem->DrawLine(contactPoint.position, contactPoint.position + contactPoint.normal * 0.5f, CONTACT_COLOUR, true);
} }
} }