Implemented a custom physics engine #316

Merged
direnbharwani merged 95 commits from SHPhysics into main 2023-01-23 15:55:45 +08:00
23 changed files with 693 additions and 168 deletions
Showing only changes of commit 38b1c46d1f - Show all commits

View File

@ -9,10 +9,10 @@
Scale: {x: 1, y: 1, z: 1} Scale: {x: 1, y: 1, z: 1}
IsActive: true IsActive: true
RigidBody Component: RigidBody Component:
Type: Static Type: Dynamic
Auto Mass: false Auto Mass: false
Mass: 1 Mass: 1
Drag: 0 Drag: 1
Angular Drag: 0 Angular Drag: 0
Use Gravity: true Use Gravity: true
Gravity Scale: 1 Gravity Scale: 1
@ -26,3 +26,20 @@
Freeze Rotation Z: false Freeze Rotation Z: false
IsActive: true IsActive: true
Scripts: ~ Scripts: ~
- EID: 1
Name: Default
IsActive: true
NumberOfChildren: 0
Components:
Camera Component:
Position: {x: 0, y: 2, z: 5}
Pitch: 0
Yaw: 0
Roll: 0
Width: 1920
Height: 1080
Near: 0.00999999978
Far: 10000
Perspective: true
IsActive: true
Scripts: ~

View File

@ -162,7 +162,7 @@ namespace SHADE
//SHSceneNode* parentNode = entityVec[eIndex]->GetSceneNode()->GetParent(); //SHSceneNode* parentNode = entityVec[eIndex]->GetSceneNode()->GetParent();
//SHSceneGraph::RemoveChild(parentNode,entityVec[eIndex].get()); //SHSceneGraph::removeChild(parentNode,entityVec[eIndex].get());
//TODO remove from parent and recursively delete child. //TODO remove from parent and recursively delete child.

View File

