From 400cbb35d9b43685f02a79c651b89e4be4583f11 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Fri, 30 Dec 2022 00:45:37 +0800 Subject: [PATCH] Partial implementation of a generic convex polyhedron object --- Assets/Scenes/PhysicsSandbox.shade | 1 + .../CollisionShapes/SHBoxCollisionShape.cpp | 18 ++ .../CollisionShapes/SHBoxCollisionShape.h | 24 +- .../CollisionShapes/SHConvexPolyhedron.cpp | 216 ++++++++++++++++++ .../CollisionShapes/SHConvexPolyhedron.h | 144 ++++++++++++ 5 files changed, 392 insertions(+), 11 deletions(-) create mode 100644 SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedron.cpp create mode 100644 SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedron.h diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index e001867a..67d35bb5 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -131,6 +131,7 @@ Colliders: - Is Trigger: false Type: Box + Half Extents: {x: 1, y: 1, z: 1} Friction: 0.400000006 Bounciness: 0 Density: 1 diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.cpp index 67e497a8..6d7308da 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.cpp +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.cpp @@ -18,6 +18,19 @@ #include "Math/SHMatrix.h" #include "Physics/Collision/SHCollider.h" +/* + * Local box vertices, faces & half-edges + * + * Vertices (Front/Back Face): + * + * 3/7 ---------- 2/6 + * | | + * | | + * | | + * 0/4 ---------- 1/5 + * + */ + namespace SHADE { /*-----------------------------------------------------------------------------------*/ @@ -29,6 +42,7 @@ namespace SHADE , SHBox () , relativeExtents { SHVec3::One } , scale { SHVec3::One } + , polyhedron { nullptr } {} SHBoxCollisionShape::SHBoxCollisionShape(const SHBoxCollisionShape& rhs) noexcept @@ -36,6 +50,7 @@ namespace SHADE , SHBox (rhs.Center, rhs.Extents, rhs.Orientation) , relativeExtents { rhs.relativeExtents } , scale { rhs.scale } + , polyhedron { rhs.polyhedron } { material = rhs.material; @@ -52,6 +67,7 @@ namespace SHADE , SHBox (rhs.Center, rhs.Extents, rhs.Orientation) , relativeExtents { rhs.relativeExtents } , scale { rhs.scale } + , polyhedron { rhs.polyhedron } { material = rhs.material; @@ -93,6 +109,7 @@ namespace SHADE relativeExtents = rhs.relativeExtents; scale = rhs.scale; + polyhedron = rhs.polyhedron; return *this; } @@ -120,6 +137,7 @@ namespace SHADE relativeExtents = rhs.relativeExtents; scale = rhs.scale; + polyhedron = rhs.polyhedron; return *this; } diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.h index 0cb925f2..142858e0 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.h @@ -13,6 +13,7 @@ // Project Headers #include "Math/Geometry/SHBox.h" #include "SHCollisionShape.h" +#include "SHConvexPolyhedron.h" namespace SHADE { @@ -73,20 +74,20 @@ namespace SHADE /* Getter Functions */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] SHVec3 GetCenter () const noexcept; - [[nodiscard]] SHVec3 GetWorldExtents () const noexcept; - [[nodiscard]] SHVec3 GetRelativeExtents () const noexcept; - [[nodiscard]] SHQuaternion GetOrientation () const noexcept; + [[nodiscard]] SHVec3 GetCenter () const noexcept; + [[nodiscard]] SHVec3 GetWorldExtents () const noexcept; + [[nodiscard]] SHVec3 GetRelativeExtents () const noexcept; + [[nodiscard]] SHQuaternion GetOrientation () const noexcept; /*---------------------------------------------------------------------------------*/ /* Setter Functions */ /*---------------------------------------------------------------------------------*/ - void SetCenter (const SHVec3& newCenter) noexcept; - void SetWorldExtents (const SHVec3& newWorldExtents) noexcept; - void SetRelativeExtents (const SHVec3& newRelativeExtents) noexcept; - void SetOrientation (const SHQuaternion& newOrientation) noexcept; - void SetScale (const SHVec3& newScale) noexcept; + void SetCenter (const SHVec3& newCenter) noexcept; + void SetWorldExtents (const SHVec3& newWorldExtents) noexcept; + void SetRelativeExtents (const SHVec3& newRelativeExtents) noexcept; + void SetOrientation (const SHQuaternion& newOrientation) noexcept; + void SetScale (const SHVec3& newScale) noexcept; /*---------------------------------------------------------------------------------*/ /* Function Members */ @@ -150,8 +151,9 @@ namespace SHADE /* Data Members */ /*---------------------------------------------------------------------------------*/ - SHVec3 relativeExtents; - SHVec3 scale; // Intended to be passed in by the base collider. + SHVec3 relativeExtents; + SHVec3 scale; // Intended to be passed in by the base collider. + SHConvexPolyhedron* polyhedron; // Defines the polyhedron by it's half edges. }; diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedron.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedron.cpp new file mode 100644 index 00000000..cb5fad76 --- /dev/null +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedron.cpp @@ -0,0 +1,216 @@ +/**************************************************************************************** + * \file SHConvexPolyhedron.cpps + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for a convex polyhedron structure. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#include + +// Primary Header +#include "SHConvexPolyhedron.h" + +// Helper Macros + +#define BUILD_UINT64_FROM_UINT32S(a, b) (uint64_t)a << 32 | b + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Constructors & Destructor Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHConvexPolyhedron::HalfEdge::HalfEdge() noexcept + : tailVertexIndex { -1 } + , headVertexIndex { -1 } + , edgeIndex { -1 } + , twinEdgeIndex { -1 } + , faceIndex { -1 } + {} + + SHConvexPolyhedron::HalfEdge::HalfEdge(const HalfEdge& rhs) noexcept + : tailVertexIndex { rhs.tailVertexIndex } + , headVertexIndex { rhs.headVertexIndex } + , edgeIndex { rhs.edgeIndex } + , twinEdgeIndex { rhs.twinEdgeIndex } + , faceIndex { rhs.faceIndex } + {} + + SHConvexPolyhedron::HalfEdge::HalfEdge(HalfEdge&& rhs) noexcept + : tailVertexIndex { rhs.tailVertexIndex } + , headVertexIndex { rhs.headVertexIndex } + , edgeIndex { rhs.edgeIndex } + , twinEdgeIndex { rhs.twinEdgeIndex } + , faceIndex { rhs.faceIndex } + {} + + SHConvexPolyhedron::Face::Face(const Face& rhs) noexcept + : normal { rhs.normal } + { + std::ranges::copy(rhs.vertexIndices.begin(), rhs.vertexIndices.end(), std::back_inserter(vertexIndices)); + } + + SHConvexPolyhedron::Face::Face(Face&& rhs) noexcept + : normal { rhs.normal } + { + std::ranges::copy(rhs.vertexIndices.begin(), rhs.vertexIndices.end(), std::back_inserter(vertexIndices)); + } + + /*-----------------------------------------------------------------------------------*/ + /* Operator Overload Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHConvexPolyhedron::HalfEdge& SHConvexPolyhedron::HalfEdge::operator=(const HalfEdge& rhs) noexcept + { + if (this == &rhs) + return *this; + + tailVertexIndex = rhs.tailVertexIndex; + headVertexIndex = rhs.headVertexIndex; + edgeIndex = rhs.edgeIndex ; + twinEdgeIndex = rhs.twinEdgeIndex ; + faceIndex = rhs.faceIndex ; + + return *this; + } + + SHConvexPolyhedron::HalfEdge& SHConvexPolyhedron::HalfEdge::operator=(HalfEdge&& rhs) noexcept + { + tailVertexIndex = rhs.tailVertexIndex; + headVertexIndex = rhs.headVertexIndex; + edgeIndex = rhs.edgeIndex ; + twinEdgeIndex = rhs.twinEdgeIndex ; + faceIndex = rhs.faceIndex ; + + return *this; + } + + SHConvexPolyhedron::Face& SHConvexPolyhedron::Face::operator=(const Face& rhs) noexcept + { + if (this == &rhs) + return *this; + + normal = rhs.normal; + + vertexIndices.clear(); + std::ranges::copy(rhs.vertexIndices.begin(), rhs.vertexIndices.end(), std::back_inserter(vertexIndices)); + + return *this; + } + + SHConvexPolyhedron::Face& SHConvexPolyhedron::Face::operator=(Face&& rhs) noexcept + { + normal = rhs.normal; + + vertexIndices.clear(); + std::ranges::copy(rhs.vertexIndices.begin(), rhs.vertexIndices.end(), std::back_inserter(vertexIndices)); + + return *this; + } + + /*-----------------------------------------------------------------------------------*/ + /* Getter Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + int32_t SHConvexPolyhedron::GetFaceCount() const noexcept + { + return static_cast(faces.size()); + } + + int32_t SHConvexPolyhedron::GetHalfEdgeCount() const noexcept + { + return static_cast(halfEdges.size()); + } + + const SHConvexPolyhedron::Face& SHConvexPolyhedron::GetFace(int32_t index) const + { + if (index < 0 || index >= static_cast(faces.size())) + throw std::invalid_argument("Index out-of-range!"); + + return faces[index]; + } + + const SHConvexPolyhedron::HalfEdge& SHConvexPolyhedron::GetHalfEdge(int32_t index) const + { + if (index < 0 || index >= static_cast(halfEdges.size())) + throw std::invalid_argument("Index out-of-range!"); + + return halfEdges[index]; + } + + /*-----------------------------------------------------------------------------------*/ + /* Public Member Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHConvexPolyhedron::AddFace(const Face& face) + { + faces.emplace_back(face); + } + + void SHConvexPolyhedron::BuildPolyhedron() noexcept + { + // We use the pair of vertex IDs on a half-edge to prevent duplicates + std::unordered_map edgeMap; + edgeMap.clear(); + + if (faces.empty()) + { + SHLOGV_CRITICAL("Unable to build convex polyhedron, no faces have been added!") + return; + } + + // For each face, build half edges + for (size_t i = 0; i < faces.size(); ++i) + { + const Face& FACE = faces[i]; + + // Iterate through vertices and build half-edges + for (size_t j = 0; j < FACE.vertexIndices.size(); ++j) + { + const int32_t TAIL = FACE.vertexIndices[j]; + const int32_t HEAD = (TAIL + 1) > FACE.vertexIndices.back() ? FACE.vertexIndices.front() : TAIL + 1; // Wrap around + + const uint64_t NEW_EDGE_ID = BUILD_UINT64_FROM_UINT32S(TAIL, HEAD); + const uint64_t TWIN_EDGE_ID = BUILD_UINT64_FROM_UINT32S(HEAD, TAIL); + + // Check if the half-edge has already been inserted + auto newEdgeIter = edgeMap.find(NEW_EDGE_ID); + if (newEdgeIter == edgeMap.end()) + { + // Reuse the iterator for mapping with the twin + newEdgeIter = edgeMap.emplace(NEW_EDGE_ID, HalfEdge{}).first; + + HalfEdge& newHalfEdge = newEdgeIter->second; + newHalfEdge.tailVertexIndex = TAIL; + newHalfEdge.headVertexIndex = HEAD; + newHalfEdge.faceIndex = static_cast(i); + + // Set edge index of the newly inserted edge as the size of the map - 1 + // Since it is an unordered map, it will just be at the back + newHalfEdge.edgeIndex = static_cast(edgeMap.size()) - 1; + } + + // Find twin edge if one exists + auto twinEdgeIter = edgeMap.find(TWIN_EDGE_ID); + if (twinEdgeIter != edgeMap.end()) + { + // Set the twin index of both the edges + HalfEdge& newHalfEdge = newEdgeIter->second; + HalfEdge& twinHalfEdge = twinEdgeIter->second; + + newHalfEdge.twinEdgeIndex = twinHalfEdge.edgeIndex; + twinHalfEdge.twinEdgeIndex = newHalfEdge.edgeIndex; + } + } + } + + // Copy all half edges into the vector + // At this point, no duplicates should be in the map and all edges should be linked. + for (auto& halfEdge : edgeMap | std::views::values) + halfEdges.emplace_back(halfEdge); + } + +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedron.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedron.h new file mode 100644 index 00000000..762368a3 --- /dev/null +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedron.h @@ -0,0 +1,144 @@ +/**************************************************************************************** + * \file SHConvexPolyhedron.h + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Interface for a convex polyhedron structure. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#pragma once + +#include + +// Project Headers +#include "Math/Vector/SHVec3.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------------*/ + + /** + * @brief + * Encapsulates data for a convex polyhedron's geometry represented as faces & half edges. + */ + class SH_API SHConvexPolyhedron + { + public: + /*---------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*---------------------------------------------------------------------------------*/ + + struct HalfEdge + { + /*-------------------------------------------------------------------------------*/ + /* Data Members */ + /*-------------------------------------------------------------------------------*/ + + //Head and tail forms the edge. + //Head <----- Tail + int32_t tailVertexIndex; + + // Head is also tail of the next edge. + int32_t headVertexIndex; + + int32_t edgeIndex; + // Other half of the edge on a different face. + // Important for extrapolating face normals. + int32_t twinEdgeIndex; + + // Adjacent face of this edge. + int32_t faceIndex; + + /*-------------------------------------------------------------------------------*/ + /* Constructors & Destructor */ + /*-------------------------------------------------------------------------------*/ + + HalfEdge () noexcept; + HalfEdge (const HalfEdge& rhs) noexcept; + HalfEdge (HalfEdge&& rhs) noexcept; + ~HalfEdge () noexcept = default; + + /*-------------------------------------------------------------------------------*/ + /* Operator Overloads */ + /*-------------------------------------------------------------------------------*/ + + HalfEdge& operator= (const HalfEdge& rhs) noexcept; + HalfEdge& operator= (HalfEdge&& rhs) noexcept; + }; + + struct Face + { + public: + /*-------------------------------------------------------------------------------*/ + /* Data Members */ + /*-------------------------------------------------------------------------------*/ + + SHVec3 normal; + std::vector vertexIndices; // Must be in CCW order + + /*-------------------------------------------------------------------------------*/ + /* Constructors & Destructor */ + /*-------------------------------------------------------------------------------*/ + + Face () noexcept = default; + Face (const Face& rhs) noexcept; + Face (Face&& rhs) noexcept; + ~Face () noexcept = default; + + /*-------------------------------------------------------------------------------*/ + /* Operator Overloads */ + /*-------------------------------------------------------------------------------*/ + + Face& operator= (const Face& rhs) noexcept; + Face& operator= (Face&& rhs) noexcept; + }; + + /*---------------------------------------------------------------------------------*/ + /* Getter Functions */ + /*---------------------------------------------------------------------------------*/ + + [[nodiscard]] int32_t GetFaceCount () const noexcept; + [[nodiscard]] int32_t GetHalfEdgeCount () const noexcept; + + [[nodiscard]] const Face& GetFace (int32_t index) const; + [[nodiscard]] const HalfEdge& GetHalfEdge (int32_t index) const; + + /*---------------------------------------------------------------------------------*/ + /* Member Functions */ + /*---------------------------------------------------------------------------------*/ + + /** + * @brief + * Adds a face to the polyhedron. The face must be constructed outside the polyhedron. + * @param face + * The face to insert. + */ + void AddFace (const Face& face); + + /** + * @brief + * Builds the half-edges of the polyhedron using the faces.
+ * Before this method is invoked, there must be some faces. + * @return + */ + void BuildPolyhedron() noexcept; + + private: + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + + float radius = 0.2f; // Default Radius is 2 cm + + // Store the faces and half-edges + + std::vector faces; + std::vector halfEdges; + + }; + +} // namespace SHADE