Editor fixes - Parenting of range selected entities, Component IsActive Checkbox, Scene Play/Pause behaviour, Gizmo controls #209

Merged
srishamharan merged 6 commits from SP3-4-Editor into main 2022-11-15 16:37:09 +08:00
38 changed files with 946 additions and 435 deletions
Showing only changes of commit 7c636adbfc - Show all commits

27
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,27 @@
---
name: Bug report
about: Report a bug that should be fixed
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Additional context**
Add any other context about the problem here.

View File

@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest a feature for the project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@ -223,9 +223,4 @@
Bounciness: 0 Bounciness: 0
Density: 1 Density: 1
Position Offset: {x: 0, y: 0.5, z: 0} Position Offset: {x: 0, y: 0.5, z: 0}
Scripts: Scripts: ~
- Type: Item
currCategory: 0
- Type: PickAndThrow
throwForce: [100, 200, 100]
item: 51000

View File

@ -560,6 +560,34 @@ namespace SHADE
assetCollection.emplace(newAsset.id, newAsset); assetCollection.emplace(newAsset.id, newAsset);
SHAssetMetaHandler::WriteMetaData(newAsset); SHAssetMetaHandler::WriteMetaData(newAsset);
return newAsset.id;
}
else if (ext == SCENE_EXTENSION)
{
SHAsset newAsset{
path.stem().string(),
GenerateAssetID(AssetType::SCENE),
AssetType::SCENE,
path,
false
};
assetCollection.emplace(newAsset.id, newAsset);
SHAssetMetaHandler::WriteMetaData(newAsset);
return newAsset.id;
}
else if (ext == FONT_EXTENSION)
{
SHAsset newAsset{
path.stem().string(),
GenerateAssetID(AssetType::FONT),
AssetType::FONT,
path,
false
};
assetCollection.emplace(newAsset.id, newAsset);
SHAssetMetaHandler::WriteMetaData(newAsset);
return newAsset.id; return newAsset.id;
} }
} }
@ -571,8 +599,11 @@ namespace SHADE
for (auto& file : toGenNew) for (auto& file : toGenNew)
{ {
auto newID{ GenerateNewMeta(file->path).value() }; auto newID{ GenerateNewMeta(file->path) };
file->assetMeta = &assetCollection[newID]; if (newID.has_value())
{
file->assetMeta = &assetCollection[newID.value()];
}
} }
for (auto& asset : std::ranges::views::values(assetCollection)) for (auto& asset : std::ranges::views::values(assetCollection))

View File