@ -252,12 +252,13 @@ namespace SHADE
if(rbType == SHRigidBodyComponent::Type::DYNAMIC) //Dynamic only fields if(rbType == SHRigidBodyComponent::Type::DYNAMIC) //Dynamic only fields
{ {
SHEditorWidgets::CheckBox("Use Gravity", [component]{return component->IsGravityEnabled();}, [component](bool const& value){component->SetIsGravityEnabled(value);}, "Gravity"); SHEditorWidgets::CheckBox("Use Gravity", [component]{return component->IsGravityEnabled();}, [component](bool const& value){component->SetIsGravityEnabled(value);}, "Gravity");
//SHEditorWidgets::DragFloat("Mass", [component] {return component->GetMass(); }, [component](float const& value) {component->SetMass(value); }, "Mass"); SHEditorWidgets::DragFloat("Gravity Scale", [component] { return component->GetGravityScale(); }, [component](float const& value) { component->SetGravityScale(value); }, "Gravity Scale", 0.1f, 0.0f);
SHEditorWidgets::DragFloat("Mass", [component] {return component->GetMass(); }, [component](float const& value) {component->SetMass(value); }, "Mass");
} }
if (rbType == SHRigidBodyComponent::Type::DYNAMIC || rbType == SHRigidBodyComponent::Type::KINEMATIC) //Dynamic or Kinematic only fields if (rbType == SHRigidBodyComponent::Type::DYNAMIC || rbType == SHRigidBodyComponent::Type::KINEMATIC) //Dynamic or Kinematic only fields
{ {
SHEditorWidgets::DragFloat("Drag", [component] {return component->GetDrag(); }, [component](float const& value) {component->SetDrag(value); }, "Drag"); SHEditorWidgets::DragFloat("Drag", [component] {return component->GetDrag(); }, [component](float const& value) {component->SetDrag(value); }, "Drag", 0.1f, 0.0f);
SHEditorWidgets::DragFloat("Angular Drag", [component] {return component->GetAngularDrag(); }, [component](float const& value) {component->SetAngularDrag(value); }, "Angular Drag"); SHEditorWidgets::DragFloat("Angular Drag", [component] {return component->GetAngularDrag(); }, [component](float const& value) {component->SetAngularDrag(value); }, "Angular Drag", 0.1f, 0.0f);
SHEditorWidgets::CheckBox("Interpolate", [component] {return component->IsInterpolating(); }, [component](bool const& value) {component->SetInterpolate(value); }, "Interpolate"); SHEditorWidgets::CheckBox("Interpolate", [component] {return component->IsInterpolating(); }, [component](bool const& value) {component->SetInterpolate(value); }, "Interpolate");
@ -284,8 +285,7 @@ namespace SHADE
//Debug Info (Read-Only) //Debug Info (Read-Only)
if(ImGui::CollapsingHeader("Debug Information", ImGuiTreeNodeFlags_DefaultOpen))//Dynamic or Kinematic only fields if(ImGui::CollapsingHeader("Debug Information", ImGuiTreeNodeFlags_DefaultOpen))//Dynamic or Kinematic only fields
{ {
SHEditorWidgets::DragFloat("Mass", [component] { return component->GetMass(); }, [](float value){}, "Mass", 0.1f, 0.0f, std::numeric_limits<float>::infinity(), "%.3f", ImGuiSliderFlags_ReadOnly); SHEditorWidgets::DragVec3("Position", { "X", "Y", "Z" }, [component] {return component->GetPosition(); }, [](SHVec3 const& value) {}, false, "Position", 0.1f, "%.3f", 0.0f, 0.0f, ImGuiSliderFlags_ReadOnly);
//SHEditorWidgets::DragVec3("Position", { "X", "Y", "Z" }, [component] {return component->GetPosition(); }, [](SHVec3 const& value) {}, false, "Position", 0.1f, "%.3f", 0.0f, 0.0f, ImGuiSliderFlags_ReadOnly);
//SHEditorWidgets::DragVec3("Rotation", { "X", "Y", "Z" }, [component] {return component->GetRotation(); }, [](SHVec3 const& value) {}, false, "Rotation", 0.1f, "%.3f", 0.0f, 0.0f, ImGuiSliderFlags_ReadOnly); //SHEditorWidgets::DragVec3("Rotation", { "X", "Y", "Z" }, [component] {return component->GetRotation(); }, [](SHVec3 const& value) {}, false, "Rotation", 0.1f, "%.3f", 0.0f, 0.0f, ImGuiSliderFlags_ReadOnly);
if (rbType == SHRigidBodyComponent::Type::DYNAMIC || rbType == SHRigidBodyComponent::Type::KINEMATIC) //Dynamic or Kinematic only fields if (rbType == SHRigidBodyComponent::Type::DYNAMIC || rbType == SHRigidBodyComponent::Type::KINEMATIC) //Dynamic or Kinematic only fields
{ {

View File

@ -246,8 +246,6 @@ namespace SHADE
tf.world.position = SHVec3::Transform(tf.local.position, localToWorld); tf.world.position = SHVec3::Transform(tf.local.position, localToWorld);
tf.world.scale = tf.local.scale * (parent ? parent->GetLocalScale() : SHVec3::One); tf.world.scale = tf.local.scale * (parent ? parent->GetLocalScale() : SHVec3::One);
if (convertRotation) if (convertRotation)
{ {
tf.worldRotation = tf.localRotation + (parent ? parent->GetLocalRotation() : SHVec3::Zero); tf.worldRotation = tf.localRotation + (parent ? parent->GetLocalRotation() : SHVec3::Zero);

View File

@ -0,0 +1,100 @@
/****************************************************************************************
* \file SHMotionState.cpp
* \author Diren D Bharwani, diren.dbharwani, 390002520
* \brief Implementation for a Motion State.
*
* \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 <SHpch.h>
// Primary Header
#include "SHMotionState.h"
// Project Headers
#include "Math/SHMathHelpers.h"
namespace SHADE
{
/*-----------------------------------------------------------------------------------*/
/* Constructors & Destructor Definitions */
/*-----------------------------------------------------------------------------------*/
SHMotionState::SHMotionState() noexcept
: hasMoved { false }
{}
SHMotionState::SHMotionState(const SHMotionState& rhs) noexcept
: hasMoved { rhs.hasMoved }
, position { rhs.position }
, prevPosition { rhs.prevPosition }
{}
SHMotionState::SHMotionState(SHMotionState&& rhs) noexcept
: hasMoved { rhs.hasMoved }
, position { rhs.position }
, prevPosition { rhs.prevPosition }
{}
/*-----------------------------------------------------------------------------------*/
/* Operator Overload Definitions */
/*-----------------------------------------------------------------------------------*/
SHMotionState& SHMotionState::operator=(const SHMotionState& rhs) noexcept
{
if (this == &rhs)
return *this;
hasMoved = rhs.hasMoved;
position = rhs.position;
prevPosition = rhs.prevPosition;
return *this;
}
SHMotionState& SHMotionState::operator=(SHMotionState&& rhs) noexcept
{
hasMoved = rhs.hasMoved;
position = rhs.position;
prevPosition = rhs.prevPosition;
return *this;
}
SHMotionState::operator bool() const noexcept
{
return hasMoved;
}
/*-----------------------------------------------------------------------------------*/
/* Public Member Function Definition */
/*-----------------------------------------------------------------------------------*/
void SHMotionState::ForcePosition(const SHVec3& newPosition) noexcept
{
hasMoved = false;
prevPosition = newPosition;
position = newPosition;
}
void SHMotionState::IntegratePosition(const SHVec3& velocity, float dt) noexcept
{
// Velocities are 0 when objects are static or sleeping. We do not want to integrate them here.
// This call should never reach here.
hasMoved = true;
prevPosition = position;
position += velocity * dt;
}
SHVec3 SHMotionState::InterpolatePositions(float factor) const noexcept
{
return SHVec3::ClampedLerp(prevPosition, position, factor);
}
} // namespace SHADE

View File

@ -0,0 +1,93 @@
/****************************************************************************************
* \file SHMotionState.h
* \author Diren D Bharwani, diren.dbharwani, 390002520
* \brief Interface for a Motion State.
*
* \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
// Project Headers
#include "Math/Vector/SHVec3.h"
namespace SHADE
{
/*-------------------------------------------------------------------------------------*/
/* Type Definitions */
/*-------------------------------------------------------------------------------------*/
/**
* @brief
* Encapsulates the motion state of a rigid body in physics.
*/
struct SH_API SHMotionState
{
public:
/*-----------------------------------------------------------------------------------*/
/* Data Members */
/*-----------------------------------------------------------------------------------*/
bool hasMoved;
SHVec3 position;
SHVec3 prevPosition;
/*-----------------------------------------------------------------------------------*/
/* Constructors & Destructor */
/*-----------------------------------------------------------------------------------*/
SHMotionState () noexcept;
SHMotionState (const SHMotionState& rhs) noexcept;
SHMotionState (SHMotionState&& rhs) noexcept;
~SHMotionState() = default;
/*-----------------------------------------------------------------------------------*/
/* Operator Overloads */
/*-----------------------------------------------------------------------------------*/
SHMotionState& operator= (const SHMotionState& rhs) noexcept;
SHMotionState& operator= (SHMotionState&& rhs) noexcept;
operator bool () const noexcept;
/*-----------------------------------------------------------------------------------*/
/* Function Members */
/*-----------------------------------------------------------------------------------*/
/**
* @brief
* Forcefully sets the position. Meant to be used when transform overrides the rigid body
* positions.
* @param newPosition
* The new position to set.
*/
void ForcePosition (const SHVec3& newPosition) noexcept;
/**
* @brief
* Integrates the positions using velocity with respect to time.
* @param velocity
* The velocity to integrate.
* @param dt
* The delta time to integrate with respect to.
*/
void IntegratePosition (const SHVec3& velocity, float dt) noexcept;
/**
* @brief
* Interpolates the position between the previous and the last using a given factor.
* @param factor
* The factor to interpolate by. Should be between 0 & 1.
* @returns
* The interpolated position meant for rendering.
*/
SHVec3 InterpolatePositions (float factor) const noexcept;
};
} // namespace SHADE

View File

@ -0,0 +1,122 @@
/****************************************************************************************
* \file SHPhysicsWorld.cpp
* \author Diren D Bharwani, diren.dbharwani, 390002520
* \brief Implementation for a Physics World.
*
* \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 <SHpch.h>
// Primary Header
#include "SHPhysicsWorld.h"
// Project Headers
namespace SHADE
{
/*-----------------------------------------------------------------------------------*/
/* Constructors & Destructor Definitions */
/*-----------------------------------------------------------------------------------*/
SHPhysicsWorld::SHPhysicsWorld(const WorldSettings& worldSettings) noexcept
: settings { worldSettings }
{
rigidBodies.clear();
SHLOG_INFO_D("Creating Physics World")
}
SHPhysicsWorld::~SHPhysicsWorld() noexcept
{
rigidBodies.clear();
}
/*-----------------------------------------------------------------------------------*/
/* Public Member Functions Definitions */
/*-----------------------------------------------------------------------------------*/
void SHPhysicsWorld::AddRigidBody(SHRigidBody* rigidBody) noexcept
{
const bool INSERTED = rigidBodies.emplace(rigidBody->entityID, rigidBody).second;
if (!INSERTED)
{
SHLOG_WARNING_D("Attempting to add duplicate rigid body {} to the Physics World!", rigidBody->entityID)
}
}
void SHPhysicsWorld::RemoveRigidBody(SHRigidBody* rigidBody) noexcept
{
rigidBodies.erase(rigidBody->entityID);
}
void SHPhysicsWorld::Step(float dt)
{
for (auto* rigidBody : rigidBodies | std::views::values)
integrateForces(*rigidBody, dt);
for (auto* rigidBody : rigidBodies | std::views::values)
integrateVelocities(*rigidBody, dt);
}
/*-----------------------------------------------------------------------------------*/
/* Private Member Functions Definitions */
/*-----------------------------------------------------------------------------------*/
void SHPhysicsWorld::integrateForces(SHRigidBody& rigidBody, float dt) const noexcept
{
if (rigidBody.bodyType != SHRigidBody::Type::DYNAMIC)
return;
// Integrate forces and gravity into linear velocity
const SHVec3 LINEAR_ACCELERATION = rigidBody.accumulatedForce * rigidBody.invMass;
const SHVec3 GRAVITATIONAL_ACCELERATION = rigidBody.IsGravityEnabled() ? settings.gravity * rigidBody.gravityScale : SHVec3::Zero;
rigidBody.linearVelocity += (LINEAR_ACCELERATION + GRAVITATIONAL_ACCELERATION) * dt;
// Apply drag (exponentially applied)
rigidBody.linearVelocity *= 1.0f / (1.0f + dt * rigidBody.linearDrag);
}
void SHPhysicsWorld::integrateVelocities(SHRigidBody& rigidBody, float dt) const noexcept
{
static const auto ENFORCE_CONSTRAINED_VELOCITIES = [](SHRigidBody& rigidBody)
{
// Enforce linear constraints
rigidBody.linearVelocity = SHVec3
{
rigidBody.GetFreezePositionX() ? 0.0f : rigidBody.linearVelocity.x
, rigidBody.GetFreezePositionY() ? 0.0f : rigidBody.linearVelocity.y
, rigidBody.GetFreezePositionZ() ? 0.0f : rigidBody.linearVelocity.z
};
// TODO: Enforce angular constraints
};
// Always reset movement flag
rigidBody.motionState.hasMoved = false;
// Set all velocities of static bodies to 0
if (rigidBody.bodyType == SHRigidBody::Type::STATIC)
{
rigidBody.linearVelocity = SHVec3::Zero;
}
else // Dynamic & Kinematic bodies
{
// Both dynamic and kinematic can sleep when their velocities are under the thresholds.
if (!rigidBody.IsSleeping())
{
ENFORCE_CONSTRAINED_VELOCITIES(rigidBody);
rigidBody.motionState.IntegratePosition(rigidBody.linearVelocity, dt);
// TODO: Integrate orientations
}
}
// Clear all forces
// We clear forces for static bodies as well for redundancy
rigidBody.ClearForces();
}
} // namespace SHADE

View File

@ -10,7 +10,10 @@
#pragma once #pragma once
#include <unordered_map>
// Project Headers // Project Headers
#include "SHRigidBody.h"
#include "Math/SHMath.h" #include "Math/SHMath.h"
#include "SH_API.h" #include "SH_API.h"
@ -20,7 +23,7 @@ namespace SHADE
/* Type Definitions */ /* Type Definitions */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
struct SH_API SHPhysicsWorldState class SH_API SHPhysicsWorld
{ {
public: public:
@ -41,22 +44,55 @@ namespace SHADE
bool sleepingEnabled = true; bool sleepingEnabled = true;
}; };
/*---------------------------------------------------------------------------------*/
/* Data Members */
/*---------------------------------------------------------------------------------*/
WorldSettings settings;
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* Constructors & Destructor */ /* Constructors & Destructor */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
SHPhysicsWorldState() noexcept; SHPhysicsWorld (const WorldSettings& worldSettings = WorldSettings{}) noexcept;
~SHPhysicsWorld () noexcept;
SHPhysicsWorld (const SHPhysicsWorld&) = delete;
SHPhysicsWorld (SHPhysicsWorld&&) = delete;
/*---------------------------------------------------------------------------------*/
/* Operator Overloads */
/*---------------------------------------------------------------------------------*/
SHPhysicsWorld& operator=(const SHPhysicsWorld&) = delete;
SHPhysicsWorld& operator=(SHPhysicsWorld&&) = delete;
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* Function Members */ /* Function Members */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
void AddRigidBody (SHRigidBody* rigidBody) noexcept;
void RemoveRigidBody (SHRigidBody* rigidBody) noexcept;
void Step (float dt);
private:
/*---------------------------------------------------------------------------------*/
/* Type Definitions */
/*---------------------------------------------------------------------------------*/
using RigidBodies = std::unordered_map<EntityID, SHRigidBody*>;
/*---------------------------------------------------------------------------------*/
/* Data Members */
/*---------------------------------------------------------------------------------*/
WorldSettings settings;
RigidBodies rigidBodies;
/*---------------------------------------------------------------------------------*/
/* Function Members */
/*---------------------------------------------------------------------------------*/
void integrateForces (SHRigidBody& rigidBody, float dt) const noexcept;
void integrateVelocities (SHRigidBody& rigidBody, float dt) const noexcept;
}; };

View File

@ -26,7 +26,7 @@ namespace SHADE
: entityID { eid } : entityID { eid }
, bodyType { type } , bodyType { type }
, invMass { type == Type::DYNAMIC ? 1.0f : 0.0f } , invMass { type == Type::DYNAMIC ? 1.0f : 0.0f }
, linearDrag { type != Type::STATIC ? 0.01f : 0.0f } , linearDrag { 0.01f }
, gravityScale { 1.0f } , gravityScale { 1.0f }
, flags { 0U } , flags { 0U }
{ {
@ -45,6 +45,7 @@ namespace SHADE
, linearDrag { rhs.linearDrag } , linearDrag { rhs.linearDrag }
, gravityScale { rhs.gravityScale } , gravityScale { rhs.gravityScale }
, flags { rhs.flags } , flags { rhs.flags }
, motionState { rhs.motionState }
{ {
// All other properties are defaulted to 0 to not carry over any potential errors / invalid values. // All other properties are defaulted to 0 to not carry over any potential errors / invalid values.
} }
@ -56,6 +57,7 @@ namespace SHADE
, linearDrag { rhs.linearDrag } , linearDrag { rhs.linearDrag }
, gravityScale { rhs.gravityScale } , gravityScale { rhs.gravityScale }
, flags { rhs.flags } , flags { rhs.flags }
, motionState { std::move(rhs.motionState) }
{ {
// All other properties are defaulted to 0 to not carry over any potential errors / invalid values. // All other properties are defaulted to 0 to not carry over any potential errors / invalid values.
} }
@ -76,8 +78,12 @@ namespace SHADE
linearDrag = rhs.linearDrag; linearDrag = rhs.linearDrag;
gravityScale = rhs.gravityScale; gravityScale = rhs.gravityScale;
flags = rhs.flags; flags = rhs.flags;
motionState = rhs.motionState;
// All other properties are defaulted to 0 to not carry over any potential errors / invalid values. // All other properties are defaulted to 0 to not carry over any potential errors / invalid values.
accumulatedForce = SHVec3::Zero;
linearVelocity = SHVec3::Zero;
angularVelocity = SHVec3::Zero;
return *this; return *this;
} }
@ -90,8 +96,12 @@ namespace SHADE
linearDrag = rhs.linearDrag; linearDrag = rhs.linearDrag;
gravityScale = rhs.gravityScale; gravityScale = rhs.gravityScale;
flags = rhs.flags; flags = rhs.flags;
motionState = std::move(rhs.motionState);
// All other properties are defaulted to 0 to not carry over any potential errors / invalid values. // All other properties are defaulted to 0 to not carry over any potential errors / invalid values.
accumulatedForce = SHVec3::Zero;
linearVelocity = SHVec3::Zero;
angularVelocity = SHVec3::Zero;
return *this; return *this;
} }
@ -203,6 +213,12 @@ namespace SHADE
return flags & (1U << FLAG_POS); return flags & (1U << FLAG_POS);
} }
SHMotionState& SHRigidBody::GetMotionState() noexcept
{
return motionState;
}
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
/* Setter Functions Definitions */ /* Setter Functions Definitions */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
@ -424,7 +440,7 @@ namespace SHADE
} }
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
/* Member Function Definitions */ /* Public Member Function Definition */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
void SHRigidBody::AddForce(const SHVec3& force, const SHVec3& pos) noexcept void SHRigidBody::AddForce(const SHVec3& force, const SHVec3& pos) noexcept

View File

@ -13,7 +13,7 @@
// Project Headers // Project Headers
#include "ECS_Base/SHECSMacros.h" #include "ECS_Base/SHECSMacros.h"
#include "Math/Vector/SHVec3.h" #include "Math/Vector/SHVec3.h"
#include "SH_API.h" #include "SHMotionState.h"
namespace SHADE namespace SHADE
{ {
@ -27,6 +27,13 @@ namespace SHADE
*/ */
class SH_API SHRigidBody class SH_API SHRigidBody
{ {
private:
/*-----------------------------------------------------------------------------------*/
/* Friends */
/*-----------------------------------------------------------------------------------*/
friend class SHPhysicsWorld;
public: public:
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
/* Type Definitions */ /* Type Definitions */
@ -83,13 +90,16 @@ namespace SHADE
[[nodiscard]] bool GetFreezeRotationY () const noexcept; [[nodiscard]] bool GetFreezeRotationY () const noexcept;
[[nodiscard]] bool GetFreezeRotationZ () const noexcept; [[nodiscard]] bool GetFreezeRotationZ () const noexcept;
[[nodiscard]] SHMotionState& GetMotionState () noexcept;
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
/* Setter Functions */ /* Setter Functions */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
/** /**
* @brief * @brief
* Changing the type from non-Dynamic to Dynamic will set the default mass. * Changing the type from non-Dynamic to Dynamic will set the default
* mass and drag values.
*/ */
void SetType (Type newType) noexcept; void SetType (Type newType) noexcept;
@ -111,7 +121,6 @@ namespace SHADE
void SetGravityScale (float newGravityScale) noexcept; void SetGravityScale (float newGravityScale) noexcept;
void SetLinearVelocity (const SHVec3& newLinearVelocity) noexcept; void SetLinearVelocity (const SHVec3& newLinearVelocity) noexcept;
// Flags // Flags
@ -180,6 +189,8 @@ namespace SHADE
// aZ aY aX pZ pY pX 0 0 0 0 0 autoMass enableGravity enableSleeping sleeping active // aZ aY aX pZ pY pX 0 0 0 0 0 autoMass enableGravity enableSleeping sleeping active
uint16_t flags; uint16_t flags;
SHMotionState motionState;
}; };
} // namespace SHADE } // namespace SHADE

View File

@ -44,7 +44,6 @@ namespace SHADE
delete rigidBody; delete rigidBody;
} }
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
/* Operator Overload Definitions */ /* Operator Overload Definitions */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
@ -71,4 +70,14 @@ namespace SHADE
return *this; return *this;
} }
/*-----------------------------------------------------------------------------------*/
/* Public Member Function Definitions */
/*-----------------------------------------------------------------------------------*/
bool SHPhysicsObject::IsEmpty() const noexcept
{
return rigidBody == nullptr;
}
} // namespace SHADE } // namespace SHADE

View File

@ -48,5 +48,11 @@ namespace SHADE
SHPhysicsObject& operator=(const SHPhysicsObject& rhs) noexcept; SHPhysicsObject& operator=(const SHPhysicsObject& rhs) noexcept;
SHPhysicsObject& operator=(SHPhysicsObject&& rhs) noexcept; SHPhysicsObject& operator=(SHPhysicsObject&& rhs) noexcept;
/*-----------------------------------------------------------------------------------*/
/* Member Functions */
/*-----------------------------------------------------------------------------------*/
bool IsEmpty() const noexcept;
}; };
} // namespace SHADE } // namespace SHADE

View File

@ -14,6 +14,7 @@
#include "SHPhysicsObjectManager.h" #include "SHPhysicsObjectManager.h"
// Project Headers // Project Headers
#include "Math/Transform/SHTransformComponent.h"
#include "Physics/Interface/SHColliderComponent.h" #include "Physics/Interface/SHColliderComponent.h"
#include "Physics/Interface/SHCollisionShape.h" #include "Physics/Interface/SHCollisionShape.h"
#include "Physics/Interface/RigidBodyComponent/SHRigidBodyComponent.h" #include "Physics/Interface/RigidBodyComponent/SHRigidBodyComponent.h"
@ -21,6 +22,15 @@
namespace SHADE namespace SHADE
{ {
/*-----------------------------------------------------------------------------------*/
/* Constructors & Destructor Definitions */
/*-----------------------------------------------------------------------------------*/
SHPhysicsObjectManager::~SHPhysicsObjectManager() noexcept
{
RemoveAllObjects();
}
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
/* Getter Function Definitions */ /* Getter Function Definitions */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
@ -68,6 +78,17 @@ namespace SHADE
, static_cast<SHRigidBody::Type>(rigidBodyComponent->GetType()) , static_cast<SHRigidBody::Type>(rigidBodyComponent->GetType())
}; };
SHMotionState& motionState = physicsObject->rigidBody->GetMotionState();
if (const auto* TRANSFORM_COMPONENT = SHComponentManager::GetComponent<SHTransformComponent>(entityID); TRANSFORM_COMPONENT)
{
motionState.ForcePosition(TRANSFORM_COMPONENT->GetWorldPosition());
}
else
{
motionState.ForcePosition(SHVec3::Zero);
}
// Link with the component // Link with the component
rigidBodyComponent->SetRigidBody(physicsObject->rigidBody); rigidBodyComponent->SetRigidBody(physicsObject->rigidBody);
@ -85,7 +106,7 @@ namespace SHADE
physicsObject->rigidBody = nullptr; physicsObject->rigidBody = nullptr;
// Destroy empty physics objects // Destroy empty physics objects
if (physicsObject->rigidBody == nullptr) if (physicsObject->IsEmpty())
destroyPhysicsObject(entityID); destroyPhysicsObject(entityID);
} }
@ -101,7 +122,15 @@ namespace SHADE
void SHPhysicsObjectManager::RemoveCollider(EntityID entityID) noexcept void SHPhysicsObjectManager::RemoveCollider(EntityID entityID) noexcept
{ {
// Unlink with the component const auto PHYSICS_OBJECT_ITERATOR = physicsObjects.find(entityID);
if (PHYSICS_OBJECT_ITERATOR != physicsObjects.end())
{
SHPhysicsObject* physicsObject = &PHYSICS_OBJECT_ITERATOR->second;
// Destroy empty physics objects
if (physicsObject->IsEmpty())
destroyPhysicsObject(entityID);
}
// TODO: Broadcast event // TODO: Broadcast event
} }
@ -120,6 +149,11 @@ namespace SHADE
// TODO: Broadcast event // TODO: Broadcast event
} }
void SHPhysicsObjectManager::RemoveAllObjects() noexcept
{
physicsObjects.clear();
}
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
/* Private Member Function Definitions */ /* Private Member Function Definitions */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/

View File

@ -41,6 +41,7 @@ namespace SHADE
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
SHPhysicsObjectManager () noexcept = default; SHPhysicsObjectManager () noexcept = default;
~SHPhysicsObjectManager () noexcept;
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
/* Getter Functions */ /* Getter Functions */
@ -107,6 +108,13 @@ namespace SHADE
*/ */
void RemoveCollisionShape (EntityID entityID, uint8_t shapeID) noexcept; void RemoveCollisionShape (EntityID entityID, uint8_t shapeID) noexcept;
/**
* @brief
* Removes all physics object in the manager. This is only meant to be called when
* the world is being destroyed or the scene is being changed.
*/
void RemoveAllObjects () noexcept;
private: private:
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
/* Data Members */ /* Data Members */

View File

@ -182,6 +182,15 @@ namespace SHADE
return SHVec3::Zero; return SHVec3::Zero;
} }
SHVec3 SHRigidBodyComponent::GetPosition() const noexcept
{
if (rigidBody)
return rigidBody->GetMotionState().position;
return SHVec3::Zero;
}
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
/* Setter Functions Definitions */ /* Setter Functions Definitions */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/

View File

@ -97,6 +97,8 @@ namespace SHADE
[[nodiscard]] SHVec3 GetLinearVelocity () const noexcept; [[nodiscard]] SHVec3 GetLinearVelocity () const noexcept;
[[nodiscard]] SHVec3 GetAngularVelocity () const noexcept; [[nodiscard]] SHVec3 GetAngularVelocity () const noexcept;
[[nodiscard]] SHVec3 GetPosition () const noexcept;
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* Setter Functions */ /* Setter Functions */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/

