Fixed contact point derivation and manifold reduction.
System tends to be a bit unstable now, will look into it. Islands and sleeping needs to be implemented to reduce unnecessary drift
This commit is contained in:
parent
57498bb8b8
commit
3e91f99d78
|
@ -45,9 +45,9 @@
|
|||
NumberOfChildren: 0
|
||||
Components:
|
||||
Camera Component:
|
||||
Position: {x: -0.5, y: 10, z: -3}
|
||||
Position: {x: -0.5, y: 10, z: 3}
|
||||
Pitch: 0
|
||||
Yaw: 180
|
||||
Yaw: 0
|
||||
Roll: 0
|
||||
Width: 1920
|
||||
Height: 1080
|
||||
|
@ -280,8 +280,8 @@
|
|||
NumberOfChildren: 0
|
||||
Components:
|
||||
Transform Component:
|
||||
Translate: {x: 0.524352431, y: 13.5, z: 0.0463808179}
|
||||
Rotate: {x: -0, y: 0.785398066, z: 0.785398185}
|
||||
Translate: {x: -0.5, y: 13.5, z: 0}
|
||||
Rotate: {x: -0, y: 0, z: 0}
|
||||
Scale: {x: 0.999999821, y: 0.999999821, z: 0.999999881}
|
||||
IsActive: true
|
||||
RigidBody Component:
|
||||
|
@ -321,7 +321,7 @@
|
|||
NumberOfChildren: 0
|
||||
Components:
|
||||
Transform Component:
|
||||
Translate: {x: -0.5, y: 10, z: 0}
|
||||
Translate: {x: 0, y: 10, z: 0}
|
||||
Rotate: {x: -0, y: 0, z: -0}
|
||||
Scale: {x: 1, y: 1, z: 1}
|
||||
IsActive: true
|
||||
|
|
|
@ -116,7 +116,8 @@ namespace SHADE
|
|||
static float distanceBetweenEdges (const SHConvexPolyhedron& A, const SHConvexPolyhedron& B, int32_t edgeA, int32_t edgeB) noexcept;
|
||||
static SHVec3 findClosestPointBetweenEdges (const SHConvexPolyhedron& A, const SHConvexPolyhedron& B, int32_t edgeA, int32_t edgeB) noexcept;
|
||||
static int32_t findIncidentFace (const SHConvexPolyhedron& poly, const SHVec3& normal) noexcept;
|
||||
static std::vector<SHContact> clipsPolygonAgainstPlane (const std::vector<SHContact>& in, const SHPlane& plane) noexcept;
|
||||
static std::vector<SHContact> clipPolygonAgainstPlane (const std::vector<SHContact>& in, const SHPlane& plane) noexcept;
|
||||
static std::vector<SHContact> reduceContacts (const std::vector<SHContact>& in, const SHVec3& normal) noexcept;
|
||||
|
||||
// TODO: Reduce Manifold
|
||||
};
|
||||
|
|
|
@ -135,14 +135,17 @@ namespace SHADE
|
|||
std::vector<SHContact> incidentPolygon;
|
||||
for (const int32_t i : std::views::iota(0, MAX_NUM_CONTACTS))
|
||||
{
|
||||
SHContact contact;
|
||||
|
||||
const int32_t VERTEX_INDEX = INCIDENT_FACE.vertexIndices[i].index;
|
||||
|
||||
incidentPolygon[i].position = incidentPoly->GetVertex(VERTEX_INDEX);
|
||||
contact.position = incidentPoly->GetVertex(VERTEX_INDEX);
|
||||
contact.featurePair.indexA = minFaceQuery.closestFace;
|
||||
contact.featurePair.indexB = VERTEX_INDEX;
|
||||
contact.featurePair.typeA = SHUtilities::ConvertEnum(SHContactFeatures::Type::FACE);
|
||||
contact.featurePair.typeB = SHUtilities::ConvertEnum(SHContactFeatures::Type::VERTEX);
|
||||
|
||||
incidentPolygon[i].featurePair.indexA = minFaceQuery.closestFace;
|
||||
incidentPolygon[i].featurePair.indexB = VERTEX_INDEX;
|
||||
incidentPolygon[i].featurePair.typeA = SHUtilities::ConvertEnum(SHContactFeatures::Type::FACE);
|
||||
incidentPolygon[i].featurePair.typeB = SHUtilities::ConvertEnum(SHContactFeatures::Type::VERTEX);
|
||||
incidentPolygon.emplace_back(contact);
|
||||
}
|
||||
|
||||
// Number of vertices == number of edges on face
|
||||
|
@ -164,7 +167,7 @@ namespace SHADE
|
|||
const SHVec3 TANGENT = SHVec3::Normalise(V2 - V1);
|
||||
const SHPlane PLANE{ V1, SHVec3::Cross(REFERENCE_NORMAL, TANGENT) };
|
||||
|
||||
auto clipped = clipsPolygonAgainstPlane(incidentPolygon, PLANE);
|
||||
auto clipped = clipPolygonAgainstPlane(incidentPolygon, PLANE);
|
||||
memcpy_s(incidentPolygon.data(), COPY_SIZE, clipped.data(), COPY_SIZE);
|
||||
}
|
||||
|
||||
|
@ -198,13 +201,18 @@ namespace SHADE
|
|||
|
||||
if (numContacts > 4)
|
||||
{
|
||||
// TODO: Reduce the manifold
|
||||
auto reducedSet = reduceContacts(finalContacts, REFERENCE_NORMAL);
|
||||
numContacts = 4;
|
||||
|
||||
memcpy_s(manifold.contacts, sizeof(SHContact) * numContacts, reducedSet.data(), sizeof(SHContact) * numContacts);
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy_s(manifold.contacts, sizeof(SHContact) * numContacts, finalContacts.data(), sizeof(SHContact) * numContacts);
|
||||
}
|
||||
|
||||
|
||||
|
||||
manifold.normal = flipNormal ? -REFERENCE_NORMAL : REFERENCE_NORMAL;
|
||||
manifold.numContacts = numContacts;
|
||||
|
||||
|
@ -415,8 +423,10 @@ namespace SHADE
|
|||
return bestFace;
|
||||
}
|
||||
|
||||
std::vector<SHContact> SHCollision::clipsPolygonAgainstPlane(const std::vector<SHContact>& in, const SHPlane& plane) noexcept
|
||||
std::vector<SHContact> SHCollision::clipPolygonAgainstPlane(const std::vector<SHContact>& in, const SHPlane& plane) noexcept
|
||||
{
|
||||
static constexpr float THRESHOLD = -SHMath::EPSILON;
|
||||
|
||||
std::vector<SHContact> out;
|
||||
|
||||
//
|
||||
|
@ -429,26 +439,30 @@ namespace SHADE
|
|||
* This variation is has 4 scenarios:
|
||||
*
|
||||
* 1. Both behind: ignore
|
||||
* 2. Head behind, tail in-front: Keep interpolated position
|
||||
* 3. Both in-front: Keep tail
|
||||
* 2. Both in-front: Keep tail
|
||||
* 3. Head behind, tail in-front: Keep interpolated position
|
||||
* 4. Head in-front, tail behind: Keep head & interpolated position
|
||||
*/
|
||||
|
||||
const size_t NUM_POINTS = in.size();
|
||||
for (size_t i = 0; i < NUM_POINTS; ++i)
|
||||
{
|
||||
const SHContact& HEAD = in[i];
|
||||
const SHContact& TAIL = in[(i + 1) % NUM_POINTS];
|
||||
const SHContact& TAIL = in[i];
|
||||
const SHContact& HEAD = in[(i + 1) % NUM_POINTS];
|
||||
|
||||
const float HEAD_DISTANCE = plane.SignedDistance(HEAD.position);
|
||||
const float TAIL_DISTANCE = plane.SignedDistance(TAIL.position);
|
||||
const float HEAD_DISTANCE = plane.SignedDistance(HEAD.position);
|
||||
|
||||
// Scenario 1: Both Behind
|
||||
if (HEAD_DISTANCE < 0.0f && TAIL_DISTANCE < 0.0f)
|
||||
if (HEAD_DISTANCE < THRESHOLD && TAIL_DISTANCE < THRESHOLD)
|
||||
continue;
|
||||
|
||||
// Scenario 2: Tail In-Front, Head Behind
|
||||
if (HEAD_DISTANCE >= 0.0f && TAIL_DISTANCE < 0.0f)
|
||||
// Scenario 2: Both In-Front
|
||||
if (HEAD_DISTANCE >= THRESHOLD && TAIL_DISTANCE >= THRESHOLD)
|
||||
out.emplace_back(TAIL);
|
||||
|
||||
// Scenario 3: Head In-Front, Tail Behind
|
||||
if (HEAD_DISTANCE >= THRESHOLD && TAIL_DISTANCE < THRESHOLD)
|
||||
{
|
||||
SHContact interpolated;
|
||||
|
||||
|
@ -463,16 +477,10 @@ namespace SHADE
|
|||
out.emplace_back(interpolated);
|
||||
}
|
||||
|
||||
// Scenario 3: Both In-Front
|
||||
if (HEAD_DISTANCE >= 0.0f && TAIL_DISTANCE >= 0.0f)
|
||||
// Scenario 4: Head Behind, Tail In-Front
|
||||
if (HEAD_DISTANCE < THRESHOLD && TAIL_DISTANCE >= THRESHOLD)
|
||||
{
|
||||
out.emplace_back(TAIL);
|
||||
}
|
||||
|
||||
// Scenario 4: Head Behind, Tail In-Front
|
||||
if (HEAD_DISTANCE < 0.0f && TAIL_DISTANCE >= 0.0f)
|
||||
{
|
||||
out.emplace_back(HEAD);
|
||||
|
||||
SHContact interpolated;
|
||||
|
||||
|
@ -491,4 +499,111 @@ namespace SHADE
|
|||
return out;
|
||||
}
|
||||
|
||||
std::vector<SHContact> SHCollision::reduceContacts(const std::vector<SHContact>& in, const SHVec3& normal) noexcept
|
||||
{
|
||||
std::vector<SHContact> out;
|
||||
std::unordered_map<int, SHVec3> contactMap;
|
||||
|
||||
const int NUM_IN = static_cast<int>(in.size());
|
||||
for (const int i : std::views::iota(0, NUM_IN))
|
||||
contactMap.emplace(i, in[i].position);
|
||||
|
||||
// We'll be reusing these in all 4 phases
|
||||
float bestMetric = std::numeric_limits<float>::lowest();
|
||||
int bestVertex = -1;
|
||||
|
||||
// Find the furthest point alone the -Z-axis
|
||||
for (const auto& [index, pos] : contactMap)
|
||||
{
|
||||
const float PROJECTION = SHVec3::Dot(pos, -SHVec3::UnitZ);
|
||||
|
||||
if (PROJECTION > bestMetric)
|
||||
{
|
||||
bestMetric = PROJECTION;
|
||||
bestVertex = index;
|
||||
}
|
||||
}
|
||||
|
||||
const SHVec3& A = out.emplace_back(in[bestVertex]).position;
|
||||
contactMap.erase(bestVertex);
|
||||
|
||||
// Reset
|
||||
bestMetric = std::numeric_limits<float>::lowest();
|
||||
bestVertex = -1;
|
||||
|
||||
// Find farthest point
|
||||
for (const auto& [index, pos] : contactMap)
|
||||
{
|
||||
const float DISTANCE = SHVec3::DistanceSquared(A, pos);
|
||||
if (DISTANCE > bestMetric)
|
||||
{
|
||||
bestMetric = DISTANCE;
|
||||
bestVertex = index;
|
||||
}
|
||||
}
|
||||
|
||||
const SHVec3& B = out.emplace_back(in[bestVertex]).position;
|
||||
contactMap.erase(bestVertex);
|
||||
|
||||
// Reset
|
||||
bestMetric = std::numeric_limits<float>::lowest();
|
||||
bestVertex = -1;
|
||||
|
||||
// Find next point with largest area
|
||||
// Largest area can be found as 0.5 * crossProduct /dot normal
|
||||
for (const auto& [index, pos] : contactMap)
|
||||
{
|
||||
const SHVec3 CA = A - pos;
|
||||
const SHVec3 CB = B - pos;
|
||||
|
||||
const float AREA = SHVec3::Dot(0.5f * SHVec3::Cross(CA, CB), normal);
|
||||
|
||||
// We only want triangles with negative areas!
|
||||
if (AREA > bestMetric)
|
||||
{
|
||||
bestMetric = AREA;
|
||||
bestVertex = index;
|
||||
}
|
||||
}
|
||||
|
||||
const SHVec3& C = out.emplace_back(in[bestVertex]).position;
|
||||
contactMap.erase(bestVertex);
|
||||
|
||||
const float CURRENT_AREA = bestMetric;
|
||||
|
||||
// Reset
|
||||
bestMetric = std::numeric_limits<float>::lowest();
|
||||
bestVertex = -1;
|
||||
|
||||
// Find the contact point which forms the largest polygon
|
||||
// We need to construct 3 triangles and compare them
|
||||
for (const auto& [index, pos] : contactMap)
|
||||
{
|
||||
const SHVec3 DA = A - pos;
|
||||
const SHVec3 DB = B - pos;
|
||||
const SHVec3 DC = C - pos;
|
||||
|
||||
float areas[3] =
|
||||
{
|
||||
SHVec3::Dot(0.5f * SHVec3::Cross(DA, DB), normal) // DAB
|
||||
, SHVec3::Dot(0.5f * SHVec3::Cross(DB, DC), normal) // DBC
|
||||
, SHVec3::Dot(0.5f * SHVec3::Cross(DC, DA), normal) // DCA
|
||||
};
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
if (areas[i] < 0.0f && areas[i] + CURRENT_AREA > bestMetric)
|
||||
{
|
||||
bestMetric = areas[i] + CURRENT_AREA;
|
||||
bestVertex = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out.emplace_back(in[bestVertex]);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
} // namespace SHADE
|
|
@ -113,6 +113,11 @@ namespace SHADE
|
|||
return relativeExtents;
|
||||
}
|
||||
|
||||
int32_t SHBox::GetNumVertices() const noexcept
|
||||
{
|
||||
return NUM_VERTICES;
|
||||
}
|
||||
|
||||
SHVec3 SHBox::GetVertex(int index) const
|
||||
{
|
||||
if (index < 0 || index >= NUM_VERTICES)
|
||||
|
@ -290,30 +295,4 @@ namespace SHADE
|
|||
|
||||
return SHAABB{ CENTROID, HALF_EXTENTS };
|
||||
}
|
||||
|
||||
SHVec3 SHBox::FindSupportPoint(const SHVec3& direction) const noexcept
|
||||
{
|
||||
float bestDistance = std::numeric_limits<float>::lowest();
|
||||
|
||||
|
||||
SHVec3 vertices[NUM_VERTICES];
|
||||
GetCorners(vertices);
|
||||
|
||||
// No reason to put the center really..
|
||||
SHVec3 bestPoint = Center;
|
||||
for (auto& vertex : vertices)
|
||||
{
|
||||
const float PROJECTION = SHVec3::Dot(vertex, direction);
|
||||
|
||||
if (PROJECTION > bestDistance)
|
||||
{
|
||||
bestDistance = PROJECTION;
|
||||
bestPoint = vertex;
|
||||
}
|
||||
}
|
||||
|
||||
return bestPoint;
|
||||
}
|
||||
|
||||
|
||||
} // namespace SHADE
|
|
@ -85,6 +85,7 @@ namespace SHADE
|
|||
|
||||
// Overriden Methods
|
||||
|
||||
[[nodiscard]] int32_t GetNumVertices () const noexcept override;
|
||||
[[nodiscard]] SHVec3 GetVertex (int index) const override;
|
||||
[[nodiscard]] SHVec3 GetNormal (int faceIndex) const override;
|
||||
|
||||
|
@ -114,7 +115,6 @@ namespace SHADE
|
|||
[[nodiscard]] SHRaycastResult Raycast (const SHRay& ray) const noexcept override;
|
||||
[[nodiscard]] SHMatrix GetTRS () const noexcept override;
|
||||
[[nodiscard]] SHAABB ComputeAABB () const noexcept override;
|
||||
[[nodiscard]] SHVec3 FindSupportPoint (const SHVec3& direction) const noexcept override;
|
||||
|
||||
private:
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
|
|
|
@ -92,5 +92,26 @@ namespace SHADE
|
|||
return halfEdgeStructure->GetHalfEdge(index);
|
||||
}
|
||||
|
||||
SHVec3 SHConvexPolyhedron::FindSupportPoint(const SHVec3& direction) const noexcept
|
||||
{
|
||||
float bestDistance = std::numeric_limits<float>::lowest();
|
||||
SHVec3 bestPoint = SHVec3::Zero;
|
||||
|
||||
for (const int32_t i : std::views::iota(0, GetNumVertices()))
|
||||
{
|
||||
const SHVec3 VERTEX = GetVertex(i);
|
||||
const float PROJECTION = SHVec3::Dot(VERTEX, direction);
|
||||
|
||||
if (PROJECTION > bestDistance)
|
||||
{
|
||||
bestDistance = PROJECTION;
|
||||
bestPoint = VERTEX;
|
||||
}
|
||||
}
|
||||
|
||||
return bestPoint;
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace SHADE
|
|
@ -65,8 +65,9 @@ namespace SHADE
|
|||
|
||||
// Virtual Methods
|
||||
|
||||
[[nodiscard]] virtual SHVec3 GetVertex (int index) const = 0;
|
||||
[[nodiscard]] virtual SHVec3 GetNormal (int faceIndex) const = 0;
|
||||
[[nodiscard]] virtual int32_t GetNumVertices () const noexcept = 0;
|
||||
[[nodiscard]] virtual SHVec3 GetVertex (int index) const = 0;
|
||||
[[nodiscard]] virtual SHVec3 GetNormal (int faceIndex) const = 0;
|
||||
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* Member Functions */
|
||||
|
@ -80,7 +81,7 @@ namespace SHADE
|
|||
* @return
|
||||
* The most extreme vertex in the given direction.
|
||||
*/
|
||||
[[nodiscard]] virtual SHVec3 FindSupportPoint (const SHVec3& direction) const noexcept = 0;
|
||||
[[nodiscard]] SHVec3 FindSupportPoint (const SHVec3& direction) const noexcept;
|
||||
|
||||
protected:
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
|
|
Loading…
Reference in New Issue