Implemented a custom physics engine #316

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

View File

@ -45,7 +45,7 @@
NumberOfChildren: 0
Components:
Camera Component:
Position: {x: 0, y: 4, z: 5}
Position: {x: 0, y: 2, z: 5}
Pitch: 0
Yaw: 0
Roll: 0
@ -62,14 +62,14 @@
NumberOfChildren: 0
Components:
Transform Component:
Translate: {x: -1.45715916, y: 7, z: 0.0329093337}
Translate: {x: -1.45715916, y: 7, z: 0.227711335}
Rotate: {x: -0, y: 0, z: -0}
Scale: {x: 1, y: 1, z: 1}
IsActive: true
RigidBody Component:
Type: Dynamic
Auto Mass: false
Mass: 1
Mass: 0.52359879
Drag: 0.00999999978
Angular Drag: 0.00999999978
Use Gravity: true
@ -77,7 +77,7 @@
Interpolate: true
Sleeping Enabled: true
Freeze Position X: false
Freeze Position Y: false
Freeze Position Y: true
Freeze Position Z: false
Freeze Rotation X: false
Freeze Rotation Y: false
@ -95,6 +95,15 @@
Density: 1
Position Offset: {x: 0, y: 0, z: 0}
Rotation Offset: {x: 0, y: 0, z: 0}
- Is Trigger: false
Collision Tag: 1
Type: Sphere
Radius: 0.5
Friction: 0.400000006
Bounciness: 0
Density: 1
Position Offset: {x: 0.75, y: 0.5, z: 0}
Rotation Offset: {x: 0, y: 0, z: 0}
IsActive: true
Scripts:
- Type: PhysicsTestObj

View File

@ -251,9 +251,16 @@ namespace SHADE
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);}, "Whether Gravity is enabled for this body");
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");
SHEditorWidgets::CheckBox("Auto Mass", [component]{return component->GetAutoMass();}, [component](bool const& value){component->SetAutoMass(value);}, "If mass should be automatically computed");
const bool autoMass = component->GetAutoMass();
if (autoMass)
SHEditorWidgets::DragFloat("Mass", [component] {return component->GetMass(); }, [component](float const& value) {}, "Mass", 0.1f, 0.0f, 0.0f, "%.3f", ImGuiSliderFlags_ReadOnly);
else
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
{

View File

@ -147,11 +147,6 @@ namespace SHADE
return relativeExtents;
}
SHQuaternion SHBoxCollisionShape::GetOrientation() const noexcept
{
return Orientation;
}
SHVec3 SHBoxCollisionShape::GetVertex(int index) const
{
static constexpr int NUM_VERTICES = 8;
@ -160,41 +155,6 @@ namespace SHADE
throw std::invalid_argument("Index out-of-range!");
return GetVertices()[index];
//// Rotate half extents
//const SHVec3 ROTATED_EXTENTS = SHVec3::Rotate(Extents, Orientation);
//const SHVec3 CENTER { Center };
///*
// * Front: -Z
// *
// * 3 _____ 2
// * | |
// * | |
// * |_____|
// * 0 1
// *
// * Back: +Z
// *
// * 7 _____ 6
// * | |
// * | |
// * |_____|
// * 4 5
// *
// */
//switch (index)
//{
// case 0: return CENTER + SHVec3 { -ROTATED_EXTENTS.x, -ROTATED_EXTENTS.y, -ROTATED_EXTENTS.z }
// case 1: return CENTER + SHVec3 {
// case 2: return CENTER + SHVec3 {
// case 3: return CENTER + SHVec3 {
// case 4: return CENTER + SHVec3 {
// case 5: return CENTER + SHVec3 {
// case 6: return CENTER + SHVec3 {
// case 7: return CENTER + SHVec3 {
//}
}
SHVec3 SHBoxCollisionShape::GetNormal(int faceIndex) const
@ -204,7 +164,26 @@ namespace SHADE
// Rotate normal into world space
return SHVec3::Rotate(LOCAL_NORMAL, Orientation);
}
SHVec3 SHBoxCollisionShape::GetPosition() const noexcept
{
return Center;
}
SHQuaternion SHBoxCollisionShape::GetOrientation() const noexcept
{
return Orientation;
}
float SHBoxCollisionShape::GetVolume() const noexcept
{
return Volume();
}
SHVec3 SHBoxCollisionShape::GetLocalCentroid() const noexcept
{
return SHVec3::Zero;
}
/*-----------------------------------------------------------------------------------*/