View File

@ -1,27 +0,0 @@
/****************************************************************************************
* \file SHPhysicsWorld.cpp
* \author Diren D Bharwani, diren.dbharwani, 390002520
* \brief Implementation for a Physics World.
*
* \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 <SHpch.h>
// Primary Header
#include "SHPhysicsWorld.h"
namespace SHADE
{
/*-----------------------------------------------------------------------------------*/
/* Constructors & Destructor Definitions */
/*-----------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------*/
/* Public Function Members Definitions */
/*-----------------------------------------------------------------------------------*/
} // namespace SHADE

View File

@ -15,6 +15,7 @@
// Project Headers // Project Headers
#include "ECS_Base/Managers/SHSystemManager.h" #include "ECS_Base/Managers/SHSystemManager.h"
#include "Math/Transform/SHTransformComponent.h"
#include "Scripting/SHScriptEngine.h" #include "Scripting/SHScriptEngine.h"
@ -42,21 +43,37 @@ namespace SHADE
SHLOGV_ERROR("Unable to invoke collision and trigger script events due to missing SHScriptEngine!"); SHLOGV_ERROR("Unable to invoke collision and trigger script events due to missing SHScriptEngine!");
} }
// Interpolate transforms for rendering const float FACTOR = static_cast<float>(physicsSystem->interpolationFactor);
// Interpolate transforms for rendering.
// Only rigid bodies can move due to physics, so we run through the rigid body component dense set.
const auto& RIGIDBODY_DENSE = SHComponentManager::GetDense<SHRigidBodyComponent>();
for (auto& rigidBodyComponent : RIGIDBODY_DENSE)
{
auto* transformComponent = SHComponentManager::GetComponent_s<SHTransformComponent>(rigidBodyComponent.GetEID());
if (!transformComponent)
continue;
if (!rigidBodyComponent.rigidBody)
continue;
if (const SHMotionState& MOTION_STATE = rigidBodyComponent.rigidBody->GetMotionState(); MOTION_STATE)
{
SHVec3 renderPosition = rigidBodyComponent.IsInterpolating()
? MOTION_STATE.InterpolatePositions(FACTOR)
: MOTION_STATE.position;
/*
* TODO: Test if the scene graph transforms abides by setting world position. Collisions will ignore the scene graph hierarchy.
*/
transformComponent->SetWorldPosition(renderPosition);
// TODO: SetOrientation
}
}
// Collision & Trigger messages // Collision & Trigger messages
if (scriptingSystem != nullptr) if (scriptingSystem != nullptr)
scriptingSystem->ExecuteCollisionFunctions(); scriptingSystem->ExecuteCollisionFunctions();
} }
/*-----------------------------------------------------------------------------------*/
/* Private Function Member Definitions */
/*-----------------------------------------------------------------------------------*/
void SHPhysicsSystem::PhysicsPostUpdate::syncTransforms(SHRigidBodyComponent* rbComponent) const noexcept
{
}
} }