@ -223,7 +223,7 @@ namespace SHADE
{ {
if (!component) if (!component)
return; return;
ImGui::PushID(component);
const auto componentType = rttr::type::get(*component); const auto componentType = rttr::type::get(*component);
SHEditorWidgets::CheckBox("##IsActive", [component]() {return component->isActive; }, [component](bool const& active) {component->isActive = active; }, "Is Component Active"); SHEditorWidgets::CheckBox("##IsActive", [component]() {return component->isActive; }, [component](bool const& active) {component->isActive = active; }, "Is Component Active");
ImGui::SameLine(); ImGui::SameLine();
@ -246,7 +246,7 @@ namespace SHADE
if (collider->GetType() == SHCollisionShape::Type::BOX) if (collider->GetType() == SHCollisionShape::Type::BOX)
{ {
SHEditorWidgets::BeginPanel(std::format("{} Box #{}", ICON_FA_CUBE, i).data(), { ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y }); SHEditorWidgets::BeginPanel(std::format("{} Box #{}", ICON_FA_CUBE, i).data(), { ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y });
const auto* BOX = reinterpret_cast<const SHBoundingBox*>(collider->GetShape()); const auto* BOX = reinterpret_cast<const SHBox*>(collider->GetShape());
SHEditorWidgets::DragVec3 SHEditorWidgets::DragVec3
( (
"Half Extents", { "X", "Y", "Z" }, "Half Extents", { "X", "Y", "Z" },
@ -256,7 +256,7 @@ namespace SHADE
else if (collider->GetType() == SHCollisionShape::Type::SPHERE) else if (collider->GetType() == SHCollisionShape::Type::SPHERE)
{ {
SHEditorWidgets::BeginPanel(std::format("{} Sphere #{}", ICON_MD_CIRCLE, i).data(), { ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y }); SHEditorWidgets::BeginPanel(std::format("{} Sphere #{}", ICON_MD_CIRCLE, i).data(), { ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y });
const auto* SPHERE = reinterpret_cast<const SHBoundingSphere*>(collider->GetShape()); const auto* SPHERE = reinterpret_cast<const SHSphere*>(collider->GetShape());
SHEditorWidgets::DragFloat SHEditorWidgets::DragFloat
( (
"Radius", "Radius",
@ -322,6 +322,7 @@ namespace SHADE
} }
} }
else DrawContextMenu(component); else DrawContextMenu(component);
ImGui::PopID();
} }
template<> template<>

View File

@ -34,8 +34,11 @@ namespace SHADE
void SHRenderable::OnDestroy() void SHRenderable::OnDestroy()
{ {
// Remove from SuperBatch // Remove from SuperBatch
Handle<SHSuperBatch> superBatch = sharedMaterial->GetBaseMaterial()->GetPipeline()->GetPipelineState().GetSubpass()->GetSuperBatch(); if (sharedMaterial)
superBatch->Remove(this); {
Handle<SHSuperBatch> superBatch = sharedMaterial->GetBaseMaterial()->GetPipeline()->GetPipelineState().GetSubpass()->GetSuperBatch();
superBatch->Remove(this);
}
// Free resources // Free resources
if (material) if (material)

View File

@ -11,7 +11,7 @@
#include <SHpch.h> #include <SHpch.h>
// Primary Header // Primary Header
#include "SHBoundingBox.h" #include "SHBox.h"
// Project Headers // Project Headers
#include "Math/SHMathHelpers.h" #include "Math/SHMathHelpers.h"
#include "Math/SHRay.h" #include "Math/SHRay.h"
@ -24,13 +24,13 @@ namespace SHADE
/* Constructors & Destructor Definitions */ /* Constructors & Destructor Definitions */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
SHBoundingBox::SHBoundingBox() noexcept SHBox::SHBox() noexcept
: RelativeExtents { SHVec3::One } : RelativeExtents { SHVec3::One }
{ {
type = Type::BOX; type = Type::BOX;
} }
SHBoundingBox::SHBoundingBox(const SHVec3& c, const SHVec3& hE) noexcept SHBox::SHBox(const SHVec3& c, const SHVec3& hE) noexcept
: RelativeExtents { SHVec3::One } : RelativeExtents { SHVec3::One }
{ {
type = Type::BOX; type = Type::BOX;
@ -40,7 +40,7 @@ namespace SHADE
} }
SHBoundingBox::SHBoundingBox(const SHBoundingBox& rhs) noexcept SHBox::SHBox(const SHBox& rhs) noexcept
{ {
if (this == &rhs) if (this == &rhs)
return; return;
@ -52,7 +52,7 @@ namespace SHADE
RelativeExtents = rhs.RelativeExtents; RelativeExtents = rhs.RelativeExtents;
} }
SHBoundingBox::SHBoundingBox(SHBoundingBox&& rhs) noexcept SHBox::SHBox(SHBox&& rhs) noexcept
{ {
type = Type::BOX; type = Type::BOX;
@ -65,7 +65,7 @@ namespace SHADE
/* Operator Overload Definitions */ /* Operator Overload Definitions */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
SHBoundingBox& SHBoundingBox::operator=(const SHBoundingBox& rhs) noexcept SHBox& SHBox::operator=(const SHBox& rhs) noexcept
{ {
if (rhs.type != Type::BOX) if (rhs.type != Type::BOX)
{ {
@ -81,7 +81,7 @@ namespace SHADE
return *this; return *this;
} }
SHBoundingBox& SHBoundingBox::operator=(SHBoundingBox&& rhs) noexcept SHBox& SHBox::operator=(SHBox&& rhs) noexcept
{ {
if (rhs.type != Type::BOX) if (rhs.type != Type::BOX)
{ {
@ -101,27 +101,27 @@ namespace SHADE
/* Getter Function Definitions */ /* Getter Function Definitions */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
SHVec3 SHBoundingBox::GetCenter() const noexcept SHVec3 SHBox::GetCenter() const noexcept
{ {
return Center; return Center;
} }
SHVec3 SHBoundingBox::GetWorldExtents() const noexcept SHVec3 SHBox::GetWorldExtents() const noexcept
{ {
return Extents; return Extents;
} }
const SHVec3& SHBoundingBox::GetRelativeExtents() const noexcept const SHVec3& SHBox::GetRelativeExtents() const noexcept
{ {
return RelativeExtents; return RelativeExtents;
} }
SHVec3 SHBoundingBox::GetMin() const noexcept SHVec3 SHBox::GetMin() const noexcept
{ {
return SHVec3{ Center.x - Extents.x, Center.y - Extents.y, Center.z - Extents.z }; return SHVec3{ Center.x - Extents.x, Center.y - Extents.y, Center.z - Extents.z };
} }
SHVec3 SHBoundingBox::GetMax() const noexcept SHVec3 SHBox::GetMax() const noexcept
{ {
return SHVec3{ Center.x + Extents.x, Center.y + Extents.y, Center.z + Extents.z }; return SHVec3{ Center.x + Extents.x, Center.y + Extents.y, Center.z + Extents.z };
} }
@ -130,22 +130,22 @@ namespace SHADE
/* Setter Function Definitions */ /* Setter Function Definitions */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
void SHBoundingBox::SetCenter(const SHVec3& newCenter) noexcept void SHBox::SetCenter(const SHVec3& newCenter) noexcept
{ {
Center = newCenter; Center = newCenter;
} }
void SHBoundingBox::SetWorldExtents(const SHVec3& newWorldExtents) noexcept void SHBox::SetWorldExtents(const SHVec3& newWorldExtents) noexcept
{ {
Extents = newWorldExtents; Extents = newWorldExtents;
} }
void SHBoundingBox::SetRelativeExtents(const SHVec3& newRelativeExtents) noexcept void SHBox::SetRelativeExtents(const SHVec3& newRelativeExtents) noexcept
{ {
RelativeExtents = newRelativeExtents; RelativeExtents = newRelativeExtents;
} }
void SHBoundingBox::SetMin(const SHVec3& min) noexcept void SHBox::SetMin(const SHVec3& min) noexcept
{ {
const SHVec3 MAX = GetMax(); const SHVec3 MAX = GetMax();
@ -153,7 +153,7 @@ namespace SHADE
Extents = SHVec3::Abs((MAX - min) * 0.5f); Extents = SHVec3::Abs((MAX - min) * 0.5f);
} }
void SHBoundingBox::SetMax(const SHVec3& max) noexcept void SHBox::SetMax(const SHVec3& max) noexcept
{ {
const SHVec3 MIN = GetMin(); const SHVec3 MIN = GetMin();
@ -161,13 +161,13 @@ namespace SHADE
Extents = SHVec3::Abs((max - MIN) * 0.5f); Extents = SHVec3::Abs((max - MIN) * 0.5f);
} }
void SHBoundingBox::SetMinMax(const SHVec3& min, const SHVec3& max) noexcept void SHBox::SetMinMax(const SHVec3& min, const SHVec3& max) noexcept
{ {
Center = SHVec3::Lerp(min, max, 0.5f); Center = SHVec3::Lerp(min, max, 0.5f);
Extents = SHVec3::Abs((max - min) * 0.5f); Extents = SHVec3::Abs((max - min) * 0.5f);
} }
std::vector<SHVec3> SHBoundingBox::GetVertices() const noexcept std::vector<SHVec3> SHBox::GetVertices() const noexcept
{ {
std::vector<SHVec3> vertices{ 8 }; std::vector<SHVec3> vertices{ 8 };
GetCorners(vertices.data()); GetCorners(vertices.data());
@ -178,27 +178,27 @@ namespace SHADE
/* Public Function Member Definitions */ /* Public Function Member Definitions */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
bool SHBoundingBox::TestPoint(const SHVec3& point) noexcept bool SHBox::TestPoint(const SHVec3& point) noexcept
{ {
return BoundingBox::Contains(point); return BoundingBox::Contains(point);
} }
bool SHBoundingBox::Raycast(const SHRay& ray, float& distance) noexcept bool SHBox::Raycast(const SHRay& ray, float& distance) noexcept
{ {
return BoundingBox::Intersects(ray.position, ray.direction, distance); return BoundingBox::Intersects(ray.position, ray.direction, distance);
} }
bool SHBoundingBox::Contains(const SHBoundingBox& rhs) const noexcept bool SHBox::Contains(const SHBox& rhs) const noexcept
{ {
return BoundingBox::Contains(rhs); return BoundingBox::Contains(rhs);
} }
float SHBoundingBox::Volume() const noexcept float SHBox::Volume() const noexcept
{ {
return 8.0f * (Extents.x * Extents.y * Extents.z); return 8.0f * (Extents.x * Extents.y * Extents.z);
} }
float SHBoundingBox::SurfaceArea() const noexcept float SHBox::SurfaceArea() const noexcept
{ {
return 8.0f * ((Extents.x * Extents.y) return 8.0f * ((Extents.x * Extents.y)
+ (Extents.x * Extents.z) + (Extents.x * Extents.z)
@ -209,21 +209,21 @@ namespace SHADE
/* Static Function Member Definitions */ /* Static Function Member Definitions */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
SHBoundingBox SHBoundingBox::Combine(const SHBoundingBox& lhs, const SHBoundingBox& rhs) noexcept SHBox SHBox::Combine(const SHBox& lhs, const SHBox& rhs) noexcept
{ {
SHBoundingBox result; SHBox result;
CreateMerged(result, lhs, rhs); CreateMerged(result, lhs, rhs);
return result; return result;
} }
bool SHBoundingBox::Intersect(const SHBoundingBox& lhs, const SHBoundingBox& rhs) noexcept bool SHBox::Intersect(const SHBox& lhs, const SHBox& rhs) noexcept
{ {
return lhs.Intersects(rhs); return lhs.Intersects(rhs);
} }
SHBoundingBox SHBoundingBox::BuildFromBoxes(const SHBoundingBox* boxes, size_t numBoxes) noexcept SHBox SHBox::BuildFromBoxes(const SHBox* boxes, size_t numBoxes) noexcept
{ {
SHBoundingBox result; SHBox result;
for (size_t i = 1; i < numBoxes; ++i) for (size_t i = 1; i < numBoxes; ++i)
CreateMerged(result, boxes[i - 1], boxes[i]); CreateMerged(result, boxes[i - 1], boxes[i]);
@ -231,9 +231,9 @@ namespace SHADE
return result; return result;
} }
SHBoundingBox SHBoundingBox::BuildFromVertices(const SHVec3* vertices, size_t numVertices, size_t stride) noexcept SHBox SHBox::BuildFromVertices(const SHVec3* vertices, size_t numVertices, size_t stride) noexcept
{ {
SHBoundingBox result; SHBox result;
CreateFromPoints(result, numVertices, vertices, stride); CreateFromPoints(result, numVertices, vertices, stride);
return result; return result;
} }

View File

@ -22,8 +22,8 @@ namespace SHADE
/* Type Definitions */ /* Type Definitions */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
class SH_API SHBoundingBox : public SHShape, class SH_API SHBox : public SHShape,
private DirectX::BoundingBox private DirectX::BoundingBox
{ {
public: public:
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
@ -36,19 +36,19 @@ namespace SHADE
/* Constructors & Destructor */ /* Constructors & Destructor */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
~SHBoundingBox () override = default; ~SHBox () override = default;
SHBoundingBox () noexcept; SHBox () noexcept;
SHBoundingBox (const SHVec3& center, const SHVec3& halfExtents) noexcept; SHBox (const SHVec3& center, const SHVec3& halfExtents) noexcept;
SHBoundingBox (const SHBoundingBox& rhs) noexcept; SHBox (const SHBox& rhs) noexcept;
SHBoundingBox (SHBoundingBox&& rhs) noexcept; SHBox (SHBox&& rhs) noexcept;
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* Operator Overloads */ /* Operator Overloads */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
SHBoundingBox& operator= (const SHBoundingBox& rhs) noexcept; SHBox& operator= (const SHBox& rhs) noexcept;
SHBoundingBox& operator= (SHBoundingBox&& rhs) noexcept; SHBox& operator= (SHBox&& rhs) noexcept;
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* Getter Functions */ /* Getter Functions */
@ -79,7 +79,7 @@ namespace SHADE
[[nodiscard]] bool TestPoint (const SHVec3& point) noexcept override; [[nodiscard]] bool TestPoint (const SHVec3& point) noexcept override;
[[nodiscard]] bool Raycast (const SHRay& ray, float& distance) noexcept override; [[nodiscard]] bool Raycast (const SHRay& ray, float& distance) noexcept override;
[[nodiscard]] bool Contains (const SHBoundingBox& rhs) const noexcept; [[nodiscard]] bool Contains (const SHBox& rhs) const noexcept;
[[nodiscard]] float Volume () const noexcept; [[nodiscard]] float Volume () const noexcept;
[[nodiscard]] float SurfaceArea () const noexcept; [[nodiscard]] float SurfaceArea () const noexcept;
@ -87,10 +87,10 @@ namespace SHADE
/* Static Function Members */ /* Static Function Members */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
[[nodiscard]] static SHBoundingBox Combine (const SHBoundingBox& lhs, const SHBoundingBox& rhs) noexcept; [[nodiscard]] static SHBox Combine (const SHBox& lhs, const SHBox& rhs) noexcept;
[[nodiscard]] static bool Intersect (const SHBoundingBox& lhs, const SHBoundingBox& rhs) noexcept; [[nodiscard]] static bool Intersect (const SHBox& lhs, const SHBox& rhs) noexcept;
[[nodiscard]] static SHBoundingBox BuildFromBoxes (const SHBoundingBox* boxes, size_t numBoxes) noexcept; [[nodiscard]] static SHBox BuildFromBoxes (const SHBox* boxes, size_t numBoxes) noexcept;
[[nodiscard]] static SHBoundingBox BuildFromVertices (const SHVec3* vertices, size_t numVertices, size_t stride = 0) noexcept; [[nodiscard]] static SHBox BuildFromVertices (const SHVec3* vertices, size_t numVertices, size_t stride = 0) noexcept;
private: private:
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/

View File

@ -11,7 +11,7 @@
#include <SHpch.h> #include <SHpch.h>
// Primary Header // Primary Header
#include "SHBoundingSphere.h" #include "SHSphere.h"
// Project Headers // Project Headers
#include "Math/SHMathHelpers.h" #include "Math/SHMathHelpers.h"
#include "Math/SHRay.h" #include "Math/SHRay.h"
@ -24,13 +24,13 @@ namespace SHADE
/* Constructors & Destructor Definitions */ /* Constructors & Destructor Definitions */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
SHBoundingSphere::SHBoundingSphere() noexcept SHSphere::SHSphere() noexcept
: RelativeRadius { 1.0f } : RelativeRadius { 1.0f }
{ {
type = Type::SPHERE; type = Type::SPHERE;
} }
SHBoundingSphere::SHBoundingSphere(const SHVec3& center, float radius) noexcept SHSphere::SHSphere(const SHVec3& center, float radius) noexcept
: RelativeRadius { 1.0f } : RelativeRadius { 1.0f }
{ {
type = Type::SPHERE; type = Type::SPHERE;
@ -39,7 +39,7 @@ namespace SHADE
Radius = radius; Radius = radius;
} }
SHBoundingSphere::SHBoundingSphere(const SHBoundingSphere& rhs) noexcept SHSphere::SHSphere(const SHSphere& rhs) noexcept
{ {
if (this == &rhs) if (this == &rhs)
return; return;
@ -51,7 +51,7 @@ namespace SHADE
RelativeRadius = rhs.RelativeRadius; RelativeRadius = rhs.RelativeRadius;
} }
SHBoundingSphere::SHBoundingSphere(SHBoundingSphere&& rhs) noexcept SHSphere::SHSphere(SHSphere&& rhs) noexcept
{ {
type = Type::SPHERE; type = Type::SPHERE;
@ -64,7 +64,7 @@ namespace SHADE
/* Operator Overload Definitions */ /* Operator Overload Definitions */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
SHBoundingSphere& SHBoundingSphere::operator=(const SHBoundingSphere& rhs) noexcept SHSphere& SHSphere::operator=(const SHSphere& rhs) noexcept
{ {
if (rhs.type != Type::SPHERE) if (rhs.type != Type::SPHERE)
{ {
@ -80,7 +80,7 @@ namespace SHADE
return *this; return *this;
} }
SHBoundingSphere& SHBoundingSphere::operator=(SHBoundingSphere&& rhs) noexcept SHSphere& SHSphere::operator=(SHSphere&& rhs) noexcept
{ {
if (rhs.type != Type::SPHERE) if (rhs.type != Type::SPHERE)
{ {
@ -100,17 +100,17 @@ namespace SHADE
/* Getter Function Definitions */ /* Getter Function Definitions */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
SHVec3 SHBoundingSphere::GetCenter() const noexcept SHVec3 SHSphere::GetCenter() const noexcept
{ {
return Center; return Center;
} }
float SHBoundingSphere::GetWorldRadius() const noexcept float SHSphere::GetWorldRadius() const noexcept
{ {
return Radius; return Radius;
} }
float SHBoundingSphere::GetRelativeRadius() const noexcept float SHSphere::GetRelativeRadius() const noexcept
{ {
return RelativeRadius; return RelativeRadius;
} }
@ -119,17 +119,17 @@ namespace SHADE
/* Setter Function Definitions */ /* Setter Function Definitions */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
void SHBoundingSphere::SetCenter(const SHVec3& center) noexcept void SHSphere::SetCenter(const SHVec3& center) noexcept
{ {
Center = center; Center = center;
} }
void SHBoundingSphere::SetWorldRadius(float newWorldRadius) noexcept void SHSphere::SetWorldRadius(float newWorldRadius) noexcept
{ {
Radius = newWorldRadius; Radius = newWorldRadius;
} }
void SHBoundingSphere::SetRelativeRadius(float newRelativeRadius) noexcept void SHSphere::SetRelativeRadius(float newRelativeRadius) noexcept
{ {
RelativeRadius = newRelativeRadius; RelativeRadius = newRelativeRadius;
} }
@ -138,27 +138,27 @@ namespace SHADE
/* Public Function Member Definitions */ /* Public Function Member Definitions */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
bool SHBoundingSphere::TestPoint(const SHVec3& point) noexcept bool SHSphere::TestPoint(const SHVec3& point) noexcept
{ {
return BoundingSphere::Contains(point); return BoundingSphere::Contains(point);
} }
bool SHBoundingSphere::Raycast(const SHRay& ray, float& distance) noexcept bool SHSphere::Raycast(const SHRay& ray, float& distance) noexcept
{ {
return Intersects(ray.position, ray.direction, distance); return Intersects(ray.position, ray.direction, distance);
} }
bool SHBoundingSphere::Contains(const SHBoundingSphere& rhs) const noexcept bool SHSphere::Contains(const SHSphere& rhs) const noexcept
{ {
return BoundingSphere::Contains(rhs); return BoundingSphere::Contains(rhs);
} }
float SHBoundingSphere::Volume() const noexcept float SHSphere::Volume() const noexcept
{ {
return (4.0f / 3.0f) * SHMath::PI * (Radius * Radius * Radius); return (4.0f / 3.0f) * SHMath::PI * (Radius * Radius * Radius);
} }
float SHBoundingSphere::SurfaceArea() const noexcept float SHSphere::SurfaceArea() const noexcept
{ {
return 4.0f * SHMath::PI * (Radius * Radius); return 4.0f * SHMath::PI * (Radius * Radius);
} }
@ -167,21 +167,21 @@ namespace SHADE
/* Static Function Member Definitions */ /* Static Function Member Definitions */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
SHBoundingSphere SHBoundingSphere::Combine(const SHBoundingSphere& lhs, const SHBoundingSphere& rhs) noexcept SHSphere SHSphere::Combine(const SHSphere& lhs, const SHSphere& rhs) noexcept
{ {
SHBoundingSphere result; SHSphere result;
CreateMerged(result, lhs, rhs); CreateMerged(result, lhs, rhs);
return result; return result;
} }
bool SHBoundingSphere::Intersect(const SHBoundingSphere& lhs, const SHBoundingSphere& rhs) noexcept bool SHSphere::Intersect(const SHSphere& lhs, const SHSphere& rhs) noexcept
{ {
return lhs.Intersects(rhs); return lhs.Intersects(rhs);
} }
SHBoundingSphere SHBoundingSphere::BuildFromSpheres(const SHBoundingSphere* spheres, size_t numSpheres) noexcept SHSphere SHSphere::BuildFromSpheres(const SHSphere* spheres, size_t numSpheres) noexcept
{ {
SHBoundingSphere result; SHSphere result;
for (size_t i = 1; i < numSpheres; ++i) for (size_t i = 1; i < numSpheres; ++i)
CreateMerged(result, spheres[i - 1], spheres[i]); CreateMerged(result, spheres[i - 1], spheres[i]);
@ -189,9 +189,9 @@ namespace SHADE
return result; return result;
} }
SHBoundingSphere SHBoundingSphere::BuildFromVertices(const SHVec3* vertices, size_t numVertices, size_t stride) noexcept SHSphere SHSphere::BuildFromVertices(const SHVec3* vertices, size_t numVertices, size_t stride) noexcept
{ {
SHBoundingSphere result; SHSphere result;
CreateFromPoints(result, numVertices, vertices, stride); CreateFromPoints(result, numVertices, vertices, stride);
return result; return result;
} }

View File

@ -22,27 +22,27 @@ namespace SHADE
/* Type Definitions */ /* Type Definitions */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
class SH_API SHBoundingSphere : public SHShape, class SH_API SHSphere : public SHShape,
private DirectX::BoundingSphere private DirectX::BoundingSphere
{ {
public: public:
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* Constructors & Destructor */ /* Constructors & Destructor */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
SHBoundingSphere () noexcept; SHSphere () noexcept;
SHBoundingSphere (const SHVec3& center, float radius) noexcept; SHSphere (const SHVec3& center, float radius) noexcept;
SHBoundingSphere (const SHBoundingSphere& rhs) noexcept; SHSphere (const SHSphere& rhs) noexcept;
SHBoundingSphere (SHBoundingSphere&& rhs) noexcept; SHSphere (SHSphere&& rhs) noexcept;
~SHBoundingSphere () override = default; ~SHSphere () override = default;
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* Operator Overloads */ /* Operator Overloads */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
SHBoundingSphere& operator= (const SHBoundingSphere& rhs) noexcept; SHSphere& operator= (const SHSphere& rhs) noexcept;
SHBoundingSphere& operator= (SHBoundingSphere&& rhs) noexcept; SHSphere& operator= (SHSphere&& rhs) noexcept;
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* Getter Functions */ /* Getter Functions */
@ -67,7 +67,7 @@ namespace SHADE
[[nodiscard]] bool TestPoint (const SHVec3& point) noexcept override; [[nodiscard]] bool TestPoint (const SHVec3& point) noexcept override;
[[nodiscard]] bool Raycast (const SHRay& ray, float& distance) noexcept override; [[nodiscard]] bool Raycast (const SHRay& ray, float& distance) noexcept override;
[[nodiscard]] bool Contains (const SHBoundingSphere& rhs) const noexcept; [[nodiscard]] bool Contains (const SHSphere& rhs) const noexcept;
[[nodiscard]] float Volume () const noexcept; [[nodiscard]] float Volume () const noexcept;
[[nodiscard]] float SurfaceArea () const noexcept; [[nodiscard]] float SurfaceArea () const noexcept;
@ -76,10 +76,10 @@ namespace SHADE
/* Static Function Members */ /* Static Function Members */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
[[nodiscard]] static SHBoundingSphere Combine (const SHBoundingSphere& lhs, const SHBoundingSphere& rhs) noexcept; [[nodiscard]] static SHSphere Combine (const SHSphere& lhs, const SHSphere& rhs) noexcept;
[[nodiscard]] static bool Intersect (const SHBoundingSphere& lhs, const SHBoundingSphere& rhs) noexcept; [[nodiscard]] static bool Intersect (const SHSphere& lhs, const SHSphere& rhs) noexcept;
[[nodiscard]] static SHBoundingSphere BuildFromSpheres (const SHBoundingSphere* spheres, size_t numSpheres) noexcept; [[nodiscard]] static SHSphere BuildFromSpheres (const SHSphere* spheres, size_t numSpheres) noexcept;
[[nodiscard]] static SHBoundingSphere BuildFromVertices (const SHVec3* vertices, size_t numVertices, size_t stride = 0) noexcept; [[nodiscard]] static SHSphere BuildFromVertices (const SHVec3* vertices, size_t numVertices, size_t stride = 0) noexcept;
private: private:
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/

View File

@ -95,7 +95,7 @@ namespace SHADE
{ {
case SHCollisionShape::Type::BOX: case SHCollisionShape::Type::BOX:
{ {
auto* box = reinterpret_cast<SHBoundingBox*>(collisionShape.shape); auto* box = reinterpret_cast<SHBox*>(collisionShape.shape);
const SHVec3& RELATIVE_EXTENTS = box->GetRelativeExtents(); const SHVec3& RELATIVE_EXTENTS = box->GetRelativeExtents();
// Recompute world extents based on new scale and fixed relative extents // Recompute world extents based on new scale and fixed relative extents
@ -106,7 +106,7 @@ namespace SHADE
} }
case SHCollisionShape::Type::SPHERE: case SHCollisionShape::Type::SPHERE:
{ {
auto* sphere = reinterpret_cast<SHBoundingSphere*>(collisionShape.shape); auto* sphere = reinterpret_cast<SHSphere*>(collisionShape.shape);
const float RELATIVE_RADIUS = sphere->GetRelativeRadius(); const float RELATIVE_RADIUS = sphere->GetRelativeRadius();
// Recompute world radius based on new scale and fixed radius // Recompute world radius based on new scale and fixed radius

View File

@ -14,8 +14,8 @@
// Project Headers // Project Headers
#include "ECS_Base/Components/SHComponent.h" #include "ECS_Base/Components/SHComponent.h"
#include "Math/Geometry/SHBoundingBox.h" #include "Math/Geometry/SHBox.h"
#include "Math/Geometry/SHBoundingSphere.h" #include "Math/Geometry/SHSphere.h"
#include "SHCollisionShape.h" #include "SHCollisionShape.h"
//namespace SHADE //namespace SHADE

View File

@ -13,8 +13,8 @@
// Primary Header // Primary Header
#include "SHCollisionShape.h" #include "SHCollisionShape.h"
// Project Headers // Project Headers
#include "Math/Geometry/SHBoundingBox.h" #include "Math/Geometry/SHBox.h"
#include "Math/Geometry/SHBoundingSphere.h" #include "Math/Geometry/SHSphere.h"
#include "Math/SHMathHelpers.h" #include "Math/SHMathHelpers.h"
#include "Reflection/SHReflectionMetadata.h" #include "Reflection/SHReflectionMetadata.h"
#include "SHColliderComponent.h" #include "SHColliderComponent.h"
@ -37,12 +37,12 @@ namespace SHADE
{ {
case Type::BOX: case Type::BOX:
{ {
shape = new SHBoundingBox{ SHVec3::Zero, SHVec3::One }; shape = new SHBox{ SHVec3::Zero, SHVec3::One };
break; break;
} }
case Type::SPHERE: case Type::SPHERE:
{ {
shape = new SHBoundingSphere{ SHVec3::Zero, 0.5f }; shape = new SHSphere{ SHVec3::Zero, 0.5f };
break; break;
} }
default: break; default: break;
@ -177,30 +177,48 @@ namespace SHADE
{ {
dirty = true; dirty = true;
const auto* colliderComponent = SHComponentManager::GetComponent<SHColliderComponent>(entityID); const auto* COLLIDER = SHComponentManager::GetComponent<SHColliderComponent>(entityID);
auto* box = reinterpret_cast<SHBox*>(shape);
SHVec3 correctedHalfExtents = halfExtents;
// Get current relative halfExtents for error checking. 0 is ignored
const SHVec3& CURRENT_RELATIVE_EXTENTS = box->GetRelativeExtents();
for (size_t i = 0; i < SHVec3::SIZE; ++i)
{
if (SHMath::CompareFloat(halfExtents[i], 0.0f))
correctedHalfExtents[i] = CURRENT_RELATIVE_EXTENTS[i];
}
// Set the half extents relative to world scale // Set the half extents relative to world scale
const SHVec3 WORLD_EXTENTS = halfExtents * colliderComponent->GetScale() * 0.5f; const SHVec3 WORLD_EXTENTS = correctedHalfExtents * COLLIDER->GetScale() * 0.5f;
if (type != Type::BOX) if (type != Type::BOX)
{ {
type = Type::BOX; type = Type::BOX;
delete shape; delete shape;
shape = new SHBoundingBox{ positionOffset, WORLD_EXTENTS }; shape = new SHBox{ positionOffset, WORLD_EXTENTS };
} }
auto* box = reinterpret_cast<SHBoundingBox*>(shape);
box->SetWorldExtents(WORLD_EXTENTS); box->SetWorldExtents(WORLD_EXTENTS);
box->SetRelativeExtents(halfExtents); box->SetRelativeExtents(correctedHalfExtents);
} }
void SHCollisionShape::SetBoundingSphere(float radius) void SHCollisionShape::SetBoundingSphere(float radius)
{ {
dirty = true; dirty = true;
const auto* colliderComponent = SHComponentManager::GetComponent<SHColliderComponent>(entityID); auto* sphere = reinterpret_cast<SHSphere*>(shape);
const auto* COLLIDER = SHComponentManager::GetComponent<SHColliderComponent>(entityID);
// Get current relative halfExtents for error checking. 0 is ignored
const float CURRENT_RELATIVE_RADIUS = sphere->GetRelativeRadius();
if (SHMath::CompareFloat(radius, 0.0f))
radius = CURRENT_RELATIVE_RADIUS;
// Set the radius relative to world scale // Set the radius relative to world scale
const SHVec3 WORLD_SCALE = colliderComponent->GetScale(); const SHVec3 WORLD_SCALE = COLLIDER->GetScale();
const float MAX_SCALE = SHMath::Max({ WORLD_SCALE.x, WORLD_SCALE.y, WORLD_SCALE.z }); const float MAX_SCALE = SHMath::Max({ WORLD_SCALE.x, WORLD_SCALE.y, WORLD_SCALE.z });
const float WORLD_RADIUS = radius * MAX_SCALE * 0.5f; const float WORLD_RADIUS = radius * MAX_SCALE * 0.5f;
@ -209,11 +227,11 @@ namespace SHADE
type = Type::SPHERE; type = Type::SPHERE;
delete shape; delete shape;
shape = new SHBoundingSphere{ positionOffset, WORLD_RADIUS }; shape = new SHSphere{ positionOffset, WORLD_RADIUS };
} }
auto* sphere = reinterpret_cast<SHBoundingSphere*>(shape);
sphere->SetWorldRadius(WORLD_RADIUS); sphere->SetWorldRadius(WORLD_RADIUS);
sphere->SetRelativeRadius(radius);
} }
void SHCollisionShape::SetIsTrigger(bool trigger) noexcept void SHCollisionShape::SetIsTrigger(bool trigger) noexcept
@ -255,12 +273,12 @@ namespace SHADE
{ {
case Type::BOX: case Type::BOX:
{ {
reinterpret_cast<SHBoundingBox*>(shape)->SetCenter(positionOffset); reinterpret_cast<SHBox*>(shape)->SetCenter(positionOffset);
break; break;
} }
case Type::SPHERE: case Type::SPHERE:
{ {
reinterpret_cast<SHBoundingSphere*>(shape)->SetCenter(positionOffset); reinterpret_cast<SHSphere*>(shape)->SetCenter(positionOffset);
break; break;
} }
default: break; default: break;
@ -283,16 +301,16 @@ namespace SHADE
{ {
case Type::BOX: case Type::BOX:
{ {
const auto* RHS_BOX = reinterpret_cast<const SHBoundingBox*>(rhs); const auto* RHS_BOX = reinterpret_cast<const SHBox*>(rhs);
shape = new SHBoundingBox{ positionOffset, RHS_BOX->GetWorldExtents() }; shape = new SHBox{ positionOffset, RHS_BOX->GetWorldExtents() };
break; break;
} }
case Type::SPHERE: case Type::SPHERE:
{ {
const auto* RHS_SPHERE = reinterpret_cast<const SHBoundingSphere*>(rhs); const auto* RHS_SPHERE = reinterpret_cast<const SHSphere*>(rhs);
shape = new SHBoundingSphere{ positionOffset, RHS_SPHERE->GetWorldRadius() }; shape = new SHSphere{ positionOffset, RHS_SPHERE->GetWorldRadius() };
break; break;
} }
default: break; default: break;

View File

@ -44,7 +44,7 @@ namespace SHADE
&& SHMath::CompareFloat(density, rhs.density); && SHMath::CompareFloat(density, rhs.density);
} }
bool SHPhysicsMaterial::operator!=(const SHPhysicsMaterial& rhs) const noexcept bool SHPhysicsMaterial::operator!=(const SHPhysicsMaterial& rhs) const noexcept
{ {
return !SHMath::CompareFloat(friction, rhs.friction) return !SHMath::CompareFloat(friction, rhs.friction)
|| !SHMath::CompareFloat(bounciness, rhs.bounciness) || !SHMath::CompareFloat(bounciness, rhs.bounciness)

View File

@ -302,6 +302,9 @@ namespace SHADE
{ {
static constexpr int FLAG_POS = 9; static constexpr int FLAG_POS = 9;
if (newMass < 0.0f)
return;
if (type != Type::DYNAMIC) if (type != Type::DYNAMIC)
{ {
SHLOG_WARNING("Cannot set mass of a non-dynamic object {}", GetEID()) SHLOG_WARNING("Cannot set mass of a non-dynamic object {}", GetEID())
@ -385,42 +388,62 @@ namespace SHADE
void SHRigidBodyComponent::AddForce(const SHVec3& force) const noexcept void SHRigidBodyComponent::AddForce(const SHVec3& force) const noexcept
{ {
system->AddForce(GetEID(), force); if (auto* physicsObject = system->GetPhysicsObject(GetEID()); physicsObject)
physicsObject->GetRigidBody()->applyWorldForceAtCenterOfMass(force);
} }
void SHRigidBodyComponent::AddForceAtLocalPos(const SHVec3& force, const SHVec3& localPos) const noexcept void SHRigidBodyComponent::AddForceAtLocalPos(const SHVec3& force, const SHVec3& localPos) const noexcept
{ {
system->AddForceAtLocalPos(GetEID(), force, localPos); if (auto* physicsObject = system->GetPhysicsObject(GetEID()); physicsObject)
physicsObject->GetRigidBody()->applyWorldForceAtLocalPosition(force, localPos);
} }
void SHRigidBodyComponent::AddForceAtWorldPos(const SHVec3& force, const SHVec3& worldPos) const noexcept void SHRigidBodyComponent::AddForceAtWorldPos(const SHVec3& force, const SHVec3& worldPos) const noexcept
{ {
system->AddForceAtWorldPos(GetEID(), force, worldPos); if (auto* physicsObject = system->GetPhysicsObject(GetEID()); physicsObject)
physicsObject->GetRigidBody()->applyWorldForceAtWorldPosition(force, worldPos);
} }
void SHRigidBodyComponent::AddRelativeForce(const SHVec3& relativeForce) const noexcept void SHRigidBodyComponent::AddRelativeForce(const SHVec3& relativeForce) const noexcept
{ {
system->AddRelativeForce(GetEID(), relativeForce); if (auto* physicsObject = system->GetPhysicsObject(GetEID()); physicsObject)
physicsObject->GetRigidBody()->applyLocalForceAtCenterOfMass(relativeForce);
} }
void SHRigidBodyComponent::AddRelativeForceAtLocalPos(const SHVec3& relativeForce, const SHVec3& localPos) const noexcept void SHRigidBodyComponent::AddRelativeForceAtLocalPos(const SHVec3& relativeForce, const SHVec3& localPos) const noexcept
{ {
system->AddRelativeForceAtLocalPos(GetEID(), relativeForce, localPos); if (auto* physicsObject = system->GetPhysicsObject(GetEID()); physicsObject)
physicsObject->GetRigidBody()->applyLocalForceAtLocalPosition(relativeForce, localPos);
} }
void SHRigidBodyComponent::AddRelativeForceAtWorldPos(const SHVec3& relativeForce, const SHVec3& worldPos) const noexcept void SHRigidBodyComponent::AddRelativeForceAtWorldPos(const SHVec3& relativeForce, const SHVec3& worldPos) const noexcept
{ {
system->AddRelativeForceAtWorldPos(GetEID(), relativeForce, worldPos); if (auto* physicsObject = system->GetPhysicsObject(GetEID()); physicsObject)
physicsObject->GetRigidBody()->applyLocalForceAtWorldPosition(relativeForce, worldPos);
} }
void SHRigidBodyComponent::AddTorque(const SHVec3& torque) const noexcept void SHRigidBodyComponent::AddTorque(const SHVec3& torque) const noexcept
{ {
system->AddTorque(GetEID(), torque); if (auto* physicsObject = system->GetPhysicsObject(GetEID()); physicsObject)
physicsObject->GetRigidBody()->applyWorldTorque(torque);
} }
void SHRigidBodyComponent::AddRelativeTorque(const SHVec3& relativeTorque) const noexcept void SHRigidBodyComponent::AddRelativeTorque(const SHVec3& relativeTorque) const noexcept
{ {
system->AddRelativeTorque(GetEID(), relativeTorque); if (auto* physicsObject = system->GetPhysicsObject(GetEID()); physicsObject)
physicsObject->GetRigidBody()->applyLocalTorque(relativeTorque);
}
void SHRigidBodyComponent::ClearForces() const noexcept
{
if (auto* physicsObject = system->GetPhysicsObject(GetEID()); physicsObject)
physicsObject->GetRigidBody()->resetForce();
}
void SHRigidBodyComponent::ClearTorque() const noexcept
{
if (auto* physicsObject = system->GetPhysicsObject(GetEID()); physicsObject)
physicsObject->GetRigidBody()->resetTorque();
} }
} // namespace SHADE } // namespace SHADE

View File

@ -125,14 +125,15 @@ namespace SHADE
void AddForce (const SHVec3& force) const noexcept; void AddForce (const SHVec3& force) const noexcept;
void AddForceAtLocalPos (const SHVec3& force, const SHVec3& localPos) const noexcept; void AddForceAtLocalPos (const SHVec3& force, const SHVec3& localPos) const noexcept;
void AddForceAtWorldPos (const SHVec3& force, const SHVec3& worldPos) const noexcept; void AddForceAtWorldPos (const SHVec3& force, const SHVec3& worldPos) const noexcept;
void AddRelativeForce (const SHVec3& relativeForce) const noexcept; void AddRelativeForce (const SHVec3& relativeForce) const noexcept;
void AddRelativeForceAtLocalPos (const SHVec3& relativeForce, const SHVec3& localPos) const noexcept; void AddRelativeForceAtLocalPos (const SHVec3& relativeForce, const SHVec3& localPos) const noexcept;
void AddRelativeForceAtWorldPos (const SHVec3& relativeForce, const SHVec3& worldPos) const noexcept; void AddRelativeForceAtWorldPos (const SHVec3& relativeForce, const SHVec3& worldPos) const noexcept;
void AddTorque (const SHVec3& torque) const noexcept; void AddTorque (const SHVec3& torque) const noexcept;
void AddRelativeTorque (const SHVec3& relativeTorque) const noexcept; void AddRelativeTorque (const SHVec3& relativeTorque) const noexcept;
void ClearForces () const noexcept;
void ClearTorque () const noexcept;
private: private:
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* Data Members */ /* Data Members */

View File

@ -16,6 +16,7 @@
// Project Headers // Project Headers
#include "ECS_Base/Managers/SHSystemManager.h" #include "ECS_Base/Managers/SHSystemManager.h"
#include "ECS_Base/Managers/SHComponentManager.h" #include "ECS_Base/Managers/SHComponentManager.h"
#include "Scene/SHSceneManager.h"
namespace SHADE namespace SHADE
@ -25,10 +26,11 @@ namespace SHADE
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
SHPhysicsObject::SHPhysicsObject(EntityID eid, rp3d::PhysicsCommon* physicsFactory, rp3d::PhysicsWorld* physicsWorld) noexcept SHPhysicsObject::SHPhysicsObject(EntityID eid, rp3d::PhysicsCommon* physicsFactory, rp3d::PhysicsWorld* physicsWorld) noexcept
: entityID { eid } : entityID { eid }
, factory { physicsFactory } , collidersActive { true }
, world { physicsWorld } , factory { physicsFactory }
, rp3dBody { nullptr } , world { physicsWorld }
, rp3dBody { nullptr }
{ {
// Implicitly create a static body. // Implicitly create a static body.
@ -148,6 +150,10 @@ namespace SHADE
void SHPhysicsObject::SyncRigidBody(SHRigidBodyComponent& component) const noexcept void SHPhysicsObject::SyncRigidBody(SHRigidBodyComponent& component) const noexcept
{ {
// This state is synced in the pre-update routine
if (!rp3dBody->isActive())
return;
if (component.dirtyFlags == 0) if (component.dirtyFlags == 0)
return; return;
@ -267,23 +273,28 @@ namespace SHADE
void SHPhysicsObject::SyncColliders(SHColliderComponent& component) const noexcept void SHPhysicsObject::SyncColliders(SHColliderComponent& component) const noexcept
{ {
int index = 0; // This state is synced in the pre-update routine
for (auto& collisionShape : component.collisionShapes) if (!rp3dBody->isActive())
return;
const int NUM_SHAPES = static_cast<int>(component.collisionShapes.size());
for (int i = 0; i < NUM_SHAPES; ++i)
{ {
auto& collisionShape = component.collisionShapes[i];
if (!collisionShape.dirty) if (!collisionShape.dirty)
continue; continue;
switch (collisionShape.GetType()) switch (collisionShape.GetType())
{ {
case SHCollisionShape::Type::BOX: syncBoxShape(index, collisionShape); break; case SHCollisionShape::Type::BOX: syncBoxShape(i, collisionShape); break;
case SHCollisionShape::Type::SPHERE: syncSphereShape(index, collisionShape); break; case SHCollisionShape::Type::SPHERE: syncSphereShape(i, collisionShape); break;
default: break; default: break;
} }
// TODO(Diren): Update Material syncMaterial(i, collisionShape);
collisionShape.dirty = false; collisionShape.dirty = false;
++index;
} }
} }
@ -291,6 +302,14 @@ namespace SHADE
/* Private Function Member Definitions */ /* Private Function Member Definitions */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
void SHPhysicsObject::syncMaterial(int colliderIndex, SHCollisionShape& collisionShape) const noexcept
{
auto& rp3dMaterial = rp3dBody->getCollider(colliderIndex)->getMaterial();
rp3dMaterial.setFrictionCoefficient(collisionShape.GetFriction());
rp3dMaterial.setBounciness(collisionShape.GetBounciness());
rp3dMaterial.setMassDensity(collisionShape.GetDensity());
}
void SHPhysicsObject::addBoxShape(SHCollisionShape& boxShape) const noexcept void SHPhysicsObject::addBoxShape(SHCollisionShape& boxShape) const noexcept
{ {
const rp3d::Transform OFFSETS const rp3d::Transform OFFSETS
@ -299,7 +318,7 @@ namespace SHADE
, boxShape.GetRotationOffset() , boxShape.GetRotationOffset()
}; };
const auto* BOX = reinterpret_cast<const SHBoundingBox*>(boxShape.GetShape()); const auto* BOX = reinterpret_cast<const SHBox*>(boxShape.GetShape());
rp3d::BoxShape* newBox = factory->createBoxShape(BOX->GetWorldExtents()); rp3d::BoxShape* newBox = factory->createBoxShape(BOX->GetWorldExtents());
rp3dBody->addCollider(newBox, OFFSETS); rp3dBody->addCollider(newBox, OFFSETS);
@ -307,7 +326,7 @@ namespace SHADE
void SHPhysicsObject::syncBoxShape(int index, SHCollisionShape& boxShape) const noexcept void SHPhysicsObject::syncBoxShape(int index, SHCollisionShape& boxShape) const noexcept
{ {
const auto* BOX = reinterpret_cast<const SHBoundingBox*>(boxShape.GetShape()); const auto* BOX = reinterpret_cast<const SHBox*>(boxShape.GetShape());
auto* rp3dCollider = rp3dBody->getCollider(index); auto* rp3dCollider = rp3dBody->getCollider(index);
auto* rp3dBox = reinterpret_cast<rp3d::BoxShape*>(rp3dCollider->getCollisionShape()); auto* rp3dBox = reinterpret_cast<rp3d::BoxShape*>(rp3dCollider->getCollisionShape());
@ -332,7 +351,7 @@ namespace SHADE
, sphereShape.GetRotationOffset() , sphereShape.GetRotationOffset()
}; };
const auto* SPHERE = reinterpret_cast<const SHBoundingSphere*>(sphereShape.GetShape()); const auto* SPHERE = reinterpret_cast<const SHSphere*>(sphereShape.GetShape());
rp3d::SphereShape* newSphere = factory->createSphereShape(SPHERE->GetWorldRadius()); rp3d::SphereShape* newSphere = factory->createSphereShape(SPHERE->GetWorldRadius());
rp3dBody->addCollider(newSphere, OFFSETS); rp3dBody->addCollider(newSphere, OFFSETS);
@ -340,7 +359,7 @@ namespace SHADE
void SHPhysicsObject::syncSphereShape(int index, SHCollisionShape& sphereShape) const noexcept void SHPhysicsObject::syncSphereShape(int index, SHCollisionShape& sphereShape) const noexcept
{ {
const auto* SPHERE = reinterpret_cast<const SHBoundingSphere*>(sphereShape.GetShape()); const auto* SPHERE = reinterpret_cast<const SHSphere*>(sphereShape.GetShape());
auto* rp3dCollider = rp3dBody->getCollider(index); auto* rp3dCollider = rp3dBody->getCollider(index);
auto* rp3dSphere = reinterpret_cast<rp3d::SphereShape*>(rp3dCollider->getCollisionShape()); auto* rp3dSphere = reinterpret_cast<rp3d::SphereShape*>(rp3dCollider->getCollisionShape());

View File

@ -84,17 +84,20 @@ namespace SHADE
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
EntityID entityID; EntityID entityID;
bool collidersActive; // Only used to sync with SHADE components
rp3d::PhysicsCommon* factory; rp3d::PhysicsCommon* factory;
rp3d::PhysicsWorld* world; rp3d::PhysicsWorld* world;
rp3d::RigidBody* rp3dBody; rp3d::RigidBody* rp3dBody;
rp3d::Transform prevTransform; // Cached transform for interpolation rp3d::Transform prevTransform; // Cached transform for interpolation
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* Function Members */ /* Function Members */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
void syncMaterial (int colliderIndex, SHCollisionShape& collisionShape) const noexcept;
// Box Shapes // Box Shapes
void addBoxShape (SHCollisionShape& boxShape) const noexcept; void addBoxShape (SHCollisionShape& boxShape) const noexcept;

View File

@ -167,6 +167,7 @@ namespace SHADE
if (!COMPONENT_GROUP.rigidBodyComponent && !COMPONENT_GROUP.colliderComponent) if (!COMPONENT_GROUP.rigidBodyComponent && !COMPONENT_GROUP.colliderComponent)
{ {
destroyPhysicsObject(COMMAND.eid); destroyPhysicsObject(COMMAND.eid);
wakeAllObjects();
continue; continue;
} }
@ -176,6 +177,10 @@ namespace SHADE
physicsObject = createPhysicsObject(COMMAND.eid); physicsObject = createPhysicsObject(COMMAND.eid);
componentFunc[SHUtilities::ConvertEnum(COMMAND.command)][SHUtilities::ConvertEnum(COMMAND.component)](COMMAND, physicsObject, COMPONENT_GROUP); componentFunc[SHUtilities::ConvertEnum(COMMAND.command)][SHUtilities::ConvertEnum(COMMAND.component)](COMMAND, physicsObject, COMPONENT_GROUP);
// If any removal was done, wake all objects
if (COMMAND.command == QueueCommand::Command::REMOVE)
wakeAllObjects();
} }
} }
@ -297,5 +302,11 @@ namespace SHADE
physicsObject->RemoveCollisionShape(command.shapeIndex); physicsObject->RemoveCollisionShape(command.shapeIndex);
} }
void SHPhysicsObjectManager::wakeAllObjects() noexcept
{
for (auto& physicsObject : physicsObjects | std::views::values)
physicsObject.GetRigidBody()->setIsSleeping(false);
}
} // namespace SHADE } // namespace SHADE

View File

@ -165,6 +165,8 @@ namespace SHADE
SHPhysicsObject* createPhysicsObject (EntityID eid) noexcept; SHPhysicsObject* createPhysicsObject (EntityID eid) noexcept;
void destroyPhysicsObject (EntityID eid) noexcept; void destroyPhysicsObject (EntityID eid) noexcept;
void wakeAllObjects () noexcept;
static void addRigidBody (const QueueCommand& command, SHPhysicsObject* physicsObject, const PhysicsComponentGroup& componentGroup); static void addRigidBody (const QueueCommand& command, SHPhysicsObject* physicsObject, const PhysicsComponentGroup& componentGroup);
static void addCollider (const QueueCommand& command, SHPhysicsObject* physicsObject, const PhysicsComponentGroup& componentGroup); static void addCollider (const QueueCommand& command, SHPhysicsObject* physicsObject, const PhysicsComponentGroup& componentGroup);
static void removeRigidBody (const QueueCommand& command, SHPhysicsObject* physicsObject, const PhysicsComponentGroup& componentGroup); static void removeRigidBody (const QueueCommand& command, SHPhysicsObject* physicsObject, const PhysicsComponentGroup& componentGroup);
@ -172,6 +174,8 @@ namespace SHADE
static void addCollisionShape (const QueueCommand& command, SHPhysicsObject* physicsObject, const PhysicsComponentGroup& componentGroup); static void addCollisionShape (const QueueCommand& command, SHPhysicsObject* physicsObject, const PhysicsComponentGroup& componentGroup);
static void removeCollisionShape (const QueueCommand& command, SHPhysicsObject* physicsObject, const PhysicsComponentGroup& componentGroup); static void removeCollisionShape (const QueueCommand& command, SHPhysicsObject* physicsObject, const PhysicsComponentGroup& componentGroup);
}; };
} // namespace SHADE } // namespace SHADE

View File

@ -16,6 +16,7 @@
// Project Headers // Project Headers
#include "ECS_Base/Managers/SHSystemManager.h" #include "ECS_Base/Managers/SHSystemManager.h"
#include "Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h" #include "Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h"
#include "Scene/SHSceneManager.h"
namespace SHADE namespace SHADE
{ {
@ -122,6 +123,10 @@ namespace SHADE
const auto& COLLIDER_SET = SHComponentManager::GetDense<SHColliderComponent>(); const auto& COLLIDER_SET = SHComponentManager::GetDense<SHColliderComponent>();
for (const auto& COLLIDER : COLLIDER_SET) for (const auto& COLLIDER : COLLIDER_SET)
{ {
// Skip inactive colliders
if (!SHSceneManager::CheckNodeAndComponentsActive<SHColliderComponent>(COLLIDER.GetEID()))
continue;
for (auto& collisionShape : COLLIDER.GetCollisionShapes()) for (auto& collisionShape : COLLIDER.GetCollisionShapes())
{ {
switch (collisionShape.GetType()) switch (collisionShape.GetType())
@ -176,7 +181,7 @@ namespace SHADE
return; return;
} }
auto* BOX = reinterpret_cast<const SHBoundingBox*>(collisionShape.GetShape()); auto* BOX = reinterpret_cast<const SHBox*>(collisionShape.GetShape());
// Calculate final position & orientation // Calculate final position & orientation
const SHVec3 FINAL_POS = colliderComponent.GetPosition() + collisionShape.GetPositionOffset(); const SHVec3 FINAL_POS = colliderComponent.GetPosition() + collisionShape.GetPositionOffset();
@ -216,7 +221,7 @@ namespace SHADE
return; return;
} }
auto* SPHERE = reinterpret_cast<const SHBoundingSphere*>(collisionShape.GetShape()); auto* SPHERE = reinterpret_cast<const SHSphere*>(collisionShape.GetShape());
const SHColour COLLIDER_COLOUR = collisionShape.IsTrigger() ? SHColour::PURPLE : SHColour::GREEN; const SHColour COLLIDER_COLOUR = collisionShape.IsTrigger() ? SHColour::PURPLE : SHColour::GREEN;

View File

@ -20,6 +20,7 @@
#include "Editor/SHEditor.h" #include "Editor/SHEditor.h"
#include "Physics/SHPhysicsEvents.h" #include "Physics/SHPhysicsEvents.h"
#include "Scene/SHSceneManager.h" #include "Scene/SHSceneManager.h"
#include "Scripting/SHScriptEngine.h"
/*-------------------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------------------*/
/* Local Helper Functions */ /* Local Helper Functions */
@ -34,16 +35,16 @@ namespace SHADE
SHPhysicsSystem::SHPhysicsSystem() SHPhysicsSystem::SHPhysicsSystem()
: worldUpdated { false } : worldUpdated { false }
, interpolationFactor { 0.0 } , interpolationFactor { 0.0 }
, fixedDT { 60.0 } , fixedDT { DEFAULT_FIXED_STEP }
{} {}
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
/* Getter Function Definitions */ /* Getter Function Definitions */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
double SHPhysicsSystem::GetFixedDT() const noexcept double SHPhysicsSystem::GetFixedUpdateRate() const noexcept
{ {
return fixedDT; return 1.0 / fixedDT;
} }
const SHPhysicsWorldState::WorldSettings& SHPhysicsSystem::GetWorldSettings() const noexcept const SHPhysicsWorldState::WorldSettings& SHPhysicsSystem::GetWorldSettings() const noexcept
@ -61,7 +62,7 @@ namespace SHADE
return collisionListener.GetTriggerInfoContainer(); return collisionListener.GetTriggerInfoContainer();
} }
const SHPhysicsObject* const SHPhysicsSystem::GetPhysicsObject(EntityID eid) noexcept const SHPhysicsObject* SHPhysicsSystem::GetPhysicsObject(EntityID eid) noexcept
{ {
return objectManager.GetPhysicsObject(eid); return objectManager.GetPhysicsObject(eid);
} }
@ -76,9 +77,9 @@ namespace SHADE
/* Setter Function Definitions */ /* Setter Function Definitions */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
void SHPhysicsSystem::SetFixedDT(double fixedUpdateRate) noexcept void SHPhysicsSystem::SetFixedUpdateRate(double fixedUpdateRate) noexcept
{ {
fixedDT = fixedUpdateRate; fixedDT = 1.0 / fixedUpdateRate;
} }
void SHPhysicsSystem::SetWorldSettings(const SHPhysicsWorldState::WorldSettings& settings) noexcept void SHPhysicsSystem::SetWorldSettings(const SHPhysicsWorldState::WorldSettings& settings) noexcept
@ -126,6 +127,45 @@ namespace SHADE
worldState.DestroyWorld(factory); worldState.DestroyWorld(factory);
} }
void SHPhysicsSystem::ForceUpdate()
{
if (!worldState.world)
{
SHLOGV_ERROR("Unable to force update without a Physics world!")
return;
}
auto* scriptingSystem = SHSystemManager::GetSystem<SHScriptEngine>();
if (scriptingSystem == nullptr)
{
SHLOGV_ERROR("Unable to invoke FixedUpdate() on scripts due to missing SHScriptEngine!");
}
// Force the physics world to update once
if (scriptingSystem != nullptr)
scriptingSystem->ExecuteFixedUpdates();
worldState.world->update(static_cast<rp3d::decimal>(fixedDT));
// Sync transforms. No interpolation applied here
for (auto& [entityID, physicsObject] : objectManager.physicsObjects)
{
auto* transformComponent = SHComponentManager::GetComponent_s<SHTransformComponent>(entityID);
auto* rigidBodyComponent = SHComponentManager::GetComponent_s<SHRigidBodyComponent>(entityID);
auto* colliderComponent = SHComponentManager::GetComponent_s<SHColliderComponent>(entityID);
postUpdateSyncTransforms
(
physicsObject
, transformComponent
, rigidBodyComponent
, colliderComponent
, 1.0 // We use 1.0 here to avoid any interpolation
);
}
}
void SHPhysicsSystem::AddCollisionShape(EntityID eid, int shapeIndex) void SHPhysicsSystem::AddCollisionShape(EntityID eid, int shapeIndex)
{ {
static const auto ADD_SHAPE = [&](EntityID entityID, int index) static const auto ADD_SHAPE = [&](EntityID entityID, int index)
@ -183,48 +223,6 @@ namespace SHADE
#endif #endif
} }
void SHPhysicsSystem::AddForce(EntityID eid, const SHVec3& force) noexcept
{
auto* physicsObject = objectManager.GetPhysicsObject(eid);
}
void SHPhysicsSystem::AddForceAtLocalPos(EntityID eid, const SHVec3& force, const SHVec3& localPos) noexcept
{
auto* physicsObject = objectManager.GetPhysicsObject(eid);
}
void SHPhysicsSystem::AddForceAtWorldPos(EntityID eid, const SHVec3& force, const SHVec3& worldPos) noexcept
{
auto* physicsObject = objectManager.GetPhysicsObject(eid);
}
void SHPhysicsSystem::AddRelativeForce(EntityID eid, const SHVec3& relativeForce) noexcept
{
auto* physicsObject = objectManager.GetPhysicsObject(eid);
}
void SHPhysicsSystem::AddRelativeForceAtLocalPos(EntityID eid, const SHVec3& relativeForce, const SHVec3& localPos) noexcept
{
auto* physicsObject = objectManager.GetPhysicsObject(eid);
}
void SHPhysicsSystem::AddRelativeForceAtWorldPos(EntityID eid, const SHVec3& relativeForce, const SHVec3& worldPos) noexcept
{
auto* physicsObject = objectManager.GetPhysicsObject(eid);
}
void SHPhysicsSystem::AddTorque(EntityID eid, const SHVec3& torque) noexcept
{
auto* physicsObject = objectManager.GetPhysicsObject(eid);
}
void SHPhysicsSystem::AddRelativeTorque(EntityID eid, const SHVec3& relativeTorque) noexcept
{
auto* physicsObject = objectManager.GetPhysicsObject(eid);
}
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
/* Private Function Member Definitions */ /* Private Function Member Definitions */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
@ -339,5 +337,93 @@ namespace SHADE
return onStopEvent->handle; return onStopEvent->handle;
} }
void SHPhysicsSystem::preUpdateSyncTransform
(
SHPhysicsObject& physicsObject
, SHTransformComponent* transformComponent
, SHRigidBodyComponent* rigidBodyComponent
, SHColliderComponent* colliderComponent
) noexcept
{
if (!transformComponent)
return;
const SHVec3& WORLD_POS = transformComponent->GetWorldPosition();
const SHQuaternion& WORLD_ROT = transformComponent->GetWorldOrientation();
const SHVec3& WORLD_SCL = transformComponent->GetWorldScale();
const rp3d::Transform RP3D_TRANSFORM { WORLD_POS, WORLD_ROT };
physicsObject.GetRigidBody()->setTransform(RP3D_TRANSFORM);
if (rigidBodyComponent && SHSceneManager::CheckNodeAndComponentsActive<SHRigidBodyComponent>(physicsObject.entityID))
{
rigidBodyComponent->position = WORLD_POS;
rigidBodyComponent->orientation = WORLD_ROT;
}
if (colliderComponent && SHSceneManager::CheckNodeAndComponentsActive<SHColliderComponent>(physicsObject.entityID))
{
colliderComponent->position = WORLD_POS;
colliderComponent->orientation = WORLD_ROT;
colliderComponent->scale = WORLD_SCL;
colliderComponent->RecomputeCollisionShapes();
}
}
void SHPhysicsSystem::postUpdateSyncTransforms
(
SHPhysicsObject& physicsObject
, SHTransformComponent* transformComponent
, SHRigidBodyComponent* rigidBodyComponent
, SHColliderComponent* colliderComponent
, double interpolationFactor
) noexcept
{
const rp3d::Transform& CURRENT_TF = physicsObject.GetRigidBody()->getTransform();
auto renderPos = CURRENT_TF.getPosition();
auto renderRot = CURRENT_TF.getOrientation();
// Cache transforms
if (physicsObject.GetRigidBody()->isActive())
physicsObject.prevTransform = CURRENT_TF;
// Sync with rigid bodies
if (rigidBodyComponent && SHSceneManager::CheckNodeAndComponentsActive<SHRigidBodyComponent>(physicsObject.entityID))
{
// Skip static bodies
if (rigidBodyComponent->GetType() == SHRigidBodyComponent::Type::STATIC)
return;
// Check if transform should be interpolated
if (rigidBodyComponent->IsInterpolating())
{
// Interpolate transforms between current and predicted next transform
const rp3d::Transform PREV_TF = physicsObject.prevTransform;
const rp3d::Transform INTERPOLATED_TF = rp3d::Transform::interpolateTransforms(PREV_TF, CURRENT_TF, static_cast<rp3d::decimal>(interpolationFactor));
renderPos = INTERPOLATED_TF.getPosition();
renderRot = INTERPOLATED_TF.getOrientation();
}
rigidBodyComponent->position = CURRENT_TF.getPosition();
rigidBodyComponent->orientation = CURRENT_TF.getOrientation();
}
// Sync with colliders
if (colliderComponent && SHSceneManager::CheckNodeAndComponentsActive<SHColliderComponent>(physicsObject.entityID))
{
colliderComponent->position = CURRENT_TF.getPosition();
colliderComponent->orientation = CURRENT_TF.getOrientation();
}
// Set transform for rendering
if (transformComponent)
{
transformComponent->SetWorldPosition(renderPos);
transformComponent->SetWorldOrientation(renderRot);
}
}
} // namespace SHADE } // namespace SHADE

View File

@ -55,20 +55,20 @@ namespace SHADE
/* Getter Functions */ /* Getter Functions */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
[[nodiscard]] double GetFixedDT () const noexcept; [[nodiscard]] double GetFixedUpdateRate () const noexcept;
[[nodiscard]] const SHPhysicsWorldState::WorldSettings& GetWorldSettings () const noexcept; [[nodiscard]] const SHPhysicsWorldState::WorldSettings& GetWorldSettings () const noexcept;
[[nodiscard]] const std::vector<SHCollisionInfo>& GetAllCollisionInfo () const noexcept; [[nodiscard]] const std::vector<SHCollisionInfo>& GetAllCollisionInfo () const noexcept;
[[nodiscard]] const std::vector<SHCollisionInfo>& GetAllTriggerInfo () const noexcept; [[nodiscard]] const std::vector<SHCollisionInfo>& GetAllTriggerInfo () const noexcept;
[[nodiscard]] const SHPhysicsObject* const GetPhysicsObject (EntityID eid) noexcept; [[nodiscard]] const SHPhysicsObject* GetPhysicsObject (EntityID eid) noexcept;
[[nodiscard]] const SHPhysicsObjectManager::PhysicsObjectEntityMap& GetPhysicsObjects () const noexcept; [[nodiscard]] const SHPhysicsObjectManager::PhysicsObjectEntityMap& GetPhysicsObjects () const noexcept;
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* Setter Functions */ /* Setter Functions */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
void SetFixedDT (double fixedUpdateRate) noexcept; void SetFixedUpdateRate (double fixedUpdateRate) noexcept;
void SetWorldSettings (const SHPhysicsWorldState::WorldSettings& settings) noexcept; void SetWorldSettings (const SHPhysicsWorldState::WorldSettings& settings) noexcept;
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* Function Members */ /* Function Members */
@ -77,25 +77,15 @@ namespace SHADE
void Init () override; void Init () override;
void Exit () override; void Exit () override;
// Specific Handling for Collision Shapes as they are not under the Component System void ForceUpdate ();
// Specific Handling for Collision Shapes as they are not under the Component System.
// This is done as events need to be sent out.
// TODO(Diren): Consider using a static method through the ColliderComponent.
void AddCollisionShape (EntityID eid, int shapeIndex); void AddCollisionShape (EntityID eid, int shapeIndex);
void RemoveCollisionShape (EntityID eid, int shapeIndex); void RemoveCollisionShape (EntityID eid, int shapeIndex);
// Forces are applied from components here. These functions should only be invoked during play (through scripts)
// Thus there is no need to check for an editor.
void AddForce (EntityID eid, const SHVec3& force) noexcept;
void AddForceAtLocalPos (EntityID eid, const SHVec3& force, const SHVec3& localPos) noexcept;
void AddForceAtWorldPos (EntityID eid, const SHVec3& force, const SHVec3& worldPos) noexcept;
void AddRelativeForce (EntityID eid, const SHVec3& relativeForce) noexcept;
void AddRelativeForceAtLocalPos (EntityID eid, const SHVec3& relativeForce, const SHVec3& localPos) noexcept;
void AddRelativeForceAtWorldPos (EntityID eid, const SHVec3& relativeForce, const SHVec3& worldPos) noexcept;
void AddTorque (EntityID eid, const SHVec3& torque) noexcept;
void AddRelativeTorque (EntityID eid, const SHVec3& relativeTorque) noexcept;
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* System Routines */ /* System Routines */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
@ -120,15 +110,9 @@ namespace SHADE
/* Function Members */ /* Function Members */
/*-------------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------------*/
static void syncOnPlay(EntityID eid, SHPhysicsObject& physicsObject) noexcept; void syncRigidBodyActive (EntityID eid, SHPhysicsObject& physicsObject) const noexcept;
void syncColliderActive (EntityID eid, SHPhysicsObject& physicsObject) const noexcept;
static void preUpdateSyncTransform static void syncOnPlay (EntityID eid, SHPhysicsObject& physicsObject) noexcept;
(
SHPhysicsObject& physicsObject
, SHTransformComponent& transformComponent
, SHRigidBodyComponent* rigidBodyComponent
, SHColliderComponent* colliderComponent
) noexcept;
}; };
class SH_API PhysicsFixedUpdate final : public SHFixedSystemRoutine class SH_API PhysicsFixedUpdate final : public SHFixedSystemRoutine
@ -161,20 +145,6 @@ namespace SHADE
/*-------------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------------*/
void Execute(double dt) noexcept override; void Execute(double dt) noexcept override;
private:
/*-------------------------------------------------------------------------------*/
/* Function Members */
/*-------------------------------------------------------------------------------*/
static void postUpdateSyncTransforms
(
SHPhysicsObject& physicsObject
, SHTransformComponent& transformComponent
, SHRigidBodyComponent* rigidBodyComponent
, SHColliderComponent* colliderComponent
, double interpolationFactor
) noexcept;
}; };
private: private:
@ -208,5 +178,24 @@ namespace SHADE
SHEventHandle onPlay (SHEventPtr onPlayEvent); SHEventHandle onPlay (SHEventPtr onPlayEvent);
SHEventHandle onStop (SHEventPtr onStopEvent); SHEventHandle onStop (SHEventPtr onStopEvent);
static void preUpdateSyncTransform
(
SHPhysicsObject& physicsObject
, SHTransformComponent* transformComponent
, SHRigidBodyComponent* rigidBodyComponent
, SHColliderComponent* colliderComponent
) noexcept;
static void postUpdateSyncTransforms
(
SHPhysicsObject& physicsObject
, SHTransformComponent* transformComponent
, SHRigidBodyComponent* rigidBodyComponent
, SHColliderComponent* colliderComponent
, double interpolationFactor
) noexcept;
}; };
} // namespace SHADE } // namespace SHADE

View File

@ -55,7 +55,7 @@ namespace SHADE
auto phySystem = SHSystemManager::GetSystem<SHPhysicsSystem>(); auto phySystem = SHSystemManager::GetSystem<SHPhysicsSystem>();
if (phySystem) if (phySystem)
{ {
return phySystem->GetFixedDT(); return phySystem->GetFixedUpdateRate();
} }
SHLOG_WARNING("[SHPhysicsSystemInterface] Failed to get fixed delta time. 0.0 returned instead."); SHLOG_WARNING("[SHPhysicsSystemInterface] Failed to get fixed delta time. 0.0 returned instead.");

View File

@ -13,10 +13,14 @@
// Primary Header // Primary Header
#include "SHPhysicsSystem.h" #include "SHPhysicsSystem.h"
// Project Headers // Project Headers
#include "ECS_Base/Managers/SHEntityManager.h"
#include "ECS_Base/Managers/SHSystemManager.h" #include "ECS_Base/Managers/SHSystemManager.h"
#include "Editor/SHEditor.h" #include "Editor/SHEditor.h"
#include "Scene/SHSceneManager.h"
#include "Scripting/SHScriptEngine.h" #include "Scripting/SHScriptEngine.h"
#include "Input/SHInputManager.h"
namespace SHADE namespace SHADE
{ {
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
@ -43,65 +47,13 @@ namespace SHADE
{ {
auto* physicsSystem = reinterpret_cast<SHPhysicsSystem*>(GetSystem()); auto* physicsSystem = reinterpret_cast<SHPhysicsSystem*>(GetSystem());
#ifdef SHEDITOR #ifdef SHEDITOR
auto* editor = SHSystemManager::GetSystem<SHEditor>();
// Only Sync on Play.
// Otherwise, Components are only holding data until the world is built on play.
if (editor)
{
if (editor->editorState != SHEditor::State::STOP)
{
physicsSystem->objectManager.UpdateCommands();
for (auto& [entityID, physicsObject] : physicsSystem->objectManager.physicsObjects)
{
// Ensure a valid physics Object
if (physicsObject.rp3dBody == nullptr)
continue;
syncOnPlay(entityID, physicsObject);
}
}
else
{
auto& rigidBodyDense = SHComponentManager::GetDense<SHRigidBodyComponent>();
auto& colliderDense = SHComponentManager::GetDense<SHColliderComponent>();
for (auto& rigidBodyComponent : rigidBodyDense)
{
const auto* TRANSFORM = SHComponentManager::GetComponent_s<SHTransformComponent>(rigidBodyComponent.GetEID());
if (TRANSFORM && TRANSFORM->HasChanged())
{
rigidBodyComponent.position = TRANSFORM->GetWorldPosition();
rigidBodyComponent.orientation = TRANSFORM->GetWorldOrientation();
}
}
for (auto& colliderComponent : colliderDense)
{
const auto* TRANSFORM = SHComponentManager::GetComponent_s<SHTransformComponent>(colliderComponent.GetEID());
if (TRANSFORM && TRANSFORM->HasChanged())
{
colliderComponent.position = TRANSFORM->GetWorldPosition();
colliderComponent.orientation = TRANSFORM->GetWorldOrientation();
colliderComponent.scale = TRANSFORM->GetWorldScale();
colliderComponent.RecomputeCollisionShapes();
}
}
}
}
#else
// Always sync Rigid Body & Collider Components with Physics Objects
// Do not check for an editor here
// Only Sync on Play.
// Otherwise, Components are only holding data until the world is built on play.
const auto* EDITOR = SHSystemManager::GetSystem<SHEditor>();
if (EDITOR && EDITOR->editorState != SHEditor::State::STOP)
{
physicsSystem->objectManager.UpdateCommands(); physicsSystem->objectManager.UpdateCommands();
for (auto& [entityID, physicsObject] : physicsSystem->objectManager.physicsObjects) for (auto& [entityID, physicsObject] : physicsSystem->objectManager.physicsObjects)
@ -110,10 +62,64 @@ namespace SHADE
if (physicsObject.rp3dBody == nullptr) if (physicsObject.rp3dBody == nullptr)
continue; continue;
// Sync active states between SHADE & RP3D
syncRigidBodyActive(entityID, physicsObject);
syncColliderActive(entityID, physicsObject);
syncOnPlay(entityID, physicsObject); syncOnPlay(entityID, physicsObject);
} }
}
else
{
auto& rigidBodyDense = SHComponentManager::GetDense<SHRigidBodyComponent>();
auto& colliderDense = SHComponentManager::GetDense<SHColliderComponent>();
#endif for (auto& rigidBodyComponent : rigidBodyDense)
{
const auto* TRANSFORM = SHComponentManager::GetComponent_s<SHTransformComponent>(rigidBodyComponent.GetEID());
if (TRANSFORM && TRANSFORM->HasChanged())
{
rigidBodyComponent.position = TRANSFORM->GetWorldPosition();
rigidBodyComponent.orientation = TRANSFORM->GetWorldOrientation();
}
}
for (auto& colliderComponent : colliderDense)
{
const auto* TRANSFORM = SHComponentManager::GetComponent_s<SHTransformComponent>(colliderComponent.GetEID());
if (TRANSFORM && TRANSFORM->HasChanged())
{
colliderComponent.position = TRANSFORM->GetWorldPosition();
colliderComponent.orientation = TRANSFORM->GetWorldOrientation();
colliderComponent.scale = TRANSFORM->GetWorldScale();
colliderComponent.RecomputeCollisionShapes();
}
}
}
#else
// Always sync Rigid Body & Collider Components with Physics Objects
// Do not check for an editor here
physicsSystem->objectManager.UpdateCommands();
for (auto& [entityID, physicsObject] : physicsSystem->objectManager.physicsObjects)
{
// Ensure a valid physics Object
if (physicsObject.rp3dBody == nullptr)
continue;
syncRigidBodyActive(entityID, physicsObject);
syncColliderActive(entityID, physicsObject);
syncOnPlay(entityID, physicsObject);
}
#endif
} }
void SHPhysicsSystem::PhysicsFixedUpdate::Execute(double dt) noexcept void SHPhysicsSystem::PhysicsFixedUpdate::Execute(double dt) noexcept
@ -125,18 +131,18 @@ namespace SHADE
SHLOGV_ERROR("Unable to invoke FixedUpdate() on scripts due to missing SHScriptEngine!"); SHLOGV_ERROR("Unable to invoke FixedUpdate() on scripts due to missing SHScriptEngine!");
} }
fixedTimeStep = 1.0 / physicsSystem->fixedDT; const double FIXED_DT = physicsSystem->fixedDT;
accumulatedTime += dt; accumulatedTime += dt;
int count = 0; int count = 0;
while (accumulatedTime > fixedTimeStep) while (accumulatedTime > FIXED_DT)
{ {
if (scriptingSystem != nullptr) if (scriptingSystem != nullptr)
scriptingSystem->ExecuteFixedUpdates(); scriptingSystem->ExecuteFixedUpdates();
physicsSystem->worldState.world->update(static_cast<rp3d::decimal>(fixedTimeStep));
accumulatedTime -= fixedTimeStep; physicsSystem->worldState.world->update(static_cast<rp3d::decimal>(FIXED_DT));
accumulatedTime -= FIXED_DT;
++count; ++count;
} }
@ -165,17 +171,14 @@ namespace SHADE
auto* rigidBodyComponent = SHComponentManager::GetComponent_s<SHRigidBodyComponent>(entityID); auto* rigidBodyComponent = SHComponentManager::GetComponent_s<SHRigidBodyComponent>(entityID);
auto* colliderComponent = SHComponentManager::GetComponent_s<SHColliderComponent>(entityID); auto* colliderComponent = SHComponentManager::GetComponent_s<SHColliderComponent>(entityID);
if (transformComponent) postUpdateSyncTransforms
{ (
postUpdateSyncTransforms physicsObject
( , transformComponent
physicsObject , rigidBodyComponent
, *transformComponent , colliderComponent
, rigidBodyComponent , physicsSystem->interpolationFactor
, colliderComponent );
, physicsSystem->interpolationFactor
);
}
} }
// Collision & Trigger messages // Collision & Trigger messages
@ -191,6 +194,48 @@ namespace SHADE
/* Private Function Member Definitions */ /* Private Function Member Definitions */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
void SHPhysicsSystem::PhysicsPreUpdate::syncRigidBodyActive(EntityID eid, SHPhysicsObject& physicsObject) const noexcept
{
if (!SHComponentManager::HasComponent<SHRigidBodyComponent>(eid))
return;
const bool IS_ACTIVE_IN_SCENE = SHSceneManager::CheckNodeAndComponentsActive<SHRigidBodyComponent>(eid);
const bool IS_RP3D_BODY_ACTIVE = physicsObject.GetRigidBody()->isActive();
if (IS_ACTIVE_IN_SCENE != IS_RP3D_BODY_ACTIVE)
physicsObject.GetRigidBody()->setIsActive(IS_ACTIVE_IN_SCENE);
}
void SHPhysicsSystem::PhysicsPreUpdate::syncColliderActive(EntityID eid, SHPhysicsObject& physicsObject) const noexcept
{
const auto* COLLIDER = SHComponentManager::GetComponent_s<SHColliderComponent>(eid);
if (!COLLIDER)
return;
const bool IS_ACTIVE_IN_SCENE = SHSceneManager::CheckNodeAndComponentsActive<SHColliderComponent>(eid);
const bool IS_RP3D_COLLIDER_ACTIVE = physicsObject.collidersActive;
if (IS_ACTIVE_IN_SCENE != IS_RP3D_COLLIDER_ACTIVE)
{
// HACK: If active state turned off, remove all collision shapes. If turned on, add them back.
auto* physicsSystem = reinterpret_cast<SHPhysicsSystem*>(GetSystem());
const int NUM_SHAPES = static_cast<int>(COLLIDER->GetCollisionShapes().size());
if (IS_ACTIVE_IN_SCENE)
{
for (int i = 0; i < NUM_SHAPES; ++i)
physicsSystem->objectManager.AddCollisionShape(eid, i);
}
else
{
for (int i = NUM_SHAPES - 1; i >= 0; --i)
physicsSystem->objectManager.RemoveCollisionShape(eid, i);
}
physicsObject.collidersActive = IS_ACTIVE_IN_SCENE;
}
}
void SHPhysicsSystem::PhysicsPreUpdate::syncOnPlay(EntityID eid, SHPhysicsObject& physicsObject) noexcept void SHPhysicsSystem::PhysicsPreUpdate::syncOnPlay(EntityID eid, SHPhysicsObject& physicsObject) noexcept
{ {
auto* transformComponent = SHComponentManager::GetComponent_s<SHTransformComponent>(eid); auto* transformComponent = SHComponentManager::GetComponent_s<SHTransformComponent>(eid);
@ -203,7 +248,7 @@ namespace SHADE
preUpdateSyncTransform preUpdateSyncTransform
( (
physicsObject physicsObject
, *transformComponent , transformComponent
, rigidBodyComponent , rigidBodyComponent
, colliderComponent , colliderComponent
); );
@ -217,99 +262,4 @@ namespace SHADE
if (colliderComponent) if (colliderComponent)
physicsObject.SyncColliders(*colliderComponent); physicsObject.SyncColliders(*colliderComponent);
} }
void SHPhysicsSystem::PhysicsPreUpdate::preUpdateSyncTransform
(
SHPhysicsObject& physicsObject
, SHTransformComponent& transformComponent
, SHRigidBodyComponent* rigidBodyComponent
, SHColliderComponent* colliderComponent
) noexcept
{
const SHVec3& WORLD_POS = transformComponent.GetWorldPosition();
const SHQuaternion& WORLD_ROT = transformComponent.GetWorldOrientation();
const SHVec3& WORLD_SCL = transformComponent.GetWorldScale();
const rp3d::Transform RP3D_TRANSFORM { WORLD_POS, WORLD_ROT };
physicsObject.GetRigidBody()->setTransform(RP3D_TRANSFORM);
if (rigidBodyComponent)
{
rigidBodyComponent->position = WORLD_POS;
rigidBodyComponent->orientation = WORLD_ROT;
}
if (colliderComponent)
{
colliderComponent->position = WORLD_POS;
colliderComponent->orientation = WORLD_ROT;
colliderComponent->scale = WORLD_SCL;
colliderComponent->RecomputeCollisionShapes();
}
}
void SHPhysicsSystem::PhysicsPostUpdate::postUpdateSyncTransforms
(
SHPhysicsObject& physicsObject
, SHTransformComponent& transformComponent
, SHRigidBodyComponent* rigidBodyComponent
, SHColliderComponent* colliderComponent
, double interpolationFactor
) noexcept
{
rp3d::Vector3 rp3dPos;
rp3d::Quaternion rp3dRot;
const rp3d::Transform CURRENT_TF = physicsObject.rp3dBody->getTransform();
// Check if transform should be interpolated
if (rigidBodyComponent)
{
// Skip static bodies
if (rigidBodyComponent->GetType() == SHRigidBodyComponent::Type::STATIC)
return;
if (rigidBodyComponent->IsInterpolating())
{
// Interpolate transforms between current and predicted next transform
const rp3d::Transform PREV_TF = physicsObject.prevTransform;
const rp3d::Transform INTERPOLATED_TF = rp3d::Transform::interpolateTransforms(PREV_TF, CURRENT_TF, static_cast<rp3d::decimal>(interpolationFactor));
rp3dPos = INTERPOLATED_TF.getPosition();
rp3dRot = INTERPOLATED_TF.getOrientation();
}
else
{
rp3dPos = CURRENT_TF.getPosition();
rp3dRot = CURRENT_TF.getOrientation();
}
rigidBodyComponent->position = CURRENT_TF.getPosition();
rigidBodyComponent->orientation = CURRENT_TF.getOrientation();
if (colliderComponent)
{
// Sync with colliders
colliderComponent->position = CURRENT_TF.getPosition();
colliderComponent->orientation = CURRENT_TF.getOrientation();
}
}
else
{
rp3dPos = CURRENT_TF.getPosition();
rp3dRot = CURRENT_TF.getOrientation();
}
// Convert RP3D Transform to SHADE
transformComponent.SetWorldPosition(rp3dPos);
transformComponent.SetWorldOrientation(rp3dRot);
// Cache transforms
physicsObject.prevTransform = CURRENT_TF;
}
} // namespace SHADE } // namespace SHADE

View File

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "Graphics/MiddleEnd/Interface/SHRenderable.h" #include "Graphics/MiddleEnd/Interface/SHRenderable.h"
#include "Graphics/MiddleEnd/Materials/SHMaterialSpec.h" #include "Graphics/MiddleEnd/Materials/SHMaterialSpec.h"
#include "Math/Geometry/SHBoundingBox.h" #include "Math/Geometry/SHBox.h"
#include "Math/Geometry/SHBoundingSphere.h" #include "Math/Geometry/SHSphere.h"
#include "Physics/Interface/SHCollisionShape.h" #include "Physics/Interface/SHCollisionShape.h"
#include "Resource/SHResourceManager.h" #include "Resource/SHResourceManager.h"
#include "Math/Vector/SHVec2.h" #include "Math/Vector/SHVec2.h"
@ -130,13 +130,13 @@ namespace YAML
{ {
case SHCollisionShape::Type::BOX: case SHCollisionShape::Type::BOX:
{ {
const auto* BOX = reinterpret_cast<const SHBoundingBox*>(rhs.GetShape()); const auto* BOX = reinterpret_cast<const SHBox*>(rhs.GetShape());
node[HalfExtents] = BOX->GetRelativeExtents(); node[HalfExtents] = BOX->GetRelativeExtents();
} }
break; break;
case SHCollisionShape::Type::SPHERE: case SHCollisionShape::Type::SPHERE:
{ {
const auto* SPHERE = reinterpret_cast<const SHBoundingSphere*>(rhs.GetShape()); const auto* SPHERE = reinterpret_cast<const SHSphere*>(rhs.GetShape());
node[Radius] = SPHERE->GetRelativeRadius(); node[Radius] = SPHERE->GetRelativeRadius();
} }
break; break;
@ -288,7 +288,15 @@ namespace YAML
{ {
YAML::Node node; YAML::Node node;
node[MESH_YAML_TAG.data()] = SHResourceManager::GetAssetID<SHMesh>(rhs.GetMesh()).value_or(0); node[MESH_YAML_TAG.data()] = SHResourceManager::GetAssetID<SHMesh>(rhs.GetMesh()).value_or(0);
node[MAT_YAML_TAG.data()] = SHResourceManager::GetAssetID<SHMaterial>(rhs.GetMaterial()->GetBaseMaterial()).value_or(0); auto mat = rhs.GetMaterial();
if (mat)
{
node[MAT_YAML_TAG.data()] = SHResourceManager::GetAssetID<SHMaterial>(rhs.GetMaterial()->GetBaseMaterial()).value_or(0);
}
else
{
node[MAT_YAML_TAG.data()] = 0;
}
return node; return node;
} }
static bool decode(YAML::Node const& node, SHRenderable& rhs) static bool decode(YAML::Node const& node, SHRenderable& rhs)

View File

@ -47,35 +47,35 @@ namespace SHADE
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
Vector3 BoxCollider::Center::get() Vector3 BoxCollider::Center::get()
{ {
return Convert::ToCLI(getNativeBoundObject<SHBoundingBox>().GetCenter()); return Convert::ToCLI(getNativeBoundObject<SHBox>().GetCenter());
} }
void BoxCollider::Center::set(Vector3 value) void BoxCollider::Center::set(Vector3 value)
{ {
getNativeBoundObject<SHBoundingBox>().SetCenter(Convert::ToNative(value)); getNativeBoundObject<SHBox>().SetCenter(Convert::ToNative(value));
} }
Vector3 BoxCollider::HalfExtents::get() Vector3 BoxCollider::HalfExtents::get()
{ {
return Convert::ToCLI(getNativeBoundObject<SHBoundingBox>().GetWorldExtents()); return Convert::ToCLI(getNativeBoundObject<SHBox>().GetWorldExtents());
} }
void BoxCollider::HalfExtents::set(Vector3 value) void BoxCollider::HalfExtents::set(Vector3 value)
{ {
getNativeBoundObject<SHBoundingBox>().SetWorldExtents(Convert::ToNative(value)); getNativeBoundObject<SHBox>().SetWorldExtents(Convert::ToNative(value));
} }
Vector3 BoxCollider::Min::get() Vector3 BoxCollider::Min::get()
{ {
return Convert::ToCLI(getNativeBoundObject<SHBoundingBox>().GetMin()); return Convert::ToCLI(getNativeBoundObject<SHBox>().GetMin());
} }
void BoxCollider::Min::set(Vector3 value) void BoxCollider::Min::set(Vector3 value)
{ {
getNativeBoundObject<SHBoundingBox>().SetMin(Convert::ToNative(value)); getNativeBoundObject<SHBox>().SetMin(Convert::ToNative(value));
} }
Vector3 BoxCollider::Max::get() Vector3 BoxCollider::Max::get()
{ {
return Convert::ToCLI(getNativeBoundObject<SHBoundingBox>().GetMax()); return Convert::ToCLI(getNativeBoundObject<SHBox>().GetMax());
} }
void BoxCollider::Max::set(Vector3 value) void BoxCollider::Max::set(Vector3 value)
{ {
getNativeBoundObject<SHBoundingBox>().SetMax(Convert::ToNative(value)); getNativeBoundObject<SHBox>().SetMax(Convert::ToNative(value));
} }
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
@ -83,11 +83,11 @@ namespace SHADE
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
bool BoxCollider::TestPoint(Vector3 point) bool BoxCollider::TestPoint(Vector3 point)
{ {
return getNativeBoundObject<SHBoundingBox>().TestPoint(Convert::ToNative(point)); return getNativeBoundObject<SHBox>().TestPoint(Convert::ToNative(point));
} }
bool BoxCollider::Raycast(Ray ray, float maxDistance) bool BoxCollider::Raycast(Ray ray, float maxDistance)
{ {
return getNativeBoundObject<SHBoundingBox>().Raycast(Convert::ToNative(ray), maxDistance); return getNativeBoundObject<SHBox>().Raycast(Convert::ToNative(ray), maxDistance);
} }
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
@ -95,19 +95,19 @@ namespace SHADE
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
Vector3 SphereCollider::Center::get() Vector3 SphereCollider::Center::get()
{ {
return Convert::ToCLI(getNativeBoundObject<SHBoundingSphere>().GetCenter()); return Convert::ToCLI(getNativeBoundObject<SHSphere>().GetCenter());
} }
void SphereCollider::Center::set(Vector3 value) void SphereCollider::Center::set(Vector3 value)
{ {
getNativeBoundObject<SHBoundingSphere>().SetCenter(Convert::ToNative(value)); getNativeBoundObject<SHSphere>().SetCenter(Convert::ToNative(value));
} }
float SphereCollider::Radius::get() float SphereCollider::Radius::get()
{ {
return getNativeBoundObject<SHBoundingSphere>().GetWorldRadius(); return getNativeBoundObject<SHSphere>().GetWorldRadius();
} }
void SphereCollider::Radius::set(float value) void SphereCollider::Radius::set(float value)
{ {
getNativeBoundObject<SHBoundingSphere>().SetWorldRadius(value); getNativeBoundObject<SHSphere>().SetWorldRadius(value);
} }
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
@ -115,11 +115,11 @@ namespace SHADE
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
bool SphereCollider::TestPoint(Vector3 point) bool SphereCollider::TestPoint(Vector3 point)
{ {
return getNativeBoundObject<SHBoundingBox>().TestPoint(Convert::ToNative(point)); return getNativeBoundObject<SHBox>().TestPoint(Convert::ToNative(point));
} }
bool SphereCollider::Raycast(Ray ray, float maxDistance) bool SphereCollider::Raycast(Ray ray, float maxDistance)
{ {
return getNativeBoundObject<SHBoundingBox>().Raycast(Convert::ToNative(ray), maxDistance); return getNativeBoundObject<SHBox>().Raycast(Convert::ToNative(ray), maxDistance);
} }
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/

View File

@ -189,7 +189,17 @@ namespace SHADE
{ {
GetNativeComponent()->AddRelativeForceAtWorldPos(Convert::ToNative(relativeForce), Convert::ToNative(worldPos)); GetNativeComponent()->AddRelativeForceAtWorldPos(Convert::ToNative(relativeForce), Convert::ToNative(worldPos));
} }
Vector3 RigidBody::GetForce()
{
return Convert::ToCLI(GetNativeComponent()->GetForce());
}
void RigidBody::ClearForces()
{
GetNativeComponent()->ClearForces();
}
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* Torque Functions */ /* Torque Functions */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
@ -202,4 +212,15 @@ namespace SHADE
{ {
GetNativeComponent()->AddRelativeTorque(Convert::ToNative(relativeTorque)); GetNativeComponent()->AddRelativeTorque(Convert::ToNative(relativeTorque));
} }
Vector3 RigidBody::GetTorque()
{
return Convert::ToCLI(GetNativeComponent()->GetTorque());
}
void RigidBody::ClearTorque()
{
GetNativeComponent()->ClearTorque();
}
} }

View File

@ -145,11 +145,17 @@ namespace SHADE
void AddRelativeForceAtLocalPos(Vector3 relativeForce, Vector3 localPos); void AddRelativeForceAtLocalPos(Vector3 relativeForce, Vector3 localPos);
void AddRelativeForceAtWorldPos(Vector3 relativeForce, Vector3 worldPos); void AddRelativeForceAtWorldPos(Vector3 relativeForce, Vector3 worldPos);
Vector3 GetForce();
void ClearForces();
/*-----------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------*/
/* Torque Functions */ /* Torque Functions */
/*-----------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------*/
void AddTorque(Vector3 force); void AddTorque(Vector3 force);
void AddRelativeTorque(Vector3 relativeForce); void AddRelativeTorque(Vector3 relativeForce);
Vector3 GetTorque();
void ClearTorque();
}; };
} }

View File

@ -27,6 +27,7 @@ of DigiPen Institute of Technology is prohibited.
#include "Scene/SHSceneManager.h" #include "Scene/SHSceneManager.h"
#include "Scene/SHSceneGraph.h" #include "Scene/SHSceneGraph.h"
#include "Tools/SHLog.h" #include "Tools/SHLog.h"
#include "Graphics\MiddleEnd\Interface\SHRenderable.h"
// Project Headers // Project Headers
#include "Utility/Convert.hxx" #include "Utility/Convert.hxx"
#include "Utility/Debug.hxx" #include "Utility/Debug.hxx"
@ -36,6 +37,7 @@ of DigiPen Institute of Technology is prohibited.
#include "Components/Camera.hxx" #include "Components/Camera.hxx"
#include "Components/CameraArm.hxx" #include "Components/CameraArm.hxx"
#include "Components/Light.hxx" #include "Components/Light.hxx"
#include "Components\Renderable.hxx"
namespace SHADE namespace SHADE
{ {
@ -166,6 +168,70 @@ namespace SHADE
return T(); return T();
} }
generic<typename T>
System::Collections::Generic::IEnumerable<T>^ ECS::GetComponentsInChildren(EntityID entity)
{
System::Type^ componentType = T::typeid;
// Check if entity is correct
if (!SHEntityManager::IsValidEID(entity))
{
std::ostringstream oss;
oss << "[ECS] Attempted to retrieve Component \""
<< Convert::ToNative(componentType->Name)
<< "\" from invalid Entity.";
Debug::LogError(oss.str());
return nullptr;
}
// Search all elements via a iterative breadth first search
System::Collections::Generic::List<T>^ results;
System::Collections::Generic::Queue<Entity>^ searchSpace = gcnew System::Collections::Generic::Queue<Entity>();
// Start off with direct children
SHSceneNode* entityNode = SHSceneManager::GetCurrentSceneGraph().GetNode(entity);
if (entityNode == nullptr)
{
std::ostringstream oss;
oss << "[ScriptStore] Failed to retrieve SceneGraphNode of entity #" << entity << ". This should not happen!";
SHLog::Warning(oss.str());
}
for (const auto& child : entityNode->GetChildren())
{
searchSpace->Enqueue(child->GetEntityID());
}
// Continue with all subsequent children
while (searchSpace->Count > 0)
{
// Check if this entity has the component we need
Entity curr = searchSpace->Dequeue();
T component = GetComponent<T>(curr);
if (component != nullptr)
{
// We only construct if we need to
if (results == nullptr)
results = gcnew System::Collections::Generic::List<T>();
results->Add(component);
}
// Add children to the queue
SHSceneNode* sceneGraphNode = SHSceneManager::GetCurrentSceneGraph().GetNode(curr);
if (sceneGraphNode == nullptr)
{
std::ostringstream oss;
oss << "[ECS_CLI] Failed to retrieve SceneGraphNode of entity #" << entity << ". This should not happen!";
SHLog::Warning(oss.str());
continue;
}
for (const auto& child : sceneGraphNode->GetChildren())
{
searchSpace->Enqueue(child->GetEntityID());
}
}
// None here
return results;
}
generic <typename T> generic <typename T>
T ECS::EnsureComponent(EntityID entity) T ECS::EnsureComponent(EntityID entity)
{ {
@ -249,6 +315,7 @@ namespace SHADE
static ECS::ECS() static ECS::ECS()
{ {
componentMap.Add(createComponentSet<SHTransformComponent, Transform>()); componentMap.Add(createComponentSet<SHTransformComponent, Transform>());
componentMap.Add(createComponentSet<SHRenderable, Renderable>());
componentMap.Add(createComponentSet<SHColliderComponent, Collider>()); componentMap.Add(createComponentSet<SHColliderComponent, Collider>());
componentMap.Add(createComponentSet<SHRigidBodyComponent, RigidBody>()); componentMap.Add(createComponentSet<SHRigidBodyComponent, RigidBody>());
componentMap.Add(createComponentSet<SHCameraComponent, Camera>()); componentMap.Add(createComponentSet<SHCameraComponent, Camera>());

View File

@ -51,9 +51,9 @@ namespace SHADE
/// specified Component. /// specified Component.
/// </returns> /// </returns>
generic<typename T> where T : BaseComponent generic<typename T> where T : BaseComponent
static T GetComponent(EntityID entity); static T GetComponent(EntityID entity);
/// <summary> /// <summary>
/// Retrieves the first Component from the specified GameObjectt's children that /// Retrieves the first Component from the specified GameObject's children that
/// matches the specified type. /// matches the specified type.
/// </summary> /// </summary>
/// <typeparam name="T">Type of the Component to get.</typeparam> /// <typeparam name="T">Type of the Component to get.</typeparam>
@ -65,6 +65,20 @@ namespace SHADE
generic<typename T> where T : BaseComponent generic<typename T> where T : BaseComponent
static T GetComponentInChildren(EntityID entity); static T GetComponentInChildren(EntityID entity);
/// <summary> /// <summary>
/// Retrieves a list of Components from the specified GameObject's children that
/// matches the specified type.
/// This function performs allocations. If expecting only 1 component, use
/// GetComponentInChildren() instead.
/// This does not search the specified entity.
/// </summary>
/// <typeparam name="T">Type of the Component to get.</typeparam>
/// <param name="entity"> Entity object to get the Component from. </param>
/// <returns>
/// Newly allocated List of components. Will be null if no components are found.
/// </returns>
generic<typename T> where T : BaseComponent
static System::Collections::Generic::IEnumerable<T>^ GetComponentsInChildren(EntityID entity);
/// <summary>
/// Ensures a Component on the specified Entity. /// Ensures a Component on the specified Entity.
/// </summary> /// </summary>
/// <typeparam name="T">Type of the Component to ensure.</typeparam> /// <typeparam name="T">Type of the Component to ensure.</typeparam>

View File

@ -170,6 +170,14 @@ namespace SHADE
return ECS::GetComponentInChildren<T>(entity); return ECS::GetComponentInChildren<T>(entity);
} }
generic<typename T>
System::Collections::Generic::IEnumerable<T>^ GameObject::GetComponentsInChildren()
{
if (!valid)
throw gcnew System::NullReferenceException();
return ECS::GetComponentsInChildren<T>(entity);
}
generic <typename T> generic <typename T>
T GameObject::EnsureComponent() T GameObject::EnsureComponent()
{ {
@ -212,6 +220,13 @@ namespace SHADE
throw gcnew System::NullReferenceException(); throw gcnew System::NullReferenceException();
return ScriptStore::GetScriptInChildren<T>(entity); return ScriptStore::GetScriptInChildren<T>(entity);
} }
generic <typename T>
System::Collections::Generic::IEnumerable<T>^ GameObject::GetScriptsInChildren()
{
if (!valid)
throw gcnew System::NullReferenceException();
return ScriptStore::GetScriptsInChildren<T>(entity);
}
generic <typename T> generic <typename T>
System::Collections::Generic::IEnumerable<T>^ GameObject::GetScripts() System::Collections::Generic::IEnumerable<T>^ GameObject::GetScripts()

View File

@ -153,6 +153,7 @@ namespace SHADE
/// <summary> /// <summary>
/// Retrieves the first Component from this GameObject's children that matches /// Retrieves the first Component from this GameObject's children that matches
/// the specified type. /// the specified type.
/// Unlike Unity, we do not search this GameObject, only the children.
/// </summary> /// </summary>
/// <typeparam name="T">Type of the Component to get.</typeparam> /// <typeparam name="T">Type of the Component to get.</typeparam>
/// <returns> /// <returns>
@ -162,6 +163,19 @@ namespace SHADE
generic<typename T> where T : BaseComponent generic<typename T> where T : BaseComponent
T GetComponentInChildren(); T GetComponentInChildren();
/// <summary> /// <summary>
/// Retrieves a list of Components from this GameObject's children that matches
/// the specified type.
/// This function performs allocations. If expecting only 1 component, use
/// GetComponentInChildren() instead.
/// Unlike Unity, we do not search this GameObject, only the children.
/// </summary>
/// <typeparam name="T">Type of the Component to get.</typeparam>
/// <returns>
/// Newly allocated List of components. Will be null if no components are found.
/// </returns>
generic<typename T> where T : BaseComponent
System::Collections::Generic::IEnumerable<T>^ GetComponentsInChildren();
/// <summary>
/// Ensures a Component on this GameObject. /// Ensures a Component on this GameObject.
/// </summary> /// </summary>
/// <typeparam name="T">Type of the Component to ensure.</typeparam> /// <typeparam name="T">Type of the Component to ensure.</typeparam>
@ -201,12 +215,26 @@ namespace SHADE
/// Retrieves a Script of the specified type from child GameObjects. /// Retrieves a Script of the specified type from child GameObjects.
/// If multiple Scripts of the same specified type are added on the same /// If multiple Scripts of the same specified type are added on the same
/// child GameObject, this will retrieve the first one added. /// child GameObject, this will retrieve the first one added.
/// Unlike Unity, we do not search this GameObject, only the children.
/// </summary> /// </summary>
/// <typeparam name="T">Type of Script to retrieve.</typeparam> /// <typeparam name="T">Type of Script to retrieve.</typeparam>
/// <returns>Reference to the Script to retrieve.</returns> /// <returns>Reference to the Script to retrieve.</returns>
generic<typename T> where T : ref class, Script generic<typename T> where T : ref class, Script
T GetScriptInChildren(); T GetScriptInChildren();
/// <summary> /// <summary>
/// Retrieves a list of Scripts from this GameObject's children that matches
/// the specified type.
/// This function performs allocations. If expecting only 1 component, use
/// GetComponentInChildren() instead.
/// Unlike Unity, we do not search this GameObject, only the children.
/// </summary>
/// <typeparam name="T">Type of the Component to get.</typeparam>
/// <returns>
/// Newly allocated List of components. Will be null if no components are found.
/// </returns>
generic<typename T> where T : ref class, Script
System::Collections::Generic::IEnumerable<T>^ GetScriptsInChildren();
/// <summary>
/// Retrieves a immutable list of Scripts of the specified type from this /// Retrieves a immutable list of Scripts of the specified type from this
/// GameObject. /// GameObject.
/// </summary> /// </summary>

View File

@ -42,6 +42,12 @@ namespace SHADE
return owner.GetComponentInChildren<T>(); return owner.GetComponentInChildren<T>();
} }
generic<typename T>
System::Collections::Generic::IEnumerable<T>^ Script::GetComponentsInChildren()
{
return owner.GetComponentsInChildren<T>();
}
generic <typename T> generic <typename T>
T Script::EnsureComponent() T Script::EnsureComponent()
{ {
@ -72,6 +78,11 @@ namespace SHADE
{ {
return ScriptStore::GetScriptInChildren<T>(owner.GetEntity()); return ScriptStore::GetScriptInChildren<T>(owner.GetEntity());
} }
generic <typename T>
System::Collections::Generic::IEnumerable<T>^ Script::GetScriptsInChildren()
{
return ScriptStore::GetScriptsInChildren<T>(owner.GetEntity());
}
generic <typename T> generic <typename T>
System::Collections::Generic::IEnumerable<T>^ Script::GetScripts() System::Collections::Generic::IEnumerable<T>^ Script::GetScripts()

View File

@ -69,6 +69,7 @@ namespace SHADE
/// <summary> /// <summary>
/// Retrieves the first Component from this GameObject's children that matches /// Retrieves the first Component from this GameObject's children that matches
/// the specified type. /// the specified type.
/// Unlike Unity, we do not search this GameObject, only the children.
/// </summary> /// </summary>
/// <typeparam name="T"> /// <typeparam name="T">
/// Type of the Component to get. Must be derived from BaseComponent. /// Type of the Component to get. Must be derived from BaseComponent.
@ -77,6 +78,19 @@ namespace SHADE
generic<typename T> where T : BaseComponent generic<typename T> where T : BaseComponent
T GetComponentInChildren(); T GetComponentInChildren();
/// <summary> /// <summary>
/// Retrieves a list of Components from this GameObject's children that
/// matches the specified type.
/// This function performs allocations. If expecting only 1 component, use
/// GetComponentInChildren() instead.
/// Unlike Unity, we do not search this GameObject, only the children.
/// </summary>
/// <typeparam name="T">Type of the Component to get.</typeparam>
/// <returns>
/// Newly allocated List of components. Will be null if no components are found.
/// </returns>
generic<typename T> where T : BaseComponent
System::Collections::Generic::IEnumerable<T>^ GetComponentsInChildren();
/// <summary>
/// Ensures a Component on the GameObject that this Script belongs to. /// Ensures a Component on the GameObject that this Script belongs to.
/// </summary> /// </summary>
/// <typeparam name="T"> /// <typeparam name="T">
@ -121,6 +135,7 @@ namespace SHADE
/// <summary> /// <summary>
/// Retrieves the first Script from this GameObject's children that matches the /// Retrieves the first Script from this GameObject's children that matches the
/// specified type. /// specified type.
/// Unlike Unity, we do not search this GameObject, only the children.
/// </summary> /// </summary>
/// <typeparam name="T"> /// <typeparam name="T">
/// Type of script to get. /// Type of script to get.
@ -130,6 +145,19 @@ namespace SHADE
generic<typename T> where T : ref class, Script generic<typename T> where T : ref class, Script
T GetScriptInChildren(); T GetScriptInChildren();
/// <summary> /// <summary>
/// Retrieves a list of Scripts from this GameObject's children that matches
/// the specified type.
/// This function performs allocations. If expecting only 1 component, use
/// GetComponentInChildren() instead.
/// Unlike Unity, we do not search this GameObject, only the children.
/// </summary>
/// <typeparam name="T">Type of the Component to get.</typeparam>
/// <returns>
/// Newly allocated List of components. Will be null if no components are found.
/// </returns>
generic<typename T> where T : ref class, Script
System::Collections::Generic::IEnumerable<T>^ GetScriptsInChildren();
/// <summary>
/// Retrieves a immutable list of scripts from the specified Entity that /// Retrieves a immutable list of scripts from the specified Entity that
/// matches the specified type. /// matches the specified type.
/// <br/> /// <br/>

View File

@ -211,6 +211,70 @@ namespace SHADE
return T(); return T();
} }
generic<typename T>
System::Collections::Generic::IEnumerable<T>^ ScriptStore::GetScriptsInChildren(Entity entity)
{
System::Type^ componentType = T::typeid;
// Check if entity is correct
if (!SHEntityManager::IsValidEID(entity))
{
std::ostringstream oss;
oss << "[ScriptStore] Attempted to retrieve Script \""
<< Convert::ToNative(componentType->Name)
<< "\" from invalid Entity.";
Debug::LogError(oss.str());
return nullptr;
}
// Search all elements via a iterative breadth first search
System::Collections::Generic::List<T>^ results;
System::Collections::Generic::Queue<Entity>^ searchSpace = gcnew System::Collections::Generic::Queue<Entity>();
// Start off with direct children
SHSceneNode* entityNode = SHSceneManager::GetCurrentSceneGraph().GetNode(entity);
if (entityNode == nullptr)
{
std::ostringstream oss;
oss << "[ScriptStore] Failed to retrieve SceneGraphNode of entity #" << entity << ". This should not happen!";
SHLog::Warning(oss.str());
}
for (const auto& child : entityNode->GetChildren())
{
searchSpace->Enqueue(child->GetEntityID());
}
// Continue with all subsequent children
while (searchSpace->Count > 0)
{
// Check if this entity has the component we need
Entity curr = searchSpace->Dequeue();
T script = GetScript<T>(curr);
if (script != nullptr)
{
// We only construct if we need to
if (results == nullptr)
results = gcnew System::Collections::Generic::List<T>();
results->Add(script);
}
// Add children to the queue
SHSceneNode* sceneGraphNode = SHSceneManager::GetCurrentSceneGraph().GetNode(curr);
if (sceneGraphNode == nullptr)
{
std::ostringstream oss;
oss << "[ScriptStore] Failed to retrieve SceneGraphNode of entity #" << entity << ". This should not happen!";
SHLog::Warning(oss.str());
continue;
}
for (const auto& child : sceneGraphNode->GetChildren())
{
searchSpace->Enqueue(child->GetEntityID());
}
}
// None here
return results;
}
generic <typename T> generic <typename T>
System::Collections::Generic::IEnumerable<T>^ ScriptStore::GetScripts(Entity entity) System::Collections::Generic::IEnumerable<T>^ ScriptStore::GetScripts(Entity entity)
{ {

View File

@ -137,6 +137,29 @@ namespace SHADE
generic<typename T> where T : ref class, Script generic<typename T> where T : ref class, Script
static T GetScriptInChildren(Entity entity); static T GetScriptInChildren(Entity entity);
/// <summary> /// <summary>
/// Retrieves the list of Scripts from the specified Entity and the Entity's
/// children that matches the specified type.
/// This function performs allocations. If expecting only 1 component, use
/// GetScriptInChildren() instead.
/// This does not search the specified entity.
/// </summary>
/// <typeparam name="T">
/// Type of script to get.
/// This needs to be a default constructable Script.
/// </typeparam>
/// <param name="entity">
/// The entity which the script to retrieve is attached.
/// </param>
/// <returns>
/// Reference to the script. This can be null if no script of the specified
/// type is attached.
/// </returns>
/// <exception cref="ArgumentException">
/// If the specified Entity is invalid.
/// </exception>
generic<typename T> where T : ref class, Script
static System::Collections::Generic::IEnumerable<T>^ GetScriptsInChildren(Entity entity);
/// <summary>
/// Retrieves a immutable list of scripts from the specified Entity that /// Retrieves a immutable list of scripts from the specified Entity that
/// matches the specified type. /// matches the specified type.
/// <br/> /// <br/>

View File

@ -28,7 +28,47 @@ namespace SHADE
template<typename FieldType> template<typename FieldType>
bool SerialisationUtilities::fieldInsertYaml(System::Reflection::FieldInfo^ fieldInfo, System::Object^ object, YAML::Node& fieldNode) bool SerialisationUtilities::fieldInsertYaml(System::Reflection::FieldInfo^ fieldInfo, System::Object^ object, YAML::Node& fieldNode)
{ {
return varInsertYamlInternal<FieldType>(fieldInfo->GetValue(object), fieldNode); // Handle null objects
System::Object^ fieldObject = fieldInfo->GetValue(object);
if (fieldObject == nullptr)
{
// Default construct if null
if (fieldInfo->FieldType == FieldType::typeid)
{
if constexpr (std::is_same_v<FieldType, System::Enum>)
{
fieldNode = 0;
}
else if constexpr (std::is_same_v<FieldType, System::String>)
{
fieldNode = "";
}
else if constexpr (std::is_same_v<FieldType, Vector2>)
{
fieldNode.SetStyle(YAML::EmitterStyle::Flow);
fieldNode.push_back(0.0f);
fieldNode.push_back(0.0f);
}
else if constexpr (std::is_same_v<FieldType, Vector3>)
{
fieldNode.SetStyle(YAML::EmitterStyle::Flow);
fieldNode.push_back(0.0f);
fieldNode.push_back(0.0f);
fieldNode.push_back(0.0f);
}
else if constexpr (std::is_same_v<FieldType, GameObject>)
{
fieldNode = MAX_EID;
}
else
{
fieldNode = FieldType();
}
return true;
}
return false;
}
return varInsertYamlInternal<FieldType>(fieldObject, fieldNode);
} }
template<typename FieldType> template<typename FieldType>
bool SerialisationUtilities::varInsertYamlInternal(System::Object^ object, YAML::Node& fieldNode) bool SerialisationUtilities::varInsertYamlInternal(System::Object^ object, YAML::Node& fieldNode)