View File

@ -75,10 +75,14 @@ namespace SHADE
[[nodiscard]] SHVec3 GetCenter () const noexcept;
[[nodiscard]] SHVec3 GetWorldExtents () const noexcept;
[[nodiscard]] SHVec3 GetRelativeExtents () const noexcept;
[[nodiscard]] SHQuaternion GetOrientation () const noexcept;
[[nodiscard]] SHVec3 GetVertex (int index) const override;
[[nodiscard]] SHVec3 GetNormal (int faceIndex) const override;
[[nodiscard]] SHVec3 GetNormal (int faceIndex) const override;
[[nodiscard]] SHVec3 GetPosition () const noexcept override;
[[nodiscard]] SHQuaternion GetOrientation () const noexcept override;
[[nodiscard]] float GetVolume () const noexcept override;
[[nodiscard]] SHVec3 GetLocalCentroid () const noexcept override;
/*---------------------------------------------------------------------------------*/
/* Setter Functions */

View File

@ -14,6 +14,7 @@
#include "SHCollisionShape.h"
// Project Headers
#include "Physics/Collision/SHCollider.h"
#include "Physics/Collision/CollisionTags/SHCollisionTagMatrix.h"
#include "Reflection/SHReflectionMetadata.h"
#include "Tools/Utilities/SHUtilities.h"
@ -111,6 +112,21 @@ namespace SHADE
return *collisionTag;
}
SHVec3 SHCollisionShape::GetPosition() const noexcept
{
const SHTransform& PARENT_TRANSFORM = collider->GetTransform();
const SHQuaternion FINAL_ROT = PARENT_TRANSFORM.orientation * transform.orientation;
const SHMatrix TRS = SHMatrix::Rotate(FINAL_ROT) * SHMatrix::Translate(PARENT_TRANSFORM.position);
return SHVec3::Transform(transform.position, TRS);
}
SHQuaternion SHCollisionShape::GetOrientation() const noexcept
{
return collider->GetOrientation() * transform.orientation;
}
/*-----------------------------------------------------------------------------------*/
/* Setter Function Definitions */
/*-----------------------------------------------------------------------------------*/

View File

@ -94,6 +94,8 @@ namespace SHADE
[[nodiscard]] float GetDensity () const noexcept;
[[nodiscard]] const SHPhysicsMaterial& GetMaterial () const noexcept;
// Offsets
[[nodiscard]] const SHVec3& GetPositionOffset () const noexcept;
@ -107,6 +109,14 @@ namespace SHADE
[[nodiscard]] const SHCollisionTag& GetCollisionTag () const noexcept;
// Virtual methods
[[nodiscard]] virtual SHVec3 GetPosition () const noexcept;
[[nodiscard]] virtual SHQuaternion GetOrientation () const noexcept;
[[nodiscard]] virtual float GetVolume () const noexcept = 0;
[[nodiscard]] virtual SHMatrix GetInertiaTensor (float mass) const noexcept = 0;
[[nodiscard]] virtual SHVec3 GetLocalCentroid () const noexcept = 0;
/*---------------------------------------------------------------------------------*/
/* Setter Functions */
/*---------------------------------------------------------------------------------*/
@ -130,7 +140,6 @@ namespace SHADE
/*---------------------------------------------------------------------------------*/
virtual void ComputeTransforms () noexcept = 0;
[[nodiscard]] virtual SHMatrix GetInertiaTensor (float mass) const noexcept = 0;
[[nodiscard]] virtual SHMatrix ComputeWorldTransform () const noexcept = 0;
[[nodiscard]] virtual SHAABB ComputeAABB () const noexcept = 0;

View File

@ -141,6 +141,26 @@ namespace SHADE
return relativeRadius;
}
SHVec3 SHSphereCollisionShape::GetPosition() const noexcept
{
return Center;
}
SHQuaternion SHSphereCollisionShape::GetOrientation() const noexcept
{
return collider->GetTransform().orientation * transform.orientation;
}
float SHSphereCollisionShape::GetVolume() const noexcept
{
return Volume();
}
SHVec3 SHSphereCollisionShape::GetLocalCentroid() const noexcept
{
return SHVec3::Zero;
}
/*-----------------------------------------------------------------------------------*/
/* Setter Function Definitions */
/*-----------------------------------------------------------------------------------*/