View File

@ -36,27 +36,27 @@ namespace SHADE
auto* physicsSystem = reinterpret_cast<SHPhysicsSystem*>(GetSystem()); auto* physicsSystem = reinterpret_cast<SHPhysicsSystem*>(GetSystem());
// Get all physics objects & sync transforms // Get all physics objects & sync transforms
for (auto& physicsObject : physicsSystem->physicsObjectManager.GetPhysicsObjects() | std::views::values) for (auto& [entityID, physicsObject] : physicsSystem->physicsObjectManager.GetPhysicsObjects())
syncTransforms(&physicsObject);
}
/*-----------------------------------------------------------------------------------*/
/* Private Function Member Definitions */
/*-----------------------------------------------------------------------------------*/
void SHPhysicsSystem::PhysicsPreUpdate::syncTransforms(SHPhysicsObject* physicsObject) const noexcept
{ {
const EntityID EID = physicsObject->entityID; const auto* TRANSFORM_COMPONENT = SHComponentManager::GetComponent_s<SHTransformComponent>(entityID);
// Get relevant components: Transform, Rigidbody & Collider // We assume that all engine components and physics object components have been successfully linked
const auto* TRANSFORM_COMPONENT = SHComponentManager::GetComponent_s<SHTransformComponent>(EID);
auto* rigidBodyComponent = SHComponentManager::GetComponent_s<SHRigidBodyComponent>(EID);
auto* colliderComponent = SHComponentManager::GetComponent_s<SHColliderComponent>(EID);
if (TRANSFORM_COMPONENT && TRANSFORM_COMPONENT->HasChanged()) if (TRANSFORM_COMPONENT && TRANSFORM_COMPONENT->HasChanged())
{ {
// Sync the objects transforms // Sync the objects transforms
const SHVec3 WORLD_POS = TRANSFORM_COMPONENT->GetWorldPosition();
if (physicsObject.rigidBody)
{
SHMotionState& motionState = physicsObject.rigidBody->GetMotionState();
motionState.ForcePosition(TRANSFORM_COMPONENT->GetWorldPosition());
// TODO: Force Orientation
}
}
// TODO: Sync Collider Transform with World Transform
} }
} }
} }

View File

@ -47,7 +47,11 @@ namespace SHADE
int count = 0; int count = 0;
while (accumulatedTime > FIXED_DT) while (accumulatedTime > FIXED_DT)
{ {
if (scriptEngine)
scriptEngine->ExecuteFixedUpdates();
if (physicsSystem->physicsWorld)
physicsSystem->physicsWorld->Step(static_cast<float>(FIXED_DT));
accumulatedTime -= FIXED_DT; accumulatedTime -= FIXED_DT;
++count; ++count;

View File

@ -28,10 +28,11 @@ namespace SHADE
/* Constructors & Destructor Definitions */ /* Constructors & Destructor Definitions */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
SHPhysicsSystem::SHPhysicsSystem() SHPhysicsSystem::SHPhysicsSystem() noexcept
: worldUpdated { false } : worldUpdated { false }
, interpolationFactor { 0.0 } , interpolationFactor { 0.0 }
, fixedDT { DEFAULT_FIXED_STEP } , fixedDT { DEFAULT_FIXED_STEP }
, physicsWorld { nullptr }
{ {
// Add more events here to register them // Add more events here to register them
@ -45,6 +46,11 @@ namespace SHADE
#endif #endif
} }
SHPhysicsSystem::~SHPhysicsSystem() noexcept
{
delete physicsWorld;
}
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
/* Getter Function Definitions */ /* Getter Function Definitions */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
@ -141,24 +147,66 @@ namespace SHADE
SHEventHandle SHPhysicsSystem::onSceneInit(SHEventPtr onSceneInitEvent) SHEventHandle SHPhysicsSystem::onSceneInit(SHEventPtr onSceneInitEvent)
{ {
/* /*
* TODO:
* If a world already exists, destroy it * If a world already exists, destroy it
* Recreate the world * Recreate the world.
* *
* If there is an editor and editor mode is already playing, link all entities with the world immediately. * If there is an editor and editor mode is already playing, link all entities with the world immediately.
*/ */
// Destroy an existing world if there is one.
//! This should almost never happen.
if (physicsWorld)
{
// Remove all references of physics objects from the world
for (const auto& PHYSICS_OBJECT : physicsObjectManager.GetPhysicsObjects() | std::views::values)
{
if (PHYSICS_OBJECT.rigidBody)
physicsWorld->RemoveRigidBody(PHYSICS_OBJECT.rigidBody);
}
delete physicsWorld;
physicsWorld = nullptr;
}
// Create the physics world
physicsWorld = new SHPhysicsWorld;
#ifdef SHEDITOR
// Link all entities with the world if editor is already playing.
// This is for handling scene changes while the editor is active.
const auto* EDITOR = SHSystemManager::GetSystem<SHEditor>();
if (EDITOR && EDITOR->editorState == SHEditor::State::PLAY)
{
for (const auto& PHYSICS_OBJECT : physicsObjectManager.GetPhysicsObjects() | std::views::values)
{
if (PHYSICS_OBJECT.rigidBody)
physicsWorld->AddRigidBody(PHYSICS_OBJECT.rigidBody);
}
}
#endif
return onSceneInitEvent.get()->handle; return onSceneInitEvent.get()->handle;
} }
SHEventHandle SHPhysicsSystem::onSceneExit(SHEventPtr onSceneExitEvent) SHEventHandle SHPhysicsSystem::onSceneExit(SHEventPtr onSceneExitEvent)
{ {
/* /*
* TODO:
* Destroy the physics world. * Destroy the physics world.
* Destroy all physics objects. * Destroy all physics objects.
*/ */
for (const auto& PHYSICS_OBJECT : physicsObjectManager.GetPhysicsObjects() | std::views::values)
{
if (PHYSICS_OBJECT.rigidBody)
physicsWorld->RemoveRigidBody(PHYSICS_OBJECT.rigidBody);
}
delete physicsWorld;
physicsWorld = nullptr;
return onSceneExitEvent.get()->handle; return onSceneExitEvent.get()->handle;
} }
@ -181,8 +229,17 @@ namespace SHADE
// Link engine components with physics object component // Link engine components with physics object component
if (IS_RIGID_BODY) if (IS_RIGID_BODY)
{
physicsObjectManager.AddRigidBody(EID); physicsObjectManager.AddRigidBody(EID);
if (physicsWorld)
{
auto* rigidBody = physicsObjectManager.GetPhysicsObject(EID)->rigidBody;
physicsWorld->AddRigidBody(rigidBody);
}
}
if (IS_COLLIDER) if (IS_COLLIDER)
physicsObjectManager.AddCollider(EID); physicsObjectManager.AddCollider(EID);
} }
@ -209,7 +266,16 @@ namespace SHADE
// Link engine components with physics object component // Link engine components with physics object component
if (IS_RIGID_BODY) if (IS_RIGID_BODY)
{
if (physicsWorld)
{
auto* rigidBody = physicsObjectManager.GetPhysicsObject(EID)->rigidBody;
physicsWorld->RemoveRigidBody(rigidBody);
}
physicsObjectManager.RemoveRigidBody(EID); physicsObjectManager.RemoveRigidBody(EID);
}
if (IS_COLLIDER) if (IS_COLLIDER)
physicsObjectManager.RemoveCollider(EID); physicsObjectManager.RemoveCollider(EID);
@ -222,10 +288,15 @@ namespace SHADE
SHEventHandle SHPhysicsSystem::onEditorPlay(SHEventPtr onEditorPlayEvent) SHEventHandle SHPhysicsSystem::onEditorPlay(SHEventPtr onEditorPlayEvent)
{ {
/* // Add all physics components to the physics world
* TODO: for (const auto& PHYSICS_OBJECT : physicsObjectManager.GetPhysicsObjects() | std::views::values)
* Link all entities with the world. {
*/ // Add rigid body if it exists
if (PHYSICS_OBJECT.rigidBody)
physicsWorld->AddRigidBody(PHYSICS_OBJECT.rigidBody);
// TODO: Add Collider if it exists
}
return onEditorPlayEvent.get()->handle; return onEditorPlayEvent.get()->handle;
} }

View File

@ -16,6 +16,7 @@
#include "ECS_Base/System/SHFixedSystemRoutine.h" #include "ECS_Base/System/SHFixedSystemRoutine.h"
#include "Events/SHEvent.h" #include "Events/SHEvent.h"
#include "Physics/Dynamics/SHPhysicsWorld.h"
#include "Physics/Interface/PhysicsObject/SHPhysicsObjectManager.h" #include "Physics/Interface/PhysicsObject/SHPhysicsObjectManager.h"
#include "Physics/Interface/RigidBodyComponent/SHRigidBodyComponent.h" #include "Physics/Interface/RigidBodyComponent/SHRigidBodyComponent.h"
#include "Physics/Interface/SHColliderComponent.h" #include "Physics/Interface/SHColliderComponent.h"
@ -34,7 +35,8 @@ namespace SHADE
/* Constructors & Destructor */ /* Constructors & Destructor */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
SHPhysicsSystem(); SHPhysicsSystem () noexcept;
~SHPhysicsSystem() noexcept;
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* Getter Functions */ /* Getter Functions */
@ -75,9 +77,6 @@ namespace SHADE
public: public:
PhysicsPreUpdate(); PhysicsPreUpdate();
void Execute(double dt) noexcept override; void Execute(double dt) noexcept override;
private:
void syncTransforms(SHPhysicsObject* physicsObject) const noexcept;
}; };
/** /**
@ -103,9 +102,6 @@ namespace SHADE
public: public:
PhysicsPostUpdate(); PhysicsPostUpdate();
void Execute(double dt) noexcept override; void Execute(double dt) noexcept override;
private:
void syncTransforms(SHRigidBodyComponent* rbComponent) const noexcept;
}; };
private: private:
@ -136,6 +132,8 @@ namespace SHADE
// Sub-systems / managers // Sub-systems / managers
SHPhysicsWorld* physicsWorld;
SHPhysicsObjectManager physicsObjectManager; SHPhysicsObjectManager physicsObjectManager;
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/

View File

@ -52,5 +52,6 @@ namespace SHADE
[[nodiscard]] static const std::vector<SHCollisionInfo>& GetCollisionInfo () noexcept; [[nodiscard]] static const std::vector<SHCollisionInfo>& GetCollisionInfo () noexcept;
[[nodiscard]] static const std::vector<SHCollisionInfo>& GetTriggerInfo () noexcept; [[nodiscard]] static const std::vector<SHCollisionInfo>& GetTriggerInfo () noexcept;
[[nodiscard]] static double GetFixedDT () noexcept; [[nodiscard]] static double GetFixedDT () noexcept;
[[nodiscard]] static int GetFixedUpdateRate () noexcept;
}; };
} }