View File

@ -71,9 +71,14 @@ namespace SHADE
/* Getter Functions */
/*---------------------------------------------------------------------------------*/
[[nodiscard]] SHVec3 GetCenter () const noexcept;
[[nodiscard]] float GetWorldRadius () const noexcept;
[[nodiscard]] float GetRelativeRadius () const noexcept;
[[nodiscard]] SHVec3 GetCenter () const noexcept;
[[nodiscard]] float GetWorldRadius () const noexcept;
[[nodiscard]] float GetRelativeRadius () const noexcept;
[[nodiscard]] SHVec3 GetPosition () const noexcept override;
[[nodiscard]] SHQuaternion GetOrientation () const noexcept override;
[[nodiscard]] float GetVolume () const noexcept override;
[[nodiscard]] SHVec3 GetLocalCentroid () const noexcept override;
/*---------------------------------------------------------------------------------*/
/* Setter Functions */

View File

@ -14,6 +14,8 @@
#include "SHRigidBody.h"
// Project Headers
#include <numeric>
#include "Physics/Collision/SHCollider.h"
#include "Tools/Logger/SHLogger.h"
@ -60,19 +62,19 @@ namespace SHADE
}
SHRigidBody::SHRigidBody(SHRigidBody&& rhs) noexcept
: entityID { rhs.entityID }
, collider { nullptr }
, bodyType { rhs.bodyType }
, gravityScale { rhs.gravityScale }
, invMass { rhs.invMass }
, linearDrag { rhs.linearDrag }
, angularDrag { rhs.angularDrag }
: entityID { rhs.entityID }
, collider { nullptr }
, bodyType { rhs.bodyType }
, gravityScale { rhs.gravityScale }
, invMass { rhs.invMass }
, linearDrag { rhs.linearDrag }
, angularDrag { rhs.angularDrag }
, localInvInertia { rhs.localInvInertia }
, worldInvInertia { rhs.worldInvInertia }
, localCentroid { rhs.localCentroid }
, worldCentroid { rhs.worldCentroid }
, flags { rhs.flags }
, motionState { std::move(rhs.motionState) }
, flags { rhs.flags }
, motionState { std::move(rhs.motionState) }
{
// All other properties are defaulted to 0 to not carry over any potential errors / invalid values.
}
@ -114,21 +116,21 @@ namespace SHADE
SHRigidBody& SHRigidBody::operator=(SHRigidBody&& rhs) noexcept
{
entityID = rhs.entityID;
entityID = rhs.entityID;
// Deep copy the collider
*collider = *rhs.collider;
bodyType = rhs.bodyType;
gravityScale = rhs.gravityScale;
invMass = rhs.invMass;
linearDrag = rhs.linearDrag;
angularDrag = rhs.angularDrag;
*collider = *rhs.collider;
bodyType = rhs.bodyType;
gravityScale = rhs.gravityScale;
invMass = rhs.invMass;
linearDrag = rhs.linearDrag;
angularDrag = rhs.angularDrag;
localInvInertia = rhs.localInvInertia;
worldInvInertia = rhs.worldInvInertia;
localCentroid = rhs.localCentroid;
worldCentroid = rhs.worldCentroid;
flags = rhs.flags;
motionState = std::move(rhs.motionState);
flags = rhs.flags;
motionState = std::move(rhs.motionState);
// All other properties are defaulted to 0 to not carry over any potential errors / invalid values.
accumulatedForce = SHVec3::Zero;
@ -242,6 +244,12 @@ namespace SHADE
return flags & (1U << FLAG_POS);
}
bool SHRigidBody::IsTriggerInMassData() const noexcept
{
static constexpr unsigned int FLAG_POS = 6;
return flags & (1U << FLAG_POS);
}
bool SHRigidBody::GetFreezePositionX() const noexcept
{
static constexpr unsigned int FLAG_POS = 10;
@ -334,6 +342,11 @@ namespace SHADE
invMass = 1.0f / newMass;
// Turn off automass
static constexpr unsigned int AUTO_MASS_FLAG = 4;
static constexpr uint16_t VALUE = 1U << AUTO_MASS_FLAG;
flags &= ~(VALUE);
ComputeMassData();
}
@ -455,7 +468,6 @@ namespace SHADE
if (enableAutoMass)
{
flags |= VALUE;
// TODO: Compute mass based on collider geometry
}
else
{
@ -463,6 +475,16 @@ namespace SHADE
// Use default mass of 1
invMass = 1.0f;
}
ComputeMassData();
}
void SHRigidBody::SetTriggerInMassData(bool triggerInMassData) noexcept
{
static constexpr unsigned int FLAG_POS = 6;
static constexpr uint16_t VALUE = 1U << FLAG_POS;
triggerInMassData ? flags |= VALUE : flags &= ~VALUE;
}
void SHRigidBody::SetFreezePositionX(bool freezePositionX) noexcept
@ -532,7 +554,7 @@ namespace SHADE
else
{
flags &= ~VALUE;
computeInertiaTensor();
ComputeMassData();
}
}
@ -552,7 +574,7 @@ namespace SHADE
else
{
flags &= ~VALUE;
computeInertiaTensor();
ComputeMassData();
}
}
@ -572,7 +594,7 @@ namespace SHADE
else
{
flags &= ~VALUE;
computeInertiaTensor();
ComputeMassData();
}
}
@ -628,60 +650,110 @@ namespace SHADE
void SHRigidBody::ComputeMassData() noexcept
{
computeCentroid();
computeMassAndInertiaTensor();
// Reset centroid
localCentroid = SHVec3::Zero;
localInvInertia = SHMatrix::Identity;
// In the instance the body is a particle
if (!collider || collider->GetCollisionShapes().empty())
{
localInvInertia.m[0][0] = localInvInertia.m[1][1] = localInvInertia.m[2][2] = invMass;
return;
}
const float CUSTOM_MASS = 1.0f / invMass;
const bool AUTO_MASS = IsAutoMassEnabled();
const bool INCLUDE_TRIGGERS = IsTriggerInMassData();
const auto& SHAPES = collider->GetCollisionShapes();
// Compute Total mass and store individual masses if custom mass is being used.
// Compute local centroid at the same time
// Zero matrix;
SHMatrix tmpLocalTensor;
tmpLocalTensor -= SHMatrix::Identity;
float totalMass = 0.0f;
std::vector<float> trueMass;
for (auto* shape : SHAPES)
{
// We skip triggers by default
if (shape->IsTrigger() && !INCLUDE_TRIGGERS)
continue;
shape->ComputeTransforms();
// p = m/v, therefore m = pv. This is the true mass of the shape.
const float MASS = shape->GetDensity() * shape->GetVolume();
totalMass += MASS;
trueMass.emplace_back(MASS);
// Weighted sum of masses contribute to the centroid's location using the collider's local position.
localCentroid += MASS * (shape->GetPosition() - collider->GetPosition());
}
if (totalMass > 0.0f)
{
localCentroid /= totalMass;
if (AUTO_MASS)
invMass = 1.0f / totalMass;
}
const SHMatrix R = SHMatrix::Rotate(motionState.orientation);
const SHMatrix RT = SHMatrix::Transpose(R);
worldCentroid = (R * localCentroid) + motionState.position;
for (size_t i = 0; i < SHAPES.size(); ++i)
{
const auto* SHAPE = SHAPES[i];
// We skip triggers by default
if (SHAPE->IsTrigger() && !INCLUDE_TRIGGERS)
continue;
// If using custom mass, take the ratio of the mass
float actualMass = trueMass[i];
if (!AUTO_MASS)
actualMass *= CUSTOM_MASS / totalMass;
// Convert inertia tensor into local-space of the body
SHMatrix I = SHAPE->GetInertiaTensor( actualMass ) * RT;
I = R * I;
// Parallel Axis Theorem
const SHVec3 O = SHAPE->GetPosition() - worldCentroid;
const float O2 = O.LengthSquared();
SHMatrix offsetMatrix;
offsetMatrix -= SHMatrix::Identity;
offsetMatrix.m[0][0] = offsetMatrix.m[1][1] = offsetMatrix.m[2][2] = O2;
const SHVec3 NOX = O * -O.x;
const SHVec3 NOY = O * -O.y;
const SHVec3 NOZ = O * -O.z;
offsetMatrix += SHMatrix{ NOX, NOY, NOZ, SHVec4::Zero };
offsetMatrix *= actualMass;
tmpLocalTensor += I + offsetMatrix;
}
// Set diagonals then invert
localInvInertia.m[0][0] = tmpLocalTensor.m[0][0];
localInvInertia.m[1][1] = tmpLocalTensor.m[1][1];
localInvInertia.m[2][2] = tmpLocalTensor.m[2][2];
localInvInertia = SHMatrix::Inverse(localInvInertia);
}
/*-----------------------------------------------------------------------------------*/
/* Private Member Function Definition */
/*-----------------------------------------------------------------------------------*/
void SHRigidBody::computeCentroid() noexcept
{
// TODO
}
void SHRigidBody::computeMass() noexcept
{
// TODO: If auto mass in enabled, compute total mass based from each collider.
// TODO: If auto mass disabled, compute inertia tensor for each shape using the ratio of its mass / total mass by comparing the volume / total volume.
}
void SHRigidBody::computeInertiaTensor() noexcept
{
// TODO: Compute total inertia from composited colliders using the Parallel Axis Theorem.
if (!collider || collider->GetCollisionShapes().empty())
{
localInvInertia.m[0][0] = localInvInertia.m[1][1] = localInvInertia.m[2][2] = invMass;
}
else
{
// HACK: For now, take only the first shape as we are testing with non-composited colliders. We are using the center as the centroid.
const auto* FIRST_SHAPE = collider->GetCollisionShape(0);
localInvInertia = SHMatrix::Inverse(FIRST_SHAPE->GetInertiaTensor(1.0f / invMass));
}
}
void SHRigidBody::computeMassAndInertiaTensor() noexcept
{
// TODO: If auto mass in enabled, compute total mass based from each collider.
// TODO: If auto mass disabled, compute inertia tensor for each shape using the ratio of its mass / total mass by comparing the volume / total volume.
// TODO: Compute total inertia from composited colliders using the Parallel Axis Theorem.
if (!collider || collider->GetCollisionShapes().empty())
{
localInvInertia.m[0][0] = localInvInertia.m[1][1] = localInvInertia.m[2][2] = invMass;
}
else
{
// HACK: For now, take only the first shape as we are testing with non-composited colliders. We are using the center as the centroid.
const auto* FIRST_SHAPE = collider->GetCollisionShape(0);
localInvInertia = SHMatrix::Inverse(FIRST_SHAPE->GetInertiaTensor(1.0f / invMass));
}
}
void SHRigidBody::constrainLinearVelocities() noexcept
{
linearVelocity.x = GetFreezePositionX() ? 0.0f : linearVelocity.x;

View File

@ -99,6 +99,7 @@ namespace SHADE
[[nodiscard]] bool IsSleepingEnabled () const noexcept;
[[nodiscard]] bool IsGravityEnabled () const noexcept;
[[nodiscard]] bool IsAutoMassEnabled () const noexcept;
[[nodiscard]] bool IsTriggerInMassData () const noexcept;
[[nodiscard]] bool GetFreezePositionX () const noexcept;
[[nodiscard]] bool GetFreezePositionY () const noexcept;
[[nodiscard]] bool GetFreezePositionZ () const noexcept;
@ -157,6 +158,7 @@ namespace SHADE
void SetSleepingEnabled (bool enableSleeping) noexcept;
void SetGravityEnabled (bool enableGravity) noexcept;
void SetAutoMassEnabled (bool enableAutoMass) noexcept;
void SetTriggerInMassData(bool triggerInMassData) noexcept;
void SetFreezePositionX (bool freezePositionX) noexcept;
void SetFreezePositionY (bool freezePositionY) noexcept;
void SetFreezePositionZ (bool freezePositionZ) noexcept;
@ -249,7 +251,7 @@ namespace SHADE
SHVec3 linearVelocity;
SHVec3 angularVelocity;
// aZ aY aX rotLockActive pZ pY pX posLockActive 0 0 inIsland autoMass enableGravity enableSleeping sleeping active
// aZ aY aX pZ pY pX 0 0 0 addTriggersToMassData inIsland autoMass enableGravity enableSleeping sleeping active
uint16_t flags;
SHMotionState motionState;
@ -258,11 +260,6 @@ namespace SHADE
/* Member Functions */
/*-----------------------------------------------------------------------------------*/
void computeCentroid () noexcept;
void computeMass () noexcept;
void computeInertiaTensor () noexcept;
void computeMassAndInertiaTensor() noexcept;
void constrainLinearVelocities () noexcept;
void constrainAngularVelocities () noexcept;