From 0e518c52a953da2023a4fef789ff175ea89dd565 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Sun, 2 Oct 2022 04:22:32 +0800 Subject: [PATCH 01/10] Fixed bug when SceneGraph is destroyed --- SHADE_Engine/src/Scene/SHSceneGraph.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/SHADE_Engine/src/Scene/SHSceneGraph.cpp b/SHADE_Engine/src/Scene/SHSceneGraph.cpp index da2dcffd..60f0794d 100644 --- a/SHADE_Engine/src/Scene/SHSceneGraph.cpp +++ b/SHADE_Engine/src/Scene/SHSceneGraph.cpp @@ -312,6 +312,11 @@ namespace SHADE if (parentNode == nullptr) { SHLOG_WARNING("Removing Entity {}'s parent", entityID) + + if (parent) + parent->RemoveChild(this); + + return; } // Handle self assignment -- 2.40.1 From 5a0b34ceea63dd80bdedda43e6c74611424b5517 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Thu, 20 Oct 2022 17:47:32 +0800 Subject: [PATCH 02/10] Added Event for Scene Graph Change Parent --- SHADE_Engine/src/Events/SHEventDefines.h | 11 ++++++----- SHADE_Engine/src/Scene/SHSceneGraph.cpp | 21 +++++++++++++++++++-- SHADE_Engine/src/Scene/SHSceneGraph.h | 15 +++++++++------ 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/SHADE_Engine/src/Events/SHEventDefines.h b/SHADE_Engine/src/Events/SHEventDefines.h index 317b67c1..fc9f41e1 100644 --- a/SHADE_Engine/src/Events/SHEventDefines.h +++ b/SHADE_Engine/src/Events/SHEventDefines.h @@ -5,8 +5,9 @@ typedef uint32_t SHEventIdentifier; typedef uint32_t SHEventHandle; //Add your event identifiers here: -constexpr SHEventIdentifier SH_EXAMPLE_EVENT{0}; -constexpr SHEventIdentifier SH_ENTITY_DESTROYED_EVENT{ 1 }; -constexpr SHEventIdentifier SH_ENTITY_CREATION_EVENT{ 2 }; -constexpr SHEventIdentifier SH_COMPONENT_ADDED_EVENT{ 3 }; -constexpr SHEventIdentifier SH_COMPONENT_REMOVED_EVENT{ 4 }; +constexpr SHEventIdentifier SH_EXAMPLE_EVENT { 0 }; +constexpr SHEventIdentifier SH_ENTITY_DESTROYED_EVENT { 1 }; +constexpr SHEventIdentifier SH_ENTITY_CREATION_EVENT { 2 }; +constexpr SHEventIdentifier SH_COMPONENT_ADDED_EVENT { 3 }; +constexpr SHEventIdentifier SH_COMPONENT_REMOVED_EVENT { 4 }; +constexpr SHEventIdentifier SH_SCENEGRAPH_CHANGE_PARENT_EVENT { 5 }; diff --git a/SHADE_Engine/src/Scene/SHSceneGraph.cpp b/SHADE_Engine/src/Scene/SHSceneGraph.cpp index 572e5927..291b5b2b 100644 --- a/SHADE_Engine/src/Scene/SHSceneGraph.cpp +++ b/SHADE_Engine/src/Scene/SHSceneGraph.cpp @@ -15,6 +15,7 @@ // Project Headers #include "ECS_Base/Managers/SHEntityManager.h" +#include "Events/SHEventManager.hpp" #include "Tools/SHLogger.h" #include "Tools/SHException.h" @@ -364,10 +365,18 @@ namespace SHADE } //////////////////////////////////////// + const SHSceneGraphChangeParentEvent EVENT_DATA + { + .oldParentID = NODE_ITER->second->GetParent()->GetEntityID(), + .newParentID = parent->GetEntityID() + }; + if (parent == nullptr) parent = root; NODE_ITER->second->SetParent(parent); + + SHEventManager::BroadcastEvent(EVENT_DATA, SH_SCENEGRAPH_CHANGE_PARENT_EVENT); } void SHSceneGraph::SetParent(EntityID entityID, EntityID parent) const noexcept @@ -401,8 +410,16 @@ namespace SHADE } //////////////////////////////////////// + const SHSceneGraphChangeParentEvent EVENT_DATA + { + .oldParentID = NODE_ITER->second->GetParent()->GetEntityID(), + .newParentID = parent + }; + SHSceneNode* currentNode = NODE_ITER->second; currentNode->SetParent(PARENT_ITER->second); + + SHEventManager::BroadcastEvent(EVENT_DATA, SH_SCENEGRAPH_CHANGE_PARENT_EVENT); } /*-----------------------------------------------------------------------------------*/ @@ -563,7 +580,7 @@ namespace SHADE ReleaseNode(node); } - void SHSceneGraph::Traverse (const UnaryPredicate& predicate) const + void SHSceneGraph::Traverse (const UnaryFunction& predicate) const { TraverseAndInvokePredicate(root, predicate); } @@ -602,7 +619,7 @@ namespace SHADE delete node; } - void SHSceneGraph::TraverseAndInvokePredicate(const SHSceneNode* node, const UnaryPredicate& predicate) + void SHSceneGraph::TraverseAndInvokePredicate(const SHSceneNode* node, const UnaryFunction& predicate) { for (auto* child : node->children) { diff --git a/SHADE_Engine/src/Scene/SHSceneGraph.h b/SHADE_Engine/src/Scene/SHSceneGraph.h index 3f3ebf92..62715f72 100644 --- a/SHADE_Engine/src/Scene/SHSceneGraph.h +++ b/SHADE_Engine/src/Scene/SHSceneGraph.h @@ -97,9 +97,8 @@ namespace SHADE /* Type Definitions */ /*---------------------------------------------------------------------------------*/ - using EntityNodeMap = std::unordered_map; - - using UnaryPredicate = std::function; + using EntityNodeMap = std::unordered_map; + using UnaryFunction = std::function; /*---------------------------------------------------------------------------------*/ @@ -143,8 +142,7 @@ namespace SHADE bool RemoveNode (SHSceneNode* nodeToRemove) noexcept; void Reset () noexcept; - void Traverse (const UnaryPredicate& predicate) const; - + void Traverse (const UnaryFunction& predicate) const; private: /*---------------------------------------------------------------------------------*/ @@ -160,8 +158,13 @@ namespace SHADE SHSceneNode* AllocateNode (EntityID entityID); void ReleaseNode (SHSceneNode* node) noexcept; - static void TraverseAndInvokePredicate (const SHSceneNode* node, const UnaryPredicate& predicate); + static void TraverseAndInvokePredicate (const SHSceneNode* node, const UnaryFunction& predicate); }; + struct SHSceneGraphChangeParentEvent + { + EntityID oldParentID; + EntityID newParentID; + }; } // namespace SHADE \ No newline at end of file -- 2.40.1 From 314d497b66c93cf21c5f245e4846961297eb6a60 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Thu, 20 Oct 2022 18:35:57 +0800 Subject: [PATCH 03/10] Reparenting objects maintain world transforms --- Assets/Editor/Layouts/UserLayout.ini | 20 ++++----- SHADE_Application/src/Scenes/SBTestScene.cpp | 6 +-- .../src/Math/Transform/SHTransformSystem.cpp | 43 ++++++++++++++++++- .../src/Math/Transform/SHTransformSystem.h | 6 ++- SHADE_Engine/src/Scene/SHSceneGraph.cpp | 10 +++-- SHADE_Engine/src/Scene/SHSceneGraph.h | 1 + 6 files changed, 65 insertions(+), 21 deletions(-) diff --git a/Assets/Editor/Layouts/UserLayout.ini b/Assets/Editor/Layouts/UserLayout.ini index 530ee770..7b0a70f8 100644 --- a/Assets/Editor/Layouts/UserLayout.ini +++ b/Assets/Editor/Layouts/UserLayout.ini @@ -10,7 +10,7 @@ Collapsed=0 [Window][Hierarchy Panel] Pos=0,142 -Size=571,918 +Size=349,918 Collapsed=0 DockId=0x00000004,0 @@ -20,29 +20,29 @@ Size=400,400 Collapsed=0 [Window][Inspector] -Pos=1649,48 -Size=271,1012 +Pos=1483,48 +Size=437,1012 Collapsed=0 DockId=0x00000006,0 [Window][Profiler] Pos=0,48 -Size=571,92 +Size=349,92 Collapsed=0 DockId=0x00000003,0 [Window][Viewport] -Pos=573,48 -Size=1074,1012 +Pos=351,48 +Size=1130,1012 Collapsed=0 DockId=0x00000002,0 [Docking][Data] DockSpace ID=0xC5C9B8AB Window=0xBE4044E9 Pos=8,79 Size=1920,1012 Split=X - DockNode ID=0x00000005 Parent=0xC5C9B8AB SizeRef=1992,1036 Split=X - DockNode ID=0x00000001 Parent=0x00000005 SizeRef=571,1036 Split=Y Selected=0x1E6EB881 + DockNode ID=0x00000005 Parent=0xC5C9B8AB SizeRef=1481,1036 Split=X + DockNode ID=0x00000001 Parent=0x00000005 SizeRef=349,1036 Split=Y Selected=0x1E6EB881 DockNode ID=0x00000003 Parent=0x00000001 SizeRef=225,94 Selected=0x1E6EB881 DockNode ID=0x00000004 Parent=0x00000001 SizeRef=225,940 Selected=0xE096E5AE - DockNode ID=0x00000002 Parent=0x00000005 SizeRef=1074,1036 CentralNode=1 Selected=0x13926F0B - DockNode ID=0x00000006 Parent=0xC5C9B8AB SizeRef=271,1036 Selected=0xE7039252 + DockNode ID=0x00000002 Parent=0x00000005 SizeRef=1130,1036 CentralNode=1 Selected=0x13926F0B + DockNode ID=0x00000006 Parent=0xC5C9B8AB SizeRef=437,1036 Selected=0xE7039252 diff --git a/SHADE_Application/src/Scenes/SBTestScene.cpp b/SHADE_Application/src/Scenes/SBTestScene.cpp index f1d656ee..d81550e1 100644 --- a/SHADE_Application/src/Scenes/SBTestScene.cpp +++ b/SHADE_Application/src/Scenes/SBTestScene.cpp @@ -77,9 +77,9 @@ namespace Sandbox customMat->SetProperty("data.alpha", 0.1f); // Create Stress Test Objects - static const SHVec3 TEST_OBJ_SCALE = SHVec3::One * 0.5f; - constexpr int NUM_ROWS = 10; - constexpr int NUM_COLS = 10; + static const SHVec3 TEST_OBJ_SCALE = SHVec3::One; + constexpr int NUM_ROWS = 2; + constexpr int NUM_COLS = 1; static const SHVec3 TEST_OBJ_SPACING = { 0.1f, 0.1f, 0.1f }; static const SHVec3 TEST_OBJ_START_POS = { -(NUM_COLS / 2 * TEST_OBJ_SPACING.x) + 1.0f, -2.0f, -1.0f }; diff --git a/SHADE_Engine/src/Math/Transform/SHTransformSystem.cpp b/SHADE_Engine/src/Math/Transform/SHTransformSystem.cpp index 6b05e323..448fa60d 100644 --- a/SHADE_Engine/src/Math/Transform/SHTransformSystem.cpp +++ b/SHADE_Engine/src/Math/Transform/SHTransformSystem.cpp @@ -17,7 +17,6 @@ #include "Scene/SHSceneManager.h" #include "ECS_Base/Managers/SHComponentManager.h" #include "ECS_Base/Managers/SHEntityManager.h" -#include "Tools/SHException.h" namespace SHADE { @@ -47,7 +46,9 @@ namespace SHADE void SHTransformSystem::Init() { - + std::shared_ptr thisReceiver { std::make_shared>(this, &SHTransformSystem::ChangeParent) }; + ReceiverPtr receiver = std::dynamic_pointer_cast(thisReceiver); + SHEventManager::SubscribeTo(SH_SCENEGRAPH_CHANGE_PARENT_EVENT, receiver); } void SHTransformSystem::Exit() @@ -59,6 +60,44 @@ namespace SHADE /* Private Function Member Definitions */ /*-----------------------------------------------------------------------------------*/ + SHEventHandle SHTransformSystem::ChangeParent(SHEventPtr changeParentEvent) + { + const auto& eventData = reinterpret_cast*>(changeParentEvent.get()); + + // Get Current Respective Components + auto* tf = SHComponentManager::GetComponent(eventData->data->entityID); + const auto* PARENT = SHComponentManager::GetComponent_s(eventData->data->newParentID); + + // Recompute local transform and store localToWorld Matrix + SHMatrix localToWorld = SHMatrix::Identity; + SHMatrix worldToLocal = SHMatrix::Identity; + + if (PARENT != nullptr) // Not the root + { + localToWorld = PARENT->GetTRS(); + worldToLocal = SHMatrix::Inverse(localToWorld); + } + + // Maintain World Transform and recompute Local Transform + + // Compute Local Position + tf->local.position = SHVec3::Transform(tf->world.position, worldToLocal); + + // Compute Local Rotation + tf->local.rotation = tf->world.rotation; + if (PARENT) + tf->local.rotation -= PARENT->GetLocalRotation(); + + // Compute Local Scale + tf->local.scale = tf->world.scale; + if (PARENT) + tf->local.scale /= PARENT->GetLocalScale(); + + tf->local.trs = localToWorld; + + return eventData->handle; + } + void SHTransformSystem::UpdateEntity(const SHSceneNode* node) { const auto* NODE_TRANSFORM = SHComponentManager::GetComponent_s(node->GetEntityID()); diff --git a/SHADE_Engine/src/Math/Transform/SHTransformSystem.h b/SHADE_Engine/src/Math/Transform/SHTransformSystem.h index 02c3b6c6..8a50b368 100644 --- a/SHADE_Engine/src/Math/Transform/SHTransformSystem.h +++ b/SHADE_Engine/src/Math/Transform/SHTransformSystem.h @@ -84,8 +84,10 @@ namespace SHADE /* Function Members */ /*---------------------------------------------------------------------------------*/ - static void UpdateEntity (const SHSceneNode* node); - static void UpdateTransform(SHTransformComponent& tf, const SHTransformComponent* parent = nullptr); + SHEventHandle ChangeParent (SHEventPtr changeParentEvent); + + static void UpdateEntity (const SHSceneNode* node); + static void UpdateTransform (SHTransformComponent& tf, const SHTransformComponent* parent = nullptr); }; diff --git a/SHADE_Engine/src/Scene/SHSceneGraph.cpp b/SHADE_Engine/src/Scene/SHSceneGraph.cpp index 291b5b2b..ac6975fb 100644 --- a/SHADE_Engine/src/Scene/SHSceneGraph.cpp +++ b/SHADE_Engine/src/Scene/SHSceneGraph.cpp @@ -367,8 +367,9 @@ namespace SHADE const SHSceneGraphChangeParentEvent EVENT_DATA { - .oldParentID = NODE_ITER->second->GetParent()->GetEntityID(), - .newParentID = parent->GetEntityID() + .entityID = entityID + , .oldParentID = NODE_ITER->second->GetParent()->GetEntityID() + , .newParentID = parent ? parent->GetEntityID() : root->GetEntityID() }; if (parent == nullptr) @@ -412,8 +413,9 @@ namespace SHADE const SHSceneGraphChangeParentEvent EVENT_DATA { - .oldParentID = NODE_ITER->second->GetParent()->GetEntityID(), - .newParentID = parent + .entityID = entityID + , .oldParentID = NODE_ITER->second->GetParent()->GetEntityID() + , .newParentID = parent }; SHSceneNode* currentNode = NODE_ITER->second; diff --git a/SHADE_Engine/src/Scene/SHSceneGraph.h b/SHADE_Engine/src/Scene/SHSceneGraph.h index 62715f72..1dfc1542 100644 --- a/SHADE_Engine/src/Scene/SHSceneGraph.h +++ b/SHADE_Engine/src/Scene/SHSceneGraph.h @@ -163,6 +163,7 @@ namespace SHADE struct SHSceneGraphChangeParentEvent { + EntityID entityID; EntityID oldParentID; EntityID newParentID; }; -- 2.40.1 From fda33f7461087c84299a6afcac050a22e1ee81ac Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Thu, 20 Oct 2022 22:55:59 +0800 Subject: [PATCH 04/10] Fixed multi-level transform updates --- SHADE_Application/src/Scenes/SBTestScene.cpp | 2 +- .../src/Math/Transform/SHTransformSystem.cpp | 87 +++++++++++++++---- .../src/Math/Transform/SHTransformSystem.h | 7 +- SHADE_Engine/src/Scene/SHSceneGraph.cpp | 12 +-- SHADE_Engine/src/Scene/SHSceneGraph.h | 6 +- 5 files changed, 84 insertions(+), 30 deletions(-) diff --git a/SHADE_Application/src/Scenes/SBTestScene.cpp b/SHADE_Application/src/Scenes/SBTestScene.cpp index d81550e1..be09768b 100644 --- a/SHADE_Application/src/Scenes/SBTestScene.cpp +++ b/SHADE_Application/src/Scenes/SBTestScene.cpp @@ -78,7 +78,7 @@ namespace Sandbox // Create Stress Test Objects static const SHVec3 TEST_OBJ_SCALE = SHVec3::One; - constexpr int NUM_ROWS = 2; + constexpr int NUM_ROWS = 3; constexpr int NUM_COLS = 1; static const SHVec3 TEST_OBJ_SPACING = { 0.1f, 0.1f, 0.1f }; static const SHVec3 TEST_OBJ_START_POS = { -(NUM_COLS / 2 * TEST_OBJ_SPACING.x) + 1.0f, -2.0f, -1.0f }; diff --git a/SHADE_Engine/src/Math/Transform/SHTransformSystem.cpp b/SHADE_Engine/src/Math/Transform/SHTransformSystem.cpp index 448fa60d..a3ca3e75 100644 --- a/SHADE_Engine/src/Math/Transform/SHTransformSystem.cpp +++ b/SHADE_Engine/src/Math/Transform/SHTransformSystem.cpp @@ -64,17 +64,18 @@ namespace SHADE { const auto& eventData = reinterpret_cast*>(changeParentEvent.get()); - // Get Current Respective Components - auto* tf = SHComponentManager::GetComponent(eventData->data->entityID); - const auto* PARENT = SHComponentManager::GetComponent_s(eventData->data->newParentID); + auto* node = eventData->data->node; + auto* tf = SHComponentManager::GetComponent_s(node->GetEntityID()); // Recompute local transform and store localToWorld Matrix SHMatrix localToWorld = SHMatrix::Identity; SHMatrix worldToLocal = SHMatrix::Identity; - if (PARENT != nullptr) // Not the root + auto* newParent = eventData->data->newParent; + const auto* PARENT_TF = SHComponentManager::GetComponent_s(newParent->GetEntityID()); + if (PARENT_TF != nullptr) // Not the root { - localToWorld = PARENT->GetTRS(); + localToWorld = PARENT_TF->GetTRS(); worldToLocal = SHMatrix::Inverse(localToWorld); } @@ -83,21 +84,74 @@ namespace SHADE // Compute Local Position tf->local.position = SHVec3::Transform(tf->world.position, worldToLocal); - // Compute Local Rotation - tf->local.rotation = tf->world.rotation; - if (PARENT) - tf->local.rotation -= PARENT->GetLocalRotation(); + + tf->local.rotation = tf->world.rotation; + tf->local.scale = tf->world.scale; - // Compute Local Scale - tf->local.scale = tf->world.scale; - if (PARENT) - tf->local.scale /= PARENT->GetLocalScale(); + if (PARENT_TF != nullptr) + { + // Compute Local Rotation + tf->local.rotation -= PARENT_TF->GetLocalRotation(); + + // Compute Local Scale + tf->local.scale /= PARENT_TF->GetLocalScale(); + } tf->local.trs = localToWorld; + // Propagate maintaining world transform down the branch + UpdateChildrenLocalTransforms(node); return eventData->handle; } + void SHTransformSystem::UpdateChildrenLocalTransforms(SHSceneNode* node) + { + // Structure is similar to update entity, albeit without a queue to do being a forced update + for (const auto* child : node->GetChildren()) + { + if (auto* childTransform = SHComponentManager::GetComponent_s(child->GetEntityID()); childTransform) + { + const bool IS_NODE_ACTIVE = child->IsActive(); + if (IS_NODE_ACTIVE && childTransform->isActive) + { + // Recompute local transform and store localToWorld Matrix + SHMatrix localToWorld = SHMatrix::Identity; + SHMatrix worldToLocal = SHMatrix::Identity; + + const auto* parent = SHComponentManager::GetComponent_s(node->GetEntityID()); + + if (parent != nullptr) // Not the root + { + localToWorld = parent->GetTRS(); + worldToLocal = SHMatrix::Inverse(localToWorld); + } + + // Maintain World Transform and recompute Local Transform + + // Compute Local Position + childTransform->local.position = SHVec3::Transform(childTransform->world.position, worldToLocal); + + + childTransform->local.rotation = childTransform->world.rotation; + childTransform->local.scale = childTransform->world.scale; + + if (parent) + { + // Compute Local Rotation + childTransform->local.rotation -= parent->GetLocalRotation(); + + // Compute Local Scale + childTransform->local.scale /= parent->GetLocalScale(); + } + + childTransform->local.trs = localToWorld; + } + } + + + } + } + void SHTransformSystem::UpdateEntity(const SHSceneNode* node) { const auto* NODE_TRANSFORM = SHComponentManager::GetComponent_s(node->GetEntityID()); @@ -113,7 +167,10 @@ namespace SHADE if (IS_NODE_ACTIVE && childTransform->isActive) { if (childTransform->dirty || HAS_PARENT_CHANGED) + { UpdateTransform(*childTransform, NODE_TRANSFORM); + childTransform->dirty = true; + } } } @@ -177,10 +234,6 @@ namespace SHADE tf.world.scale = tf.local.scale * (parent ? parent->GetLocalScale() : SHVec3::One); tf.world.ComputeTRS(); - - // Transpose TRS to column major - //tf.local.trs.Transpose(); - //tf.world.trs.Transpose(); } } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Math/Transform/SHTransformSystem.h b/SHADE_Engine/src/Math/Transform/SHTransformSystem.h index 8a50b368..95957830 100644 --- a/SHADE_Engine/src/Math/Transform/SHTransformSystem.h +++ b/SHADE_Engine/src/Math/Transform/SHTransformSystem.h @@ -84,10 +84,11 @@ namespace SHADE /* Function Members */ /*---------------------------------------------------------------------------------*/ - SHEventHandle ChangeParent (SHEventPtr changeParentEvent); + SHEventHandle ChangeParent (SHEventPtr changeParentEvent); + static void UpdateChildrenLocalTransforms (SHSceneNode* node); - static void UpdateEntity (const SHSceneNode* node); - static void UpdateTransform (SHTransformComponent& tf, const SHTransformComponent* parent = nullptr); + static void UpdateEntity (const SHSceneNode* node); + static void UpdateTransform (SHTransformComponent& tf, const SHTransformComponent* parent = nullptr); }; diff --git a/SHADE_Engine/src/Scene/SHSceneGraph.cpp b/SHADE_Engine/src/Scene/SHSceneGraph.cpp index ac6975fb..950fd6a0 100644 --- a/SHADE_Engine/src/Scene/SHSceneGraph.cpp +++ b/SHADE_Engine/src/Scene/SHSceneGraph.cpp @@ -367,9 +367,9 @@ namespace SHADE const SHSceneGraphChangeParentEvent EVENT_DATA { - .entityID = entityID - , .oldParentID = NODE_ITER->second->GetParent()->GetEntityID() - , .newParentID = parent ? parent->GetEntityID() : root->GetEntityID() + .node = NODE_ITER->second + , .oldParent = NODE_ITER->second->GetParent() + , .newParent = parent ? parent : root }; if (parent == nullptr) @@ -413,9 +413,9 @@ namespace SHADE const SHSceneGraphChangeParentEvent EVENT_DATA { - .entityID = entityID - , .oldParentID = NODE_ITER->second->GetParent()->GetEntityID() - , .newParentID = parent + .node = NODE_ITER->second + , .oldParent = NODE_ITER->second->GetParent() + , .newParent = PARENT_ITER->second }; SHSceneNode* currentNode = NODE_ITER->second; diff --git a/SHADE_Engine/src/Scene/SHSceneGraph.h b/SHADE_Engine/src/Scene/SHSceneGraph.h index 1dfc1542..a4cf45eb 100644 --- a/SHADE_Engine/src/Scene/SHSceneGraph.h +++ b/SHADE_Engine/src/Scene/SHSceneGraph.h @@ -163,9 +163,9 @@ namespace SHADE struct SHSceneGraphChangeParentEvent { - EntityID entityID; - EntityID oldParentID; - EntityID newParentID; + SHSceneNode* node; + SHSceneNode* oldParent; + SHSceneNode* newParent; }; } // namespace SHADE \ No newline at end of file -- 2.40.1 From a52f0ddeed7f7090a21567daeb85c366437bd2d5 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Fri, 21 Oct 2022 00:08:24 +0800 Subject: [PATCH 05/10] Added a degree getter and setter for rotation --- Assets/Editor/Layouts/UserLayout.ini | 2 +- .../Math/Transform/SHTransformComponent.cpp | 64 ++++++++++++++++++- .../src/Math/Transform/SHTransformComponent.h | 42 ++++++------ 3 files changed, 86 insertions(+), 22 deletions(-) diff --git a/Assets/Editor/Layouts/UserLayout.ini b/Assets/Editor/Layouts/UserLayout.ini index 7b0a70f8..396b853b 100644 --- a/Assets/Editor/Layouts/UserLayout.ini +++ b/Assets/Editor/Layouts/UserLayout.ini @@ -38,7 +38,7 @@ Collapsed=0 DockId=0x00000002,0 [Docking][Data] -DockSpace ID=0xC5C9B8AB Window=0xBE4044E9 Pos=8,79 Size=1920,1012 Split=X +DockSpace ID=0xC5C9B8AB Window=0xBE4044E9 Pos=-227,-1256 Size=1920,1012 Split=X DockNode ID=0x00000005 Parent=0xC5C9B8AB SizeRef=1481,1036 Split=X DockNode ID=0x00000001 Parent=0x00000005 SizeRef=349,1036 Split=Y Selected=0x1E6EB881 DockNode ID=0x00000003 Parent=0x00000001 SizeRef=225,94 Selected=0x1E6EB881 diff --git a/SHADE_Engine/src/Math/Transform/SHTransformComponent.cpp b/SHADE_Engine/src/Math/Transform/SHTransformComponent.cpp index 949cfa67..a8b75497 100644 --- a/SHADE_Engine/src/Math/Transform/SHTransformComponent.cpp +++ b/SHADE_Engine/src/Math/Transform/SHTransformComponent.cpp @@ -12,6 +12,8 @@ // Primary Header #include "SHTransformComponent.h" +// Project Headers +#include "Math/SHMathHelpers.h" namespace SHADE { @@ -43,6 +45,15 @@ namespace SHADE return local.rotation; } + SHVec3 SHTransformComponent::GetLocalRotationDeg() const noexcept + { + SHVec3 rot = local.rotation; + rot.x = SHMath::RadiansToDegrees(rot.x); + rot.y = SHMath::RadiansToDegrees(rot.y); + rot.z = SHMath::RadiansToDegrees(rot.z); + return rot; + } + const SHVec3& SHTransformComponent::GetLocalScale() const noexcept { return local.scale; @@ -58,6 +69,15 @@ namespace SHADE return world.rotation; } + SHVec3 SHTransformComponent::GetWorldRotationDeg() const noexcept + { + SHVec3 rot = world.rotation; + rot.x = SHMath::RadiansToDegrees(rot.x); + rot.y = SHMath::RadiansToDegrees(rot.y); + rot.z = SHMath::RadiansToDegrees(rot.z); + return rot; + } + const SHVec3& SHTransformComponent::GetWorldScale() const noexcept { return world.scale; @@ -94,6 +114,15 @@ namespace SHADE local.rotation = newLocalRotation; } + void SHTransformComponent::SetLocalRotationDeg(SHVec3 newLocalRotationDeg) noexcept + { + dirty = true; + + local.rotation.x = SHMath::DegreesToRadians(newLocalRotationDeg.x); + local.rotation.y = SHMath::DegreesToRadians(newLocalRotationDeg.y); + local.rotation.z = SHMath::DegreesToRadians(newLocalRotationDeg.z); + } + void SHTransformComponent::SetLocalRotation(float pitch, float yaw, float roll) noexcept { dirty = true; @@ -103,6 +132,13 @@ namespace SHADE local.rotation.z = roll; } + void SHTransformComponent::SetLocalRotationDeg(float pitch, float yaw, float roll) noexcept + { + local.rotation.x = SHMath::DegreesToRadians(pitch); + local.rotation.y = SHMath::DegreesToRadians(yaw); + local.rotation.z = SHMath::DegreesToRadians(roll); + } + void SHTransformComponent::SetLocalScale(const SHVec3& newLocalScale) noexcept { dirty = true; @@ -125,6 +161,17 @@ namespace SHADE updateQueue.push({ UpdateCommandType::WORLD_ROTATION, newWorldRotation }); } + void SHTransformComponent::SetWorldRotationDeg(const SHVec3& newWorldRotation) noexcept + { + dirty = true; + + world.rotation.x = SHMath::DegreesToRadians(newWorldRotation.x); + world.rotation.y = SHMath::DegreesToRadians(newWorldRotation.y); + world.rotation.z = SHMath::DegreesToRadians(newWorldRotation.z); + + updateQueue.push({ UpdateCommandType::WORLD_ROTATION, world.rotation }); + } + void SHTransformComponent::SetWorldRotation(float pitch, float yaw, float roll) noexcept { dirty = true; @@ -136,6 +183,17 @@ namespace SHADE updateQueue.push({ UpdateCommandType::WORLD_ROTATION, SHVec3{ pitch, yaw, roll} }); } + void SHTransformComponent::SetWorldRotationDeg(float pitch, float yaw, float roll) noexcept + { + dirty = true; + + world.rotation.x = SHMath::DegreesToRadians(pitch); + world.rotation.y = SHMath::DegreesToRadians(yaw); + world.rotation.z = SHMath::DegreesToRadians(roll); + + updateQueue.push({ UpdateCommandType::WORLD_ROTATION, world.rotation }); + } + void SHTransformComponent::SetWorldScale(const SHVec3& newWorldScale) noexcept { dirty = true; @@ -152,7 +210,7 @@ RTTR_REGISTRATION using namespace rttr; registration::class_("Transform Component") - .property("Translate" , &SHTransformComponent::GetLocalPosition , &SHTransformComponent::SetLocalPosition ) - .property("Rotate" , &SHTransformComponent::GetLocalRotation , select_overload(&SHTransformComponent::SetLocalRotation) ) - .property("Scale" , &SHTransformComponent::GetLocalScale , &SHTransformComponent::SetLocalScale ); + .property("Translate" , &SHTransformComponent::GetLocalPosition , &SHTransformComponent::SetLocalPosition ) + .property("Rotate" , &SHTransformComponent::GetLocalRotationDeg, select_overload(&SHTransformComponent::SetLocalRotationDeg)) + .property("Scale" , &SHTransformComponent::GetLocalScale , &SHTransformComponent::SetLocalScale ); } \ No newline at end of file diff --git a/SHADE_Engine/src/Math/Transform/SHTransformComponent.h b/SHADE_Engine/src/Math/Transform/SHTransformComponent.h index ad355694..2a3fa7a0 100644 --- a/SHADE_Engine/src/Math/Transform/SHTransformComponent.h +++ b/SHADE_Engine/src/Math/Transform/SHTransformComponent.h @@ -56,32 +56,38 @@ namespace SHADE /* Getter Functions */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] bool HasChanged () const noexcept; + [[nodiscard]] bool HasChanged () const noexcept; - [[nodiscard]] const SHVec3& GetLocalPosition () const noexcept; - [[nodiscard]] const SHVec3& GetLocalRotation () const noexcept; - [[nodiscard]] const SHVec3& GetLocalScale () const noexcept; - [[nodiscard]] const SHVec3& GetWorldPosition () const noexcept; - [[nodiscard]] const SHVec3& GetWorldRotation () const noexcept; - [[nodiscard]] const SHVec3& GetWorldScale () const noexcept; + [[nodiscard]] const SHVec3& GetLocalPosition () const noexcept; + [[nodiscard]] const SHVec3& GetLocalRotation () const noexcept; + [[nodiscard]] SHVec3 GetLocalRotationDeg () const noexcept; + [[nodiscard]] const SHVec3& GetLocalScale () const noexcept; + [[nodiscard]] const SHVec3& GetWorldPosition () const noexcept; + [[nodiscard]] const SHVec3& GetWorldRotation () const noexcept; + [[nodiscard]] SHVec3 GetWorldRotationDeg () const noexcept; + [[nodiscard]] const SHVec3& GetWorldScale () const noexcept; - [[nodiscard]] const SHMatrix& GetLocalToWorld () const noexcept; - [[nodiscard]] SHMatrix GetWorldToLocal () const noexcept; + [[nodiscard]] const SHMatrix& GetLocalToWorld () const noexcept; + [[nodiscard]] SHMatrix GetWorldToLocal () const noexcept; - [[nodiscard]] const SHMatrix& GetTRS () const noexcept; + [[nodiscard]] const SHMatrix& GetTRS () const noexcept; /*---------------------------------------------------------------------------------*/ /* Setter Functions */ /*---------------------------------------------------------------------------------*/ - void SetLocalPosition (const SHVec3& newLocalPosition) noexcept; - void SetLocalRotation (const SHVec3& newLocalRotation) noexcept; - void SetLocalRotation (float pitch, float yaw, float roll) noexcept; - void SetLocalScale (const SHVec3& newLocalScale) noexcept; - void SetWorldPosition (const SHVec3& newWorldPosition) noexcept; - void SetWorldRotation (const SHVec3& newWorldRotation) noexcept; - void SetWorldRotation (float pitch, float yaw, float roll) noexcept; - void SetWorldScale (const SHVec3& newWorldScale) noexcept; + void SetLocalPosition (const SHVec3& newLocalPosition) noexcept; + void SetLocalRotation (const SHVec3& newLocalRotation) noexcept; + void SetLocalRotationDeg (SHVec3 newLocalRotationDeg) noexcept; + void SetLocalRotation (float pitch, float yaw, float roll) noexcept; + void SetLocalRotationDeg (float pitch, float yaw, float roll) noexcept; + void SetLocalScale (const SHVec3& newLocalScale) noexcept; + void SetWorldPosition (const SHVec3& newWorldPosition) noexcept; + void SetWorldRotation (const SHVec3& newWorldRotation) noexcept; + void SetWorldRotationDeg (const SHVec3& newWorldRotation) noexcept; + void SetWorldRotation (float pitch, float yaw, float roll) noexcept; + void SetWorldRotationDeg (float pitch, float yaw, float roll) noexcept; + void SetWorldScale (const SHVec3& newWorldScale) noexcept; private: /*---------------------------------------------------------------------------------*/ -- 2.40.1 From db751bd1418955f3b35be6e384aeedff65cdf438 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Sat, 22 Oct 2022 18:22:26 +0800 Subject: [PATCH 06/10] Editor & Scripts can now override Physics-Based objects' Transforms --- Assets/Editor/Layouts/UserLayout.ini | 2 +- .../src/Application/SBApplication.cpp | 4 +- .../src/Math/Transform/SHTransformSystem.cpp | 44 +++++++++++++++++-- .../src/Math/Transform/SHTransformSystem.h | 42 +++++++++++++++--- 4 files changed, 80 insertions(+), 12 deletions(-) diff --git a/Assets/Editor/Layouts/UserLayout.ini b/Assets/Editor/Layouts/UserLayout.ini index 396b853b..7b0a70f8 100644 --- a/Assets/Editor/Layouts/UserLayout.ini +++ b/Assets/Editor/Layouts/UserLayout.ini @@ -38,7 +38,7 @@ Collapsed=0 DockId=0x00000002,0 [Docking][Data] -DockSpace ID=0xC5C9B8AB Window=0xBE4044E9 Pos=-227,-1256 Size=1920,1012 Split=X +DockSpace ID=0xC5C9B8AB Window=0xBE4044E9 Pos=8,79 Size=1920,1012 Split=X DockNode ID=0x00000005 Parent=0xC5C9B8AB SizeRef=1481,1036 Split=X DockNode ID=0x00000001 Parent=0x00000005 SizeRef=349,1036 Split=Y Selected=0x1E6EB881 DockNode ID=0x00000003 Parent=0x00000001 SizeRef=225,94 Selected=0x1E6EB881 diff --git a/SHADE_Application/src/Application/SBApplication.cpp b/SHADE_Application/src/Application/SBApplication.cpp index 8733e7b9..3675e275 100644 --- a/SHADE_Application/src/Application/SBApplication.cpp +++ b/SHADE_Application/src/Application/SBApplication.cpp @@ -83,11 +83,13 @@ namespace Sandbox SHSystemManager::RegisterRoutine(); SHSystemManager::RegisterRoutine(); + SHSystemManager::RegisterRoutine(); + SHSystemManager::RegisterRoutine(); SHSystemManager::RegisterRoutine(); SHSystemManager::RegisterRoutine(); - SHSystemManager::RegisterRoutine(); + SHSystemManager::RegisterRoutine(); SHSystemManager::RegisterRoutine(); SHSystemManager::RegisterRoutine(); diff --git a/SHADE_Engine/src/Math/Transform/SHTransformSystem.cpp b/SHADE_Engine/src/Math/Transform/SHTransformSystem.cpp index a3ca3e75..4b4f1cce 100644 --- a/SHADE_Engine/src/Math/Transform/SHTransformSystem.cpp +++ b/SHADE_Engine/src/Math/Transform/SHTransformSystem.cpp @@ -28,8 +28,12 @@ namespace SHADE /* Constructors & Destructor Definitions */ /*-----------------------------------------------------------------------------------*/ - SHTransformSystem::TransformUpdateRoutine::TransformUpdateRoutine() - : SHSystemRoutine { "Transform Update", true } + SHTransformSystem::TransformPostLogicUpdate::TransformPostLogicUpdate() + : SHSystemRoutine { "Transform Post-Logic Update", true } + {} + + SHTransformSystem::TransformPostPhysicsUpdate::TransformPostPhysicsUpdate() + : SHSystemRoutine { "Transform Post-Physics Update", false } {} @@ -37,13 +41,20 @@ namespace SHADE /* Public Function Member Definitions */ /*-----------------------------------------------------------------------------------*/ - void SHTransformSystem::TransformUpdateRoutine::Execute(double) noexcept + void SHTransformSystem::TransformPostLogicUpdate::Execute(double) noexcept { // Get the current scene graph to traverse and update const auto& SCENE_GRAPH = SHSceneManager::GetCurrentSceneGraph(); UpdateEntity(SCENE_GRAPH.GetRoot()); } + void SHTransformSystem::TransformPostPhysicsUpdate::Execute(double dt) noexcept + { + // Get the current scene graph to traverse and update + const auto& SCENE_GRAPH = SHSceneManager::GetCurrentSceneGraph(); + UpdateEntityAndClear(SCENE_GRAPH.GetRoot()); + } + void SHTransformSystem::Init() { std::shared_ptr thisReceiver { std::make_shared>(this, &SHTransformSystem::ChangeParent) }; @@ -175,6 +186,33 @@ namespace SHADE } UpdateEntity(child); + } + } + + + void SHTransformSystem::UpdateEntityAndClear(const SHSceneNode* node) + { + const auto* NODE_TRANSFORM = SHComponentManager::GetComponent_s(node->GetEntityID()); + const bool HAS_PARENT_CHANGED = NODE_TRANSFORM && NODE_TRANSFORM->dirty; + + for (const auto* child : node->GetChildren()) + { + auto* childTransform = SHComponentManager::GetComponent_s(child->GetEntityID()); + if (childTransform) + { + // Only update if node in hierarchy and component are both active + const bool IS_NODE_ACTIVE = child->IsActive(); + if (IS_NODE_ACTIVE && childTransform->isActive) + { + if (childTransform->dirty || HAS_PARENT_CHANGED) + { + UpdateTransform(*childTransform, NODE_TRANSFORM); + childTransform->dirty = true; + } + } + } + + UpdateEntityAndClear(child); // Clear dirty flag after all children are updated if (childTransform) diff --git a/SHADE_Engine/src/Math/Transform/SHTransformSystem.h b/SHADE_Engine/src/Math/Transform/SHTransformSystem.h index 95957830..256c1561 100644 --- a/SHADE_Engine/src/Math/Transform/SHTransformSystem.h +++ b/SHADE_Engine/src/Math/Transform/SHTransformSystem.h @@ -45,25 +45,52 @@ namespace SHADE /* System Routines */ /*---------------------------------------------------------------------------------*/ - class SH_API TransformUpdateRoutine final: public SHSystemRoutine + class SH_API TransformPostLogicUpdate final: public SHSystemRoutine { public: /*-------------------------------------------------------------------------------*/ /* Constructors & Destructor */ /*-------------------------------------------------------------------------------*/ - TransformUpdateRoutine (); - ~TransformUpdateRoutine () = default; + TransformPostLogicUpdate (); + ~TransformPostLogicUpdate () = default; - TransformUpdateRoutine (const TransformUpdateRoutine&) = delete; - TransformUpdateRoutine (TransformUpdateRoutine&&) = delete; + TransformPostLogicUpdate (const TransformPostLogicUpdate&) = delete; + TransformPostLogicUpdate (TransformPostLogicUpdate&&) = delete; /*-------------------------------------------------------------------------------*/ /* Operator Overloads */ /*-------------------------------------------------------------------------------*/ - TransformUpdateRoutine& operator= (const TransformUpdateRoutine&) = delete; - TransformUpdateRoutine& operator= (TransformUpdateRoutine&&) = delete; + TransformPostLogicUpdate& operator= (const TransformPostLogicUpdate&) = delete; + TransformPostLogicUpdate& operator= (TransformPostLogicUpdate&&) = delete; + + /*-------------------------------------------------------------------------------*/ + /* Function Members */ + /*-------------------------------------------------------------------------------*/ + + void Execute(double dt) noexcept override; + }; + + class SH_API TransformPostPhysicsUpdate final: public SHSystemRoutine + { + public: + /*-------------------------------------------------------------------------------*/ + /* Constructors & Destructor */ + /*-------------------------------------------------------------------------------*/ + + TransformPostPhysicsUpdate (); + ~TransformPostPhysicsUpdate () = default; + + TransformPostPhysicsUpdate (const TransformPostPhysicsUpdate&) = delete; + TransformPostPhysicsUpdate (TransformPostPhysicsUpdate&&) = delete; + + /*-------------------------------------------------------------------------------*/ + /* Operator Overloads */ + /*-------------------------------------------------------------------------------*/ + + TransformPostPhysicsUpdate& operator= (const TransformPostPhysicsUpdate&) = delete; + TransformPostPhysicsUpdate& operator= (TransformPostPhysicsUpdate&&) = delete; /*-------------------------------------------------------------------------------*/ /* Function Members */ @@ -88,6 +115,7 @@ namespace SHADE static void UpdateChildrenLocalTransforms (SHSceneNode* node); static void UpdateEntity (const SHSceneNode* node); + static void UpdateEntityAndClear (const SHSceneNode* node); static void UpdateTransform (SHTransformComponent& tf, const SHTransformComponent* parent = nullptr); }; -- 2.40.1 From ebfcf1c6bbbeae4c8a16c7d96ed3be9b4205fb6c Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Sat, 22 Oct 2022 20:16:38 +0800 Subject: [PATCH 07/10] Transform now stores orientation as Quaternions. Interface unchanged --- SHADE_Application/src/Scenes/SBTestScene.cpp | 6 +- SHADE_Engine/src/Math/SHMatrix.cpp | 81 +++----- SHADE_Engine/src/Math/SHMatrix.h | 2 + SHADE_Engine/src/Math/SHQuaternion.cpp | 174 +++++++----------- SHADE_Engine/src/Math/SHQuaternion.h | 9 +- .../src/Math/Transform/SHTransform.cpp | 18 +- SHADE_Engine/src/Math/Transform/SHTransform.h | 22 +-- .../Math/Transform/SHTransformComponent.cpp | 78 +++----- .../src/Math/Transform/SHTransformComponent.h | 36 ++-- .../src/Math/Transform/SHTransformSystem.cpp | 75 ++++---- .../src/Math/Transform/SHTransformSystem.h | 3 +- SHADE_Engine/src/Physics/SHPhysicsObject.cpp | 6 +- 12 files changed, 209 insertions(+), 301 deletions(-) diff --git a/SHADE_Application/src/Scenes/SBTestScene.cpp b/SHADE_Application/src/Scenes/SBTestScene.cpp index be09768b..23259ee4 100644 --- a/SHADE_Application/src/Scenes/SBTestScene.cpp +++ b/SHADE_Application/src/Scenes/SBTestScene.cpp @@ -104,10 +104,10 @@ namespace Sandbox transform.SetWorldRotation(SHMath::GenerateRandomNumber(), SHMath::GenerateRandomNumber(), SHMath::GenerateRandomNumber()); transform.SetWorldScale(TEST_OBJ_SCALE); - if (const bool IS_EVEN = (y * NUM_ROWS + x) % 2; IS_EVEN) + //if (const bool IS_EVEN = (y * NUM_ROWS + x) % 2; IS_EVEN) collider.AddBoundingBox(SHVec3::One * 0.5f, SHVec3::Zero); - else - collider.AddBoundingSphere(0.5f, SHVec3::Zero); + //else + // collider.AddBoundingSphere(0.5f, SHVec3::Zero); stressTestObjects.emplace_back(entity); } diff --git a/SHADE_Engine/src/Math/SHMatrix.cpp b/SHADE_Engine/src/Math/SHMatrix.cpp index 571fa4e0..ec3951e2 100644 --- a/SHADE_Engine/src/Math/SHMatrix.cpp +++ b/SHADE_Engine/src/Math/SHMatrix.cpp @@ -295,32 +295,33 @@ namespace SHADE ) != 0; } + SHMatrix::operator XMMATRIX() const noexcept + { + return XMLoadFloat4x4(this); + } + SHMatrix operator*(float lhs, const SHMatrix& rhs) noexcept { return rhs * lhs; } - /*-----------------------------------------------------------------------------------*/ /* Function Member Definitions */ /*-----------------------------------------------------------------------------------*/ void SHMatrix::Transpose() noexcept { - const XMMATRIX M = XMLoadFloat4x4(this); - XMStoreFloat4x4(this, XMMatrixTranspose(M)); + XMStoreFloat4x4(this, XMMatrixTranspose(*this)); } void SHMatrix::Invert() noexcept { - const XMMATRIX M = XMLoadFloat4x4(this); - XMStoreFloat4x4(this, XMMatrixInverse(nullptr, M)); + XMStoreFloat4x4(this, XMMatrixInverse(nullptr, *this)); } float SHMatrix::Determinant() const noexcept { - const XMMATRIX M = XMLoadFloat4x4(this); - return XMVectorGetX(XMMatrixDeterminant(M)); + return XMVectorGetX(XMMatrixDeterminant(*this)); } std::string SHMatrix::ToString() const noexcept @@ -337,9 +338,8 @@ namespace SHADE bool SHMatrix::Decompose(SHVec3& translation, SHVec3& rotation, SHVec3& scale) const noexcept { XMVECTOR s, r, t; - const XMMATRIX M = XMLoadFloat4x4(this); - if (!XMMatrixDecompose(&s, &r, &t, M)) + if (!XMMatrixDecompose(&s, &r, &t, *this)) return false; SHQuaternion orientation; @@ -356,9 +356,8 @@ namespace SHADE bool SHMatrix::Decompose(SHVec3& translation, SHQuaternion& orientation, SHVec3& scale) const noexcept { XMVECTOR s, r, t; - const XMMATRIX M = XMLoadFloat4x4(this); - if (!XMMatrixDecompose(&s, &r, &t, M)) + if (!XMMatrixDecompose(&s, &r, &t, *this)) return false; XMStoreFloat3(&scale, s); @@ -376,8 +375,7 @@ namespace SHADE { SHMatrix result; - const XMMATRIX M = XMLoadFloat4x4(&matrix); - XMStoreFloat4x4(&result, XMMatrixTranspose(M)); + XMStoreFloat4x4(&result, XMMatrixTranspose(matrix)); return result; } @@ -385,8 +383,7 @@ namespace SHADE { SHMatrix result; - const XMMATRIX M = XMLoadFloat4x4(&matrix); - XMStoreFloat4x4(&result, XMMatrixInverse(nullptr, M)); + XMStoreFloat4x4(&result, XMMatrixInverse(nullptr, matrix)); return result; } @@ -401,8 +398,8 @@ namespace SHADE SHMatrix SHMatrix::Translate(const SHVec3& pos) noexcept { SHMatrix result; - XMStoreFloat4x4(&result, XMMatrixTranslation(pos.x, pos.y, pos.z)); + XMStoreFloat4x4(&result, XMMatrixTranslation(pos.x, pos.y, pos.z)); return result; } @@ -410,25 +407,23 @@ namespace SHADE { SHMatrix result; - const XMVECTOR A = XMLoadFloat3(&axis); - XMStoreFloat4x4(&result, XMMatrixRotationAxis(A, angleInRad)); - + XMStoreFloat4x4(&result, XMMatrixRotationAxis(axis, angleInRad)); return result; } SHMatrix SHMatrix::Rotate(float yaw, float pitch, float roll) noexcept { SHMatrix result; - XMStoreFloat4x4(&result, XMMatrixRotationRollPitchYaw(pitch, yaw, roll)); + XMStoreFloat4x4(&result, XMMatrixRotationRollPitchYaw(pitch, yaw, roll)); return result; } SHMatrix SHMatrix::Rotate(const SHVec3& eulerAngles) noexcept { SHMatrix result; - XMStoreFloat4x4(&result, XMMatrixRotationRollPitchYaw(eulerAngles.x, eulerAngles.y, eulerAngles.z)); + XMStoreFloat4x4(&result, XMMatrixRotationRollPitchYawFromVector(eulerAngles)); return result; } @@ -436,57 +431,55 @@ namespace SHADE { SHMatrix result; - const XMVECTOR Q = XMLoadFloat4(&q); - XMStoreFloat4x4(&result, XMMatrixRotationQuaternion(Q)); - + XMStoreFloat4x4(&result, XMMatrixRotationQuaternion(q)); return result; } SHMatrix SHMatrix::RotateX(float angleInRad) noexcept { SHMatrix result; - XMStoreFloat4x4(&result, XMMatrixRotationX(angleInRad)); + XMStoreFloat4x4(&result, XMMatrixRotationX(angleInRad)); return result; } SHMatrix SHMatrix::RotateY(float angleInRad) noexcept { SHMatrix result; - XMStoreFloat4x4(&result, XMMatrixRotationY(angleInRad)); + XMStoreFloat4x4(&result, XMMatrixRotationY(angleInRad)); return result; } SHMatrix SHMatrix::RotateZ(float angleInRad) noexcept { SHMatrix result; - XMStoreFloat4x4(&result, XMMatrixRotationZ(angleInRad)); + XMStoreFloat4x4(&result, XMMatrixRotationZ(angleInRad)); return result; } SHMatrix SHMatrix::Scale(float uniformScaleFactor) noexcept { SHMatrix result; - XMStoreFloat4x4(&result, XMMatrixScaling(uniformScaleFactor, uniformScaleFactor, uniformScaleFactor)); + XMStoreFloat4x4(&result, XMMatrixScaling(uniformScaleFactor, uniformScaleFactor, uniformScaleFactor)); return result; } SHMatrix SHMatrix::Scale(float x, float y, float z) noexcept { SHMatrix result; - XMStoreFloat4x4(&result, XMMatrixScaling(x, y, z)); + XMStoreFloat4x4(&result, XMMatrixScaling(x, y, z)); return result; } SHMatrix SHMatrix::Scale(const SHVec3& scale) noexcept { SHMatrix result; - XMStoreFloat4x4(&result, XMMatrixScaling(scale.x, scale.y, scale.z)); + XMStoreFloat4x4(&result, XMMatrixScalingFromVector(scale)); return result; } @@ -494,12 +487,7 @@ namespace SHADE { SHMatrix result; - const XMVECTOR EYE = XMLoadFloat3(&eye); - const XMVECTOR TGT = XMLoadFloat3(&target); - const XMVECTOR UP = XMLoadFloat3(&up); - - XMStoreFloat4x4(&result, XMMatrixLookAtRH(EYE, TGT, UP)); - + XMStoreFloat4x4(&result, XMMatrixLookAtRH(eye, target, up)); return result; } @@ -507,12 +495,7 @@ namespace SHADE { SHMatrix result; - const XMVECTOR EYE = XMLoadFloat3(&eye); - const XMVECTOR TGT = XMLoadFloat3(&target); - const XMVECTOR UP = XMLoadFloat3(&up); - - XMStoreFloat4x4(&result, XMMatrixLookAtLH(EYE, TGT, UP)); - + XMStoreFloat4x4(&result, XMMatrixLookAtLH(eye, target, up)); return result; } @@ -522,8 +505,8 @@ namespace SHADE const SHVec3 FWD_HAT = SHVec3::Normalise(-forward); - const XMVECTOR Z_HAT = XMVector3Normalize(XMLoadFloat3(&FWD_HAT)); - const XMVECTOR X_HAT = XMVector3Normalize(XMVector3Cross(XMLoadFloat3(&up), Z_HAT)); + const XMVECTOR Z_HAT = XMVector3Normalize(FWD_HAT); + const XMVECTOR X_HAT = XMVector3Normalize(XMVector3Cross(up, Z_HAT)); const XMVECTOR Y_HAT = XMVector3Cross(Z_HAT, X_HAT); XMStoreFloat3(reinterpret_cast(&result._11), X_HAT); @@ -543,8 +526,8 @@ namespace SHADE const SHVec3 FWD_HAT = SHVec3::Normalise(forward); - const XMVECTOR Z_HAT = XMVector3Normalize(XMLoadFloat3(&FWD_HAT)); - const XMVECTOR X_HAT = XMVector3Normalize(XMVector3Cross(XMLoadFloat3(&up), Z_HAT)); + const XMVECTOR Z_HAT = XMVector3Normalize(FWD_HAT); + const XMVECTOR X_HAT = XMVector3Normalize(XMVector3Cross(up, Z_HAT)); const XMVECTOR Y_HAT = XMVector3Cross(Z_HAT, X_HAT); XMStoreFloat3(reinterpret_cast(&result._11), X_HAT); @@ -563,7 +546,6 @@ namespace SHADE SHMatrix result; XMStoreFloat4x4(&result, XMMatrixPerspectiveFovRH(fov, aspectRatio, nearPlane, farPlane)); - return result; } @@ -572,7 +554,6 @@ namespace SHADE SHMatrix result; XMStoreFloat4x4(&result, XMMatrixPerspectiveFovLH(fov, aspectRatio, nearPlane, farPlane)); - return result; } @@ -581,7 +562,6 @@ namespace SHADE SHMatrix result; XMStoreFloat4x4(&result, XMMatrixPerspectiveRH(width, height, nearPlane, farPlane)); - return result; } @@ -590,7 +570,6 @@ namespace SHADE SHMatrix result; XMStoreFloat4x4(&result, XMMatrixPerspectiveLH(width, height, nearPlane, farPlane)); - return result; } @@ -599,7 +578,6 @@ namespace SHADE SHMatrix result; XMStoreFloat4x4(&result, XMMatrixOrthographicRH(width, height, nearPlane, farPlane)); - return result; } @@ -608,7 +586,6 @@ namespace SHADE SHMatrix result; XMStoreFloat4x4(&result, XMMatrixOrthographicLH(width, height, nearPlane, farPlane)); - return result; } diff --git a/SHADE_Engine/src/Math/SHMatrix.h b/SHADE_Engine/src/Math/SHMatrix.h index 7a662478..4d8f1bfe 100644 --- a/SHADE_Engine/src/Math/SHMatrix.h +++ b/SHADE_Engine/src/Math/SHMatrix.h @@ -77,6 +77,8 @@ namespace SHADE SHMatrix& operator= (const SHMatrix& rhs) = default; SHMatrix& operator= (SHMatrix&& rhs) = default; + operator DirectX::XMMATRIX () const noexcept; + SHMatrix& operator+= (const SHMatrix& rhs) noexcept; SHMatrix& operator-= (const SHMatrix& rhs) noexcept; SHMatrix& operator*= (const SHMatrix& rhs) noexcept; diff --git a/SHADE_Engine/src/Math/SHQuaternion.cpp b/SHADE_Engine/src/Math/SHQuaternion.cpp index 33c568a5..924ac67a 100644 --- a/SHADE_Engine/src/Math/SHQuaternion.cpp +++ b/SHADE_Engine/src/Math/SHQuaternion.cpp @@ -40,40 +40,10 @@ namespace SHADE : XMFLOAT4( _x, _y, _z, _w ) {} - SHQuaternion::SHQuaternion(float yaw, float pitch, float roll) noexcept - : XMFLOAT4( 0.0f, 0.0f, 0.0f, 1.0f ) - { - XMStoreFloat4(this, XMQuaternionRotationRollPitchYaw(pitch, yaw, roll)); - } - - SHQuaternion::SHQuaternion(const SHVec3& eulerAngles) noexcept - : XMFLOAT4( 0.0f, 0.0f, 0.0f, 1.0f ) - { - const XMVECTOR V = XMLoadFloat3(&eulerAngles); - XMStoreFloat4(this, XMQuaternionRotationRollPitchYawFromVector(V)); - } - - SHQuaternion::SHQuaternion(const SHVec3& axis, float angleInRad) noexcept - : XMFLOAT4( 0.0f, 0.0f, 0.0f, 1.0f ) - { - const XMVECTOR AXIS = XMLoadFloat3(&axis); - XMStoreFloat4(this, XMQuaternionRotationAxis(AXIS, angleInRad)); - } - - SHQuaternion::SHQuaternion(const SHMatrix& rotationMatrix) noexcept - : XMFLOAT4( 0.0f, 0.0f, 0.0f, 1.0f ) - { - const XMMATRIX M = XMLoadFloat4x4(&rotationMatrix); - XMStoreFloat4(this, XMQuaternionRotationMatrix(M)); - } - SHQuaternion::SHQuaternion(const reactphysics3d::Vector3& rp3dEuler) noexcept : XMFLOAT4( 0.0f, 0.0f, 0.0f, 1.0f ) { - const SHVec3& SHADE_VEC{ rp3dEuler }; - - const XMVECTOR V = XMLoadFloat3(&SHADE_VEC); - XMStoreFloat4(this, XMQuaternionRotationRollPitchYawFromVector(V)); + XMStoreFloat4(this, XMQuaternionRotationRollPitchYawFromVector(SHVec3 { rp3dEuler })); } SHQuaternion::SHQuaternion(const reactphysics3d::Quaternion& rp3dQuat) noexcept @@ -113,10 +83,7 @@ namespace SHADE { SHQuaternion result; - const XMVECTOR Q1 = XMLoadFloat4(this); - const XMVECTOR Q2 = XMLoadFloat4(&rhs); - - XMStoreFloat4(&result, XMVectorAdd(Q1, Q2)); + XMStoreFloat4(&result, XMVectorAdd(*this, rhs)); return result; } @@ -124,10 +91,7 @@ namespace SHADE { SHQuaternion result; - const XMVECTOR Q1 = XMLoadFloat4(this); - const XMVECTOR Q2 = XMLoadFloat4(&rhs); - - XMStoreFloat4(&result, XMVectorSubtract(Q1, Q2)); + XMStoreFloat4(&result, XMVectorSubtract(*this, rhs)); return result; } @@ -135,9 +99,7 @@ namespace SHADE { SHQuaternion result; - const XMVECTOR Q = XMLoadFloat4(this); - - XMStoreFloat4(&result, XMVectorNegate(Q)); + XMStoreFloat4(&result, XMVectorNegate(*this)); return result; } @@ -145,10 +107,7 @@ namespace SHADE { SHQuaternion result; - const XMVECTOR Q1 = XMLoadFloat4(this); - const XMVECTOR Q2 = XMLoadFloat4(&rhs); - - XMStoreFloat4(&result, XMQuaternionMultiply(Q1, Q2)); + XMStoreFloat4(&result, XMQuaternionMultiply(*this, rhs)); return result; } @@ -156,9 +115,7 @@ namespace SHADE { SHQuaternion result; - const XMVECTOR Q = XMLoadFloat4(this); - - XMStoreFloat4(&result, XMVectorScale(Q, rhs)); + XMStoreFloat4(&result, XMVectorScale(*this, rhs)); return result; } @@ -166,27 +123,18 @@ namespace SHADE { SHQuaternion result; - const XMVECTOR Q1 = XMLoadFloat4(this); - const XMVECTOR Q2 = XMQuaternionInverse(XMLoadFloat4(&rhs)); - - XMStoreFloat4(&result, XMQuaternionMultiply(Q1, Q2)); + XMStoreFloat4(&result, XMQuaternionMultiply(*this, XMQuaternionInverse(rhs))); return result; } bool SHQuaternion::operator==(const SHQuaternion& rhs) const noexcept { - const XMVECTOR Q1 = XMLoadFloat4(this); - const XMVECTOR Q2 = XMLoadFloat4(&rhs); - - return XMQuaternionEqual(Q1, Q2); + return XMQuaternionEqual(*this, rhs); } bool SHQuaternion::operator!=(const SHQuaternion& rhs) const noexcept { - const XMVECTOR Q1 = XMLoadFloat4(this); - const XMVECTOR Q2 = XMLoadFloat4(&rhs); - - return XMQuaternionNotEqual(Q1, Q2); + return XMQuaternionNotEqual(*this, rhs); } SHQuaternion::operator reactphysics3d::Quaternion() const noexcept @@ -199,6 +147,11 @@ namespace SHADE return reactphysics3d::Vector3{ ToEuler() }; } + SHQuaternion::operator XMVECTOR() const noexcept + { + return XMLoadFloat4(this); + } + SHQuaternion operator*(float lhs, const SHQuaternion& rhs) noexcept { return rhs * lhs; @@ -213,8 +166,7 @@ namespace SHADE XMVECTOR axis; float angle; - const XMVECTOR Q = XMLoadFloat4(this); - XMQuaternionToAxisAngle(&axis, &angle, Q); + XMQuaternionToAxisAngle(&axis, &angle, *this); return angle; } @@ -223,8 +175,7 @@ namespace SHADE XMVECTOR axis; float angle; - const XMVECTOR Q = XMLoadFloat4(this); - XMQuaternionToAxisAngle(&axis, &angle, Q); + XMQuaternionToAxisAngle(&axis, &angle, *this); return SHVec4{XMVectorGetX(axis), XMVectorGetY(axis), XMVectorGetZ(axis), angle}; @@ -238,28 +189,22 @@ namespace SHADE void SHQuaternion::Invert() noexcept { - const XMVECTOR Q = XMLoadFloat4(this); - XMStoreFloat4(this, XMQuaternionInverse(Q)); + XMStoreFloat4(this, XMQuaternionInverse(*this)); } float SHQuaternion::Length() const noexcept { - const XMVECTOR Q = XMLoadFloat4(this); - return XMVectorGetX(XMQuaternionLength(Q)); + return XMVectorGetX(XMQuaternionLength(*this)); } float SHQuaternion::LengthSquared() const noexcept { - const XMVECTOR Q = XMLoadFloat4(this); - return XMVectorGetX(XMQuaternionLengthSq(Q)); + return XMVectorGetX(XMQuaternionLengthSq(*this)); } float SHQuaternion::Dot(const SHQuaternion& rhs) const noexcept { - const XMVECTOR Q1 = XMLoadFloat4(this); - const XMVECTOR Q2 = XMLoadFloat4(&rhs); - - return XMVectorGetX(XMQuaternionDot(Q1, Q2)); + return XMVectorGetX(XMQuaternionDot(*this, rhs)); } SHQuaternion SHQuaternion::RotateTowards(const SHQuaternion&, float) const noexcept @@ -273,29 +218,29 @@ namespace SHADE SHVec3 SHQuaternion::ToEuler() const noexcept { - const float xx = x * x; - const float yy = y * y; - const float zz = z * z; + const float XX = x * x; + const float YY = y * y; + const float ZZ = z * z; - const float m31 = 2.f * x * z + 2.f * y * w; - const float m32 = 2.f * y * z - 2.f * x * w; - const float m33 = 1.f - 2.f * xx - 2.f * yy; + const float M_31 = 2.f * x * z + 2.f * y * w; + const float M_32 = 2.f * y * z - 2.f * x * w; + const float M_33 = 1.f - 2.f * XX - 2.f * YY; - const float cy = sqrtf(m33 * m33 + m31 * m31); - const float cx = atan2f(-m32, cy); - if (cy > 16.0f * SHMath::EPSILON) + const float CY = sqrtf(M_33 * M_33 + M_31 * M_31); + const float CX = atan2f(-M_32, CY); + if (CY > 16.0f * SHMath::EPSILON) { - const float m12 = 2.f * x * y + 2.f * z * w; - const float m22 = 1.f - 2.f * xx - 2.f * zz; + const float M_12 = 2.f * x * y + 2.f * z * w; + const float M_22 = 1.f - 2.f * XX - 2.f * ZZ; - return SHVec3(cx, atan2f(m31, m33), atan2f(m12, m22)); + return SHVec3(CX, atan2f(M_31, M_33), atan2f(M_12, M_22)); } else { - const float m11 = 1.f - 2.f * yy - 2.f * zz; - const float m21 = 2.f * x * y - 2.f * z * w; + const float m11 = 1.f - 2.f * YY - 2.f * ZZ; + const float m21 = 2.f * x * y - 2.f * z * w; - return SHVec3(cx, 0.f, atan2f(-m21, m11)); + return SHVec3(CX, 0.f, atan2f(-m21, m11)); } } @@ -311,13 +256,43 @@ namespace SHADE /* Static Function Member Definitions */ /*-----------------------------------------------------------------------------------*/ + SHQuaternion SHQuaternion::FromEuler(const SHVec3& eulerAngles) noexcept + { + SHQuaternion result; + + XMStoreFloat4(&result, XMQuaternionRotationRollPitchYawFromVector(eulerAngles)); + return result; + } + + SHQuaternion SHQuaternion::FromPitchYawRoll(float pitch, float yaw, float roll) noexcept + { + SHQuaternion result; + + XMStoreFloat4(&result, XMQuaternionRotationRollPitchYaw(pitch, yaw, roll)); + return result; + } + + SHQuaternion SHQuaternion::FromAxisAngle(const SHVec3& axis, float angle) noexcept + { + SHQuaternion result; + + XMStoreFloat4(&result, XMQuaternionRotationAxis(axis, angle)); + return result; + } + + SHQuaternion SHQuaternion::FromRotationMatrix(const SHMatrix& rotationMatrix) noexcept + { + SHQuaternion result; + + XMStoreFloat4(&result, XMQuaternionRotationMatrix(rotationMatrix)); + return result; + } + SHQuaternion SHQuaternion::Normalise(const SHQuaternion& q) noexcept { SHQuaternion result; - const XMVECTOR Q = XMLoadFloat4(&q); - - XMStoreFloat4(&result, XMQuaternionNormalize(Q)); + XMStoreFloat4(&result, XMQuaternionNormalize(q)); return result; } @@ -325,9 +300,7 @@ namespace SHADE { SHQuaternion result; - const XMVECTOR Q = XMLoadFloat4(&q); - - XMStoreFloat4(&result, XMQuaternionConjugate(Q)); + XMStoreFloat4(&result, XMQuaternionConjugate(q)); return result; } @@ -335,9 +308,7 @@ namespace SHADE { SHQuaternion result; - const XMVECTOR Q = XMLoadFloat4(&q); - XMStoreFloat4(&result, XMQuaternionInverse(Q)); - + XMStoreFloat4(&result, XMQuaternionInverse(q)); return result; } @@ -362,10 +333,7 @@ namespace SHADE { SHQuaternion result; - const XMVECTOR Q1 = XMLoadFloat4(&q1); - const XMVECTOR Q2 = XMLoadFloat4(&q2); - - XMStoreFloat4(&result, XMQuaternionSlerp(Q1, Q2, t)); + XMStoreFloat4(&result, XMQuaternionSlerp(q1, q2, t)); return result; } diff --git a/SHADE_Engine/src/Math/SHQuaternion.h b/SHADE_Engine/src/Math/SHQuaternion.h index c94907b5..f3ce3d61 100644 --- a/SHADE_Engine/src/Math/SHQuaternion.h +++ b/SHADE_Engine/src/Math/SHQuaternion.h @@ -51,9 +51,6 @@ namespace SHADE SHQuaternion () noexcept; SHQuaternion (float x, float y, float z, float w) noexcept; SHQuaternion (float yaw, float pitch, float roll) noexcept; - SHQuaternion (const SHVec3& eulerAngles) noexcept; - SHQuaternion (const SHVec3& axis, float angleInRad) noexcept; - SHQuaternion (const SHMatrix& rotationMatrix) noexcept; // Conversion from other math types @@ -87,6 +84,7 @@ namespace SHADE operator reactphysics3d::Quaternion () const noexcept; operator reactphysics3d::Vector3 () const noexcept; + operator DirectX::XMVECTOR () const noexcept; /*---------------------------------------------------------------------------------*/ /* Getter Functions */ @@ -113,6 +111,11 @@ namespace SHADE /* Static Function Members */ /*---------------------------------------------------------------------------------*/ + [[nodiscard]] static SHQuaternion FromEuler (const SHVec3& eulerAngles) noexcept; + [[nodiscard]] static SHQuaternion FromPitchYawRoll (float pitch, float yaw, float roll) noexcept; + [[nodiscard]] static SHQuaternion FromAxisAngle (const SHVec3& axis, float angle) noexcept; + [[nodiscard]] static SHQuaternion FromRotationMatrix(const SHMatrix& rotationMatrix) noexcept; + [[nodiscard]] static SHQuaternion Normalise (const SHQuaternion& q) noexcept; [[nodiscard]] static SHQuaternion Conjugate (const SHQuaternion& q) noexcept; [[nodiscard]] static SHQuaternion Inverse (const SHQuaternion& q) noexcept; diff --git a/SHADE_Engine/src/Math/Transform/SHTransform.cpp b/SHADE_Engine/src/Math/Transform/SHTransform.cpp index f51d73ec..ef7c5fda 100644 --- a/SHADE_Engine/src/Math/Transform/SHTransform.cpp +++ b/SHADE_Engine/src/Math/Transform/SHTransform.cpp @@ -26,15 +26,15 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ SHTransform::SHTransform() noexcept - : position { SHVec3::Zero } - , rotation { SHVec3::Zero } - , scale { SHVec3::One } + : position { SHVec3::Zero } + , orientation { SHQuaternion::Identity } + , scale { SHVec3::One } {} SHTransform::SHTransform(const SHVec3& pos, const SHVec3& rot, const SHVec3& scl) noexcept - : position { pos } - , rotation { rot } - , scale { scl } + : position { pos } + , orientation { SHQuaternion::FromEuler(rot) } + , scale { scl } {} /*-----------------------------------------------------------------------------------*/ @@ -43,12 +43,12 @@ namespace SHADE bool SHTransform::operator==(const SHTransform& rhs) const noexcept { - return !(position != rhs.position || rotation != rhs.rotation || scale != rhs.scale); + return !(position != rhs.position || orientation != rhs.orientation || scale != rhs.scale); } bool SHTransform::operator!=(const SHTransform& rhs) const noexcept { - return (position != rhs.position || rotation != rhs.rotation || scale != rhs.scale); + return (position != rhs.position || orientation != rhs.orientation || scale != rhs.scale); } /*-----------------------------------------------------------------------------------*/ @@ -59,7 +59,7 @@ namespace SHADE { const SHMatrix T = SHMatrix::Translate(position); - const SHMatrix R = SHMatrix::Rotate(rotation); + const SHMatrix R = SHMatrix::Rotate(orientation); const SHMatrix S = SHMatrix::Scale(scale); trs = S * R * T; diff --git a/SHADE_Engine/src/Math/Transform/SHTransform.h b/SHADE_Engine/src/Math/Transform/SHTransform.h index c1a0e565..2e7d236c 100644 --- a/SHADE_Engine/src/Math/Transform/SHTransform.h +++ b/SHADE_Engine/src/Math/Transform/SHTransform.h @@ -12,8 +12,8 @@ // Project Headers #include "SH_API.h" -#include "Math/Vector/SHVec2.h" #include "Math/Vector/SHVec3.h" +#include "Math/SHQuaternion.h" #include "Math/SHMatrix.h" namespace SHADE @@ -31,22 +31,23 @@ namespace SHADE static const SHTransform Identity; - SHVec3 position; - SHVec3 rotation; - SHVec3 scale; + SHVec3 position; + SHQuaternion orientation; + SHVec3 scale; - SHMatrix trs; + SHMatrix trs; /*---------------------------------------------------------------------------------*/ /* Constructors & Destructor */ /*---------------------------------------------------------------------------------*/ - SHTransform (const SHTransform&) = default; - SHTransform (SHTransform&&) = default; - ~SHTransform () = default; + SHTransform (const SHTransform&) = default; + SHTransform (SHTransform&&) = default; + ~SHTransform () = default; - SHTransform () noexcept; - SHTransform (const SHVec3& pos, const SHVec3& rot, const SHVec3& scl) noexcept; + SHTransform () noexcept; + SHTransform (const SHVec3& pos, const SHVec3& rot, const SHVec3& scl) noexcept; + SHTransform (const SHVec3& pos, const SHQuaternion& quat, const SHVec3& scl) noexcept; /*---------------------------------------------------------------------------------*/ /* Operator Overloads */ @@ -63,7 +64,6 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ const SHMatrix& ComputeTRS(); - }; } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Math/Transform/SHTransformComponent.cpp b/SHADE_Engine/src/Math/Transform/SHTransformComponent.cpp index a8b75497..306cde67 100644 --- a/SHADE_Engine/src/Math/Transform/SHTransformComponent.cpp +++ b/SHADE_Engine/src/Math/Transform/SHTransformComponent.cpp @@ -42,16 +42,12 @@ namespace SHADE const SHVec3& SHTransformComponent::GetLocalRotation() const noexcept { - return local.rotation; + return localRotation; } - SHVec3 SHTransformComponent::GetLocalRotationDeg() const noexcept + const SHQuaternion& SHTransformComponent::GetLocalOrientation() const noexcept { - SHVec3 rot = local.rotation; - rot.x = SHMath::RadiansToDegrees(rot.x); - rot.y = SHMath::RadiansToDegrees(rot.y); - rot.z = SHMath::RadiansToDegrees(rot.z); - return rot; + return local.orientation; } const SHVec3& SHTransformComponent::GetLocalScale() const noexcept @@ -66,16 +62,12 @@ namespace SHADE const SHVec3& SHTransformComponent::GetWorldRotation() const noexcept { - return world.rotation; + return worldRotation; } - SHVec3 SHTransformComponent::GetWorldRotationDeg() const noexcept + const SHQuaternion& SHTransformComponent::GetWorldOrientation() const noexcept { - SHVec3 rot = world.rotation; - rot.x = SHMath::RadiansToDegrees(rot.x); - rot.y = SHMath::RadiansToDegrees(rot.y); - rot.z = SHMath::RadiansToDegrees(rot.z); - return rot; + return world.orientation; } const SHVec3& SHTransformComponent::GetWorldScale() const noexcept @@ -111,32 +103,21 @@ namespace SHADE void SHTransformComponent::SetLocalRotation(const SHVec3& newLocalRotation) noexcept { dirty = true; - local.rotation = newLocalRotation; - } - - void SHTransformComponent::SetLocalRotationDeg(SHVec3 newLocalRotationDeg) noexcept - { - dirty = true; - - local.rotation.x = SHMath::DegreesToRadians(newLocalRotationDeg.x); - local.rotation.y = SHMath::DegreesToRadians(newLocalRotationDeg.y); - local.rotation.z = SHMath::DegreesToRadians(newLocalRotationDeg.z); + localRotation = newLocalRotation; } void SHTransformComponent::SetLocalRotation(float pitch, float yaw, float roll) noexcept { dirty = true; - local.rotation.x = pitch; - local.rotation.y = yaw; - local.rotation.z = roll; + localRotation.x = pitch; + localRotation.y = yaw; + localRotation.z = roll; } - void SHTransformComponent::SetLocalRotationDeg(float pitch, float yaw, float roll) noexcept + void SHTransformComponent::SetLocalOrientation(const SHQuaternion& newLocalOrientation) noexcept { - local.rotation.x = SHMath::DegreesToRadians(pitch); - local.rotation.y = SHMath::DegreesToRadians(yaw); - local.rotation.z = SHMath::DegreesToRadians(roll); + } void SHTransformComponent::SetLocalScale(const SHVec3& newLocalScale) noexcept @@ -157,41 +138,24 @@ namespace SHADE { dirty = true; - world.rotation = newWorldRotation; + worldRotation = newWorldRotation; updateQueue.push({ UpdateCommandType::WORLD_ROTATION, newWorldRotation }); } - void SHTransformComponent::SetWorldRotationDeg(const SHVec3& newWorldRotation) noexcept - { - dirty = true; - - world.rotation.x = SHMath::DegreesToRadians(newWorldRotation.x); - world.rotation.y = SHMath::DegreesToRadians(newWorldRotation.y); - world.rotation.z = SHMath::DegreesToRadians(newWorldRotation.z); - - updateQueue.push({ UpdateCommandType::WORLD_ROTATION, world.rotation }); - } - void SHTransformComponent::SetWorldRotation(float pitch, float yaw, float roll) noexcept { dirty = true; - world.rotation.x = pitch; - world.rotation.y = yaw; - world.rotation.z = roll; + worldRotation.x = pitch; + worldRotation.y = yaw; + worldRotation.z = roll; updateQueue.push({ UpdateCommandType::WORLD_ROTATION, SHVec3{ pitch, yaw, roll} }); } - void SHTransformComponent::SetWorldRotationDeg(float pitch, float yaw, float roll) noexcept + void SHTransformComponent::SetWorldOrientation(const SHQuaternion& newWorldOrientation) noexcept { - dirty = true; - - world.rotation.x = SHMath::DegreesToRadians(pitch); - world.rotation.y = SHMath::DegreesToRadians(yaw); - world.rotation.z = SHMath::DegreesToRadians(roll); - - updateQueue.push({ UpdateCommandType::WORLD_ROTATION, world.rotation }); + } void SHTransformComponent::SetWorldScale(const SHVec3& newWorldScale) noexcept @@ -210,7 +174,7 @@ RTTR_REGISTRATION using namespace rttr; registration::class_("Transform Component") - .property("Translate" , &SHTransformComponent::GetLocalPosition , &SHTransformComponent::SetLocalPosition ) - .property("Rotate" , &SHTransformComponent::GetLocalRotationDeg, select_overload(&SHTransformComponent::SetLocalRotationDeg)) - .property("Scale" , &SHTransformComponent::GetLocalScale , &SHTransformComponent::SetLocalScale ); + .property("Translate" , &SHTransformComponent::GetLocalPosition , &SHTransformComponent::SetLocalPosition ) + .property("Rotate" , &SHTransformComponent::GetLocalRotation , select_overload(&SHTransformComponent::SetLocalRotation) ) + .property("Scale" , &SHTransformComponent::GetLocalScale , &SHTransformComponent::SetLocalScale ); } \ No newline at end of file diff --git a/SHADE_Engine/src/Math/Transform/SHTransformComponent.h b/SHADE_Engine/src/Math/Transform/SHTransformComponent.h index 2a3fa7a0..d1d21bec 100644 --- a/SHADE_Engine/src/Math/Transform/SHTransformComponent.h +++ b/SHADE_Engine/src/Math/Transform/SHTransformComponent.h @@ -60,11 +60,11 @@ namespace SHADE [[nodiscard]] const SHVec3& GetLocalPosition () const noexcept; [[nodiscard]] const SHVec3& GetLocalRotation () const noexcept; - [[nodiscard]] SHVec3 GetLocalRotationDeg () const noexcept; + [[nodiscard]] const SHQuaternion& GetLocalOrientation () const noexcept; [[nodiscard]] const SHVec3& GetLocalScale () const noexcept; [[nodiscard]] const SHVec3& GetWorldPosition () const noexcept; [[nodiscard]] const SHVec3& GetWorldRotation () const noexcept; - [[nodiscard]] SHVec3 GetWorldRotationDeg () const noexcept; + [[nodiscard]] const SHQuaternion& GetWorldOrientation () const noexcept; [[nodiscard]] const SHVec3& GetWorldScale () const noexcept; [[nodiscard]] const SHMatrix& GetLocalToWorld () const noexcept; @@ -76,28 +76,30 @@ namespace SHADE /* Setter Functions */ /*---------------------------------------------------------------------------------*/ - void SetLocalPosition (const SHVec3& newLocalPosition) noexcept; - void SetLocalRotation (const SHVec3& newLocalRotation) noexcept; - void SetLocalRotationDeg (SHVec3 newLocalRotationDeg) noexcept; - void SetLocalRotation (float pitch, float yaw, float roll) noexcept; - void SetLocalRotationDeg (float pitch, float yaw, float roll) noexcept; - void SetLocalScale (const SHVec3& newLocalScale) noexcept; - void SetWorldPosition (const SHVec3& newWorldPosition) noexcept; - void SetWorldRotation (const SHVec3& newWorldRotation) noexcept; - void SetWorldRotationDeg (const SHVec3& newWorldRotation) noexcept; - void SetWorldRotation (float pitch, float yaw, float roll) noexcept; - void SetWorldRotationDeg (float pitch, float yaw, float roll) noexcept; - void SetWorldScale (const SHVec3& newWorldScale) noexcept; + void SetLocalPosition (const SHVec3& newLocalPosition) noexcept; + void SetLocalRotation (const SHVec3& newLocalRotation) noexcept; + void SetLocalRotation (float pitch, float yaw, float roll) noexcept; + void SetLocalOrientation (const SHQuaternion& newLocalOrientation) noexcept; + void SetLocalScale (const SHVec3& newLocalScale) noexcept; + void SetWorldPosition (const SHVec3& newWorldPosition) noexcept; + void SetWorldRotation (const SHVec3& newWorldRotation) noexcept; + void SetWorldRotation (float pitch, float yaw, float roll) noexcept; + void SetWorldOrientation (const SHQuaternion& newWorldOrientation) noexcept; + void SetWorldScale (const SHVec3& newWorldScale) noexcept; private: /*---------------------------------------------------------------------------------*/ /* Type Definitions */ /*---------------------------------------------------------------------------------*/ + // Differentiate between rotation and orientation for setters + // Setting a quaternion directly is different from using euler angle rotations. + enum class UpdateCommandType { WORLD_POSITION , WORLD_ROTATION + , WORLD_ORIENTATION , WORLD_SCALE }; @@ -120,6 +122,12 @@ namespace SHADE bool dirty; + // We store euler angle rotations separately to interface with transform quaternions. + // Reading quaternions are unreliable. + + SHVec3 localRotation; // Stored in degrees + SHVec3 worldRotation; // Stored in degrees + SHTransform local; // Local TRS holds Local To World Transform SHTransform world; diff --git a/SHADE_Engine/src/Math/Transform/SHTransformSystem.cpp b/SHADE_Engine/src/Math/Transform/SHTransformSystem.cpp index 4b4f1cce..3244db1b 100644 --- a/SHADE_Engine/src/Math/Transform/SHTransformSystem.cpp +++ b/SHADE_Engine/src/Math/Transform/SHTransformSystem.cpp @@ -17,6 +17,8 @@ #include "Scene/SHSceneManager.h" #include "ECS_Base/Managers/SHComponentManager.h" #include "ECS_Base/Managers/SHEntityManager.h" +#include "ECS_Base/Managers/SHSystemManager.h" +#include "Math/SHMathHelpers.h" namespace SHADE { @@ -45,14 +47,16 @@ namespace SHADE { // Get the current scene graph to traverse and update const auto& SCENE_GRAPH = SHSceneManager::GetCurrentSceneGraph(); - UpdateEntity(SCENE_GRAPH.GetRoot()); + + // TODO(Diren): Consider how to clear dirty in pause / stop mode and update physics, but do not clear in play mode. + UpdateEntity(SCENE_GRAPH.GetRoot(), false); } - void SHTransformSystem::TransformPostPhysicsUpdate::Execute(double dt) noexcept + void SHTransformSystem::TransformPostPhysicsUpdate::Execute(double) noexcept { // Get the current scene graph to traverse and update const auto& SCENE_GRAPH = SHSceneManager::GetCurrentSceneGraph(); - UpdateEntityAndClear(SCENE_GRAPH.GetRoot()); + UpdateEntity(SCENE_GRAPH.GetRoot(), true); } void SHTransformSystem::Init() @@ -96,13 +100,13 @@ namespace SHADE tf->local.position = SHVec3::Transform(tf->world.position, worldToLocal); - tf->local.rotation = tf->world.rotation; - tf->local.scale = tf->world.scale; + tf->localRotation = tf->worldRotation; + tf->local.scale = tf->world.scale; if (PARENT_TF != nullptr) { // Compute Local Rotation - tf->local.rotation -= PARENT_TF->GetLocalRotation(); + tf->localRotation -= PARENT_TF->GetLocalRotation(); // Compute Local Scale tf->local.scale /= PARENT_TF->GetLocalScale(); @@ -143,13 +147,13 @@ namespace SHADE childTransform->local.position = SHVec3::Transform(childTransform->world.position, worldToLocal); - childTransform->local.rotation = childTransform->world.rotation; - childTransform->local.scale = childTransform->world.scale; + childTransform->localRotation = childTransform->worldRotation; + childTransform->local.scale = childTransform->world.scale; if (parent) { // Compute Local Rotation - childTransform->local.rotation -= parent->GetLocalRotation(); + childTransform->localRotation -= parent->GetLocalRotation(); // Compute Local Scale childTransform->local.scale /= parent->GetLocalScale(); @@ -158,12 +162,10 @@ namespace SHADE childTransform->local.trs = localToWorld; } } - - } } - void SHTransformSystem::UpdateEntity(const SHSceneNode* node) + void SHTransformSystem::UpdateEntity(const SHSceneNode* node, bool clearDirtyFlag) { const auto* NODE_TRANSFORM = SHComponentManager::GetComponent_s(node->GetEntityID()); const bool HAS_PARENT_CHANGED = NODE_TRANSFORM && NODE_TRANSFORM->dirty; @@ -185,37 +187,10 @@ namespace SHADE } } - UpdateEntity(child); - } - } - - - void SHTransformSystem::UpdateEntityAndClear(const SHSceneNode* node) - { - const auto* NODE_TRANSFORM = SHComponentManager::GetComponent_s(node->GetEntityID()); - const bool HAS_PARENT_CHANGED = NODE_TRANSFORM && NODE_TRANSFORM->dirty; - - for (const auto* child : node->GetChildren()) - { - auto* childTransform = SHComponentManager::GetComponent_s(child->GetEntityID()); - if (childTransform) - { - // Only update if node in hierarchy and component are both active - const bool IS_NODE_ACTIVE = child->IsActive(); - if (IS_NODE_ACTIVE && childTransform->isActive) - { - if (childTransform->dirty || HAS_PARENT_CHANGED) - { - UpdateTransform(*childTransform, NODE_TRANSFORM); - childTransform->dirty = true; - } - } - } - - UpdateEntityAndClear(child); + UpdateEntity(child, clearDirtyFlag); // Clear dirty flag after all children are updated - if (childTransform) + if (childTransform && clearDirtyFlag) childTransform->dirty = false; } } @@ -244,12 +219,17 @@ namespace SHADE } case SHTransformComponent::UpdateCommandType::WORLD_ROTATION: { - tf.local.rotation = tf.world.rotation; + tf.localRotation = tf.worldRotation; if (parent) - tf.local.rotation -= parent->GetLocalRotation(); + tf.localRotation -= parent->GetLocalRotation(); break; } + case SHTransformComponent::UpdateCommandType::WORLD_ORIENTATION: + { + // TODO(Diren): Test using scripts by concat quaternions? + break; + } case SHTransformComponent::UpdateCommandType::WORLD_SCALE: { tf.local.scale = tf.world.scale; @@ -268,7 +248,14 @@ namespace SHADE tf.local.trs = localToWorld; tf.world.position = SHVec3::Transform(tf.local.position, localToWorld); - tf.world.rotation = tf.local.rotation + (parent ? parent->GetLocalRotation() : SHVec3::Zero); + + tf.worldRotation = tf.localRotation + (parent ? parent->GetLocalRotation() : SHVec3::Zero); + + // TODO(Diren): Wrap rotations between -360 and 360 + + tf.world.orientation = SHQuaternion::FromEuler(tf.worldRotation); + tf.local.orientation = SHQuaternion::FromEuler(tf.localRotation); + tf.world.scale = tf.local.scale * (parent ? parent->GetLocalScale() : SHVec3::One); tf.world.ComputeTRS(); diff --git a/SHADE_Engine/src/Math/Transform/SHTransformSystem.h b/SHADE_Engine/src/Math/Transform/SHTransformSystem.h index 256c1561..e63969ce 100644 --- a/SHADE_Engine/src/Math/Transform/SHTransformSystem.h +++ b/SHADE_Engine/src/Math/Transform/SHTransformSystem.h @@ -114,8 +114,7 @@ namespace SHADE SHEventHandle ChangeParent (SHEventPtr changeParentEvent); static void UpdateChildrenLocalTransforms (SHSceneNode* node); - static void UpdateEntity (const SHSceneNode* node); - static void UpdateEntityAndClear (const SHSceneNode* node); + static void UpdateEntity (const SHSceneNode* node, bool clearDirtyFlag); static void UpdateTransform (SHTransformComponent& tf, const SHTransformComponent* parent = nullptr); }; diff --git a/SHADE_Engine/src/Physics/SHPhysicsObject.cpp b/SHADE_Engine/src/Physics/SHPhysicsObject.cpp index 36f7c57e..f9b476ef 100644 --- a/SHADE_Engine/src/Physics/SHPhysicsObject.cpp +++ b/SHADE_Engine/src/Physics/SHPhysicsObject.cpp @@ -140,12 +140,12 @@ namespace SHADE isRigidBody = true; rb->position = tf->GetWorldPosition(); - rb->orientation = tf->GetWorldRotation(); + rb->orientation = SHQuaternion::FromEuler(tf->GetWorldRotation()); if (hasColliders) { c->position = tf->GetWorldPosition(); - c->orientation = tf->GetWorldRotation(); + c->orientation = SHQuaternion::FromEuler(tf->GetWorldRotation()); // Get array of colliders and add them back into the rigidbody for (auto& collider : c->colliders | std::views::keys) AddCollider(&collider); @@ -160,7 +160,7 @@ namespace SHADE hasColliders = true; c->position = tf->GetWorldPosition(); - c->orientation = tf->GetWorldRotation(); + c->orientation = SHQuaternion::FromEuler(tf->GetWorldRotation()); for (auto& collider : c->colliders | std::views::keys) AddCollider(&collider); -- 2.40.1 From 33a6d3798c38ce0de2dcfe2aff19c94c8a39452f Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Sun, 23 Oct 2022 16:55:01 +0800 Subject: [PATCH 08/10] Added orientation interface and fixed compatibility between physics and transform --- SHADE_Application/src/Scenes/SBTestScene.cpp | 2 +- SHADE_Engine/src/Math/SHQuaternion.cpp | 4 ++ SHADE_Engine/src/Math/SHQuaternion.h | 1 + .../Math/Transform/SHTransformComponent.cpp | 14 +++- .../src/Math/Transform/SHTransformComponent.h | 6 +- .../src/Math/Transform/SHTransformSystem.cpp | 68 +++++++++++++++---- SHADE_Engine/src/Math/Vector/SHVec4.cpp | 9 +++ SHADE_Engine/src/Math/Vector/SHVec4.h | 14 ++-- SHADE_Engine/src/Physics/SHPhysicsSystem.cpp | 6 +- 9 files changed, 99 insertions(+), 25 deletions(-) diff --git a/SHADE_Application/src/Scenes/SBTestScene.cpp b/SHADE_Application/src/Scenes/SBTestScene.cpp index 23259ee4..966a00a5 100644 --- a/SHADE_Application/src/Scenes/SBTestScene.cpp +++ b/SHADE_Application/src/Scenes/SBTestScene.cpp @@ -101,7 +101,7 @@ namespace Sandbox //Set initial positions transform.SetWorldPosition(TEST_OBJ_START_POS + SHVec3{ x * TEST_OBJ_SPACING.x, y * TEST_OBJ_SPACING.y, SHMath::GenerateRandomNumber(-3.5f, -5.0f) }); //transform.SetWorldPosition({-1.0f, -1.0f, -1.0f}); - transform.SetWorldRotation(SHMath::GenerateRandomNumber(), SHMath::GenerateRandomNumber(), SHMath::GenerateRandomNumber()); + transform.SetWorldRotation(SHMath::GenerateRandomNumber(0.0f, 360.0f), SHMath::GenerateRandomNumber(0.0f, 360.0f), SHMath::GenerateRandomNumber(0.0f, 360.0f)); transform.SetWorldScale(TEST_OBJ_SCALE); //if (const bool IS_EVEN = (y * NUM_ROWS + x) % 2; IS_EVEN) diff --git a/SHADE_Engine/src/Math/SHQuaternion.cpp b/SHADE_Engine/src/Math/SHQuaternion.cpp index 924ac67a..3878cea1 100644 --- a/SHADE_Engine/src/Math/SHQuaternion.cpp +++ b/SHADE_Engine/src/Math/SHQuaternion.cpp @@ -36,6 +36,10 @@ namespace SHADE : XMFLOAT4( 0.0f, 0.0f, 0.0f, 1.0f ) {} + SHQuaternion::SHQuaternion(const SHVec4& vec4) noexcept + : XMFLOAT4( vec4.x, vec4.y, vec4.z, vec4.w ) + {} + SHQuaternion::SHQuaternion(float _x, float _y, float _z, float _w) noexcept : XMFLOAT4( _x, _y, _z, _w ) {} diff --git a/SHADE_Engine/src/Math/SHQuaternion.h b/SHADE_Engine/src/Math/SHQuaternion.h index f3ce3d61..cc1b5ff4 100644 --- a/SHADE_Engine/src/Math/SHQuaternion.h +++ b/SHADE_Engine/src/Math/SHQuaternion.h @@ -49,6 +49,7 @@ namespace SHADE SHQuaternion (SHQuaternion&& rhs) = default; SHQuaternion () noexcept; + SHQuaternion (const SHVec4& vec4) noexcept; SHQuaternion (float x, float y, float z, float w) noexcept; SHQuaternion (float yaw, float pitch, float roll) noexcept; diff --git a/SHADE_Engine/src/Math/Transform/SHTransformComponent.cpp b/SHADE_Engine/src/Math/Transform/SHTransformComponent.cpp index 306cde67..e56cbc8d 100644 --- a/SHADE_Engine/src/Math/Transform/SHTransformComponent.cpp +++ b/SHADE_Engine/src/Math/Transform/SHTransformComponent.cpp @@ -103,7 +103,9 @@ namespace SHADE void SHTransformComponent::SetLocalRotation(const SHVec3& newLocalRotation) noexcept { dirty = true; + localRotation = newLocalRotation; + updateQueue.push({ UpdateCommandType::LOCAL_ROTATION, newLocalRotation }); } void SHTransformComponent::SetLocalRotation(float pitch, float yaw, float roll) noexcept @@ -113,11 +115,16 @@ namespace SHADE localRotation.x = pitch; localRotation.y = yaw; localRotation.z = roll; + + updateQueue.push({ UpdateCommandType::LOCAL_ROTATION, SHVec3{pitch, yaw, roll} }); } void SHTransformComponent::SetLocalOrientation(const SHQuaternion& newLocalOrientation) noexcept { - + dirty = true; + + local.orientation = newLocalOrientation; + updateQueue.push({ UpdateCommandType::LOCAL_ORIENTATION, newLocalOrientation }); } void SHTransformComponent::SetLocalScale(const SHVec3& newLocalScale) noexcept @@ -155,7 +162,10 @@ namespace SHADE void SHTransformComponent::SetWorldOrientation(const SHQuaternion& newWorldOrientation) noexcept { - + dirty = true; + + world.orientation = newWorldOrientation; + updateQueue.push({ UpdateCommandType::WORLD_ORIENTATION, newWorldOrientation }); } void SHTransformComponent::SetWorldScale(const SHVec3& newWorldScale) noexcept diff --git a/SHADE_Engine/src/Math/Transform/SHTransformComponent.h b/SHADE_Engine/src/Math/Transform/SHTransformComponent.h index d1d21bec..ce8bb6fe 100644 --- a/SHADE_Engine/src/Math/Transform/SHTransformComponent.h +++ b/SHADE_Engine/src/Math/Transform/SHTransformComponent.h @@ -97,7 +97,9 @@ namespace SHADE enum class UpdateCommandType { - WORLD_POSITION + LOCAL_ROTATION + , LOCAL_ORIENTATION + , WORLD_POSITION , WORLD_ROTATION , WORLD_ORIENTATION , WORLD_SCALE @@ -111,7 +113,7 @@ namespace SHADE /*-------------------------------------------------------------------------------*/ UpdateCommandType type; - SHVec3 data; + SHVec4 data; }; using UpdateQueue = std::queue; diff --git a/SHADE_Engine/src/Math/Transform/SHTransformSystem.cpp b/SHADE_Engine/src/Math/Transform/SHTransformSystem.cpp index 3244db1b..8bd01fb4 100644 --- a/SHADE_Engine/src/Math/Transform/SHTransformSystem.cpp +++ b/SHADE_Engine/src/Math/Transform/SHTransformSystem.cpp @@ -200,6 +200,8 @@ namespace SHADE SHMatrix localToWorld = SHMatrix::Identity; SHMatrix worldToLocal = SHMatrix::Identity; + bool convertRotation = true; + if (parent) { localToWorld = parent->GetTRS(); @@ -212,27 +214,44 @@ namespace SHADE switch (UPDATE_COMMAND.type) { + case SHTransformComponent::UpdateCommandType::LOCAL_ROTATION: + { + convertRotation = true; + break; + } + case SHTransformComponent::UpdateCommandType::LOCAL_ORIENTATION: + { + convertRotation = false; + break; + } case SHTransformComponent::UpdateCommandType::WORLD_POSITION: { - tf.local.position = SHVec3::Transform(UPDATE_COMMAND.data, worldToLocal); + tf.local.position = SHVec3::Transform(UPDATE_COMMAND.data.ToVec3(), worldToLocal); break; } case SHTransformComponent::UpdateCommandType::WORLD_ROTATION: { - tf.localRotation = tf.worldRotation; + tf.localRotation = UPDATE_COMMAND.data.ToVec3(); if (parent) tf.localRotation -= parent->GetLocalRotation(); + convertRotation = true; + break; } case SHTransformComponent::UpdateCommandType::WORLD_ORIENTATION: { - // TODO(Diren): Test using scripts by concat quaternions? + tf.local.orientation = UPDATE_COMMAND.data; + if (parent) + tf.local.orientation /= parent->GetLocalOrientation(); + + convertRotation = false; + break; } case SHTransformComponent::UpdateCommandType::WORLD_SCALE: { - tf.local.scale = tf.world.scale; + tf.local.scale = UPDATE_COMMAND.data.ToVec3(); if (parent) tf.local.scale /= parent->GetLocalScale(); @@ -247,17 +266,42 @@ namespace SHADE tf.local.trs = localToWorld; + // Compute world transforms tf.world.position = SHVec3::Transform(tf.local.position, localToWorld); - - tf.worldRotation = tf.localRotation + (parent ? parent->GetLocalRotation() : SHVec3::Zero); - - // TODO(Diren): Wrap rotations between -360 and 360 - - tf.world.orientation = SHQuaternion::FromEuler(tf.worldRotation); - tf.local.orientation = SHQuaternion::FromEuler(tf.localRotation); - tf.world.scale = tf.local.scale * (parent ? parent->GetLocalScale() : SHVec3::One); + SHVec3 worldRotRad, localRotRad; + + if (convertRotation) + { + tf.worldRotation = tf.localRotation + (parent ? parent->GetLocalRotation() : SHVec3::Zero); + + // Set the orientation + // Wrap rotations between -360 and 360 and convert to radians + for (size_t i = 0; i < SHVec3::SIZE; ++i) + { + worldRotRad[i] = SHMath::DegreesToRadians(SHMath::Wrap(tf.worldRotation[i], -360.0f, 360.0f)); + localRotRad[i] = SHMath::DegreesToRadians(SHMath::Wrap(tf.localRotation[i], -360.0f, 360.0f)); + } + + tf.world.orientation = SHQuaternion::FromEuler(worldRotRad); + tf.local.orientation = SHQuaternion::FromEuler(localRotRad); + } + else + { + tf.world.orientation = (parent ? parent->GetLocalOrientation() : SHQuaternion::Identity) * tf.local.orientation; + + // Set the euler angle rotations + worldRotRad = tf.world.orientation.ToEuler(); + localRotRad = tf.local.orientation.ToEuler(); + + for (size_t i = 0; i < SHVec3::SIZE; ++i) + { + tf.worldRotation[i] = SHMath::RadiansToDegrees(worldRotRad[i]); + tf.localRotation[i] = SHMath::RadiansToDegrees(localRotRad[i]); + } + } + tf.world.ComputeTRS(); } diff --git a/SHADE_Engine/src/Math/Vector/SHVec4.cpp b/SHADE_Engine/src/Math/Vector/SHVec4.cpp index bcf2ef97..9857818a 100644 --- a/SHADE_Engine/src/Math/Vector/SHVec4.cpp +++ b/SHADE_Engine/src/Math/Vector/SHVec4.cpp @@ -38,6 +38,10 @@ namespace SHADE : XMFLOAT4( 0.0f, 0.0f, 0.0f, 0.0f ) {} + SHVec4::SHVec4(const SHVec3& vec3) noexcept + : XMFLOAT4( vec3.x, vec3.y, vec3.z, 1.0f ) + {} + SHVec4::SHVec4(const XMFLOAT4& xmfloat4) noexcept : XMFLOAT4( xmfloat4.x, xmfloat4.y, xmfloat4.z, xmfloat4.w ) {} @@ -271,6 +275,11 @@ namespace SHADE return result; } + SHVec3 SHVec4::ToVec3() const noexcept + { + return SHVec3{ x, y, z }; + } + /*-----------------------------------------------------------------------------------*/ /* Static Function Member Definitions */ /*-----------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Math/Vector/SHVec4.h b/SHADE_Engine/src/Math/Vector/SHVec4.h index 911a714e..ce341bed 100644 --- a/SHADE_Engine/src/Math/Vector/SHVec4.h +++ b/SHADE_Engine/src/Math/Vector/SHVec4.h @@ -16,6 +16,7 @@ // Project Headers #include "SH_API.h" +#include "SHVec3.h" namespace SHADE { @@ -53,6 +54,7 @@ namespace SHADE ~SHVec4 () = default; SHVec4 () noexcept; + SHVec4 (const SHVec3& vec3) noexcept; SHVec4 (const XMFLOAT4& xmfloat4) noexcept; SHVec4 (float x, float y, float z, float w) noexcept; @@ -102,16 +104,18 @@ namespace SHADE [[nodiscard]] float Dot3D (const SHVec4& rhs) const noexcept; [[nodiscard]] SHVec4 Cross3D (const SHVec4& rhs) const noexcept; [[nodiscard]] SHVec4 Cross (const SHVec4& v1, const SHVec4& v2) const noexcept; + + [[nodiscard]] SHVec3 ToVec3 () const noexcept; /*---------------------------------------------------------------------------------*/ /* Static Function Members */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] static SHVec4 Normalise (const SHVec4& v) noexcept; - [[nodiscard]] static SHVec4 Normalise3D (const SHVec4& v) noexcept; - [[nodiscard]] static SHVec4 Abs (const SHVec4& v) noexcept; - [[nodiscard]] static SHVec4 Min (const std::initializer_list& vs) noexcept; - [[nodiscard]] static SHVec4 Max (const std::initializer_list& vs) noexcept; + [[nodiscard]] static SHVec4 Normalise (const SHVec4& v) noexcept; + [[nodiscard]] static SHVec4 Normalise3D (const SHVec4& v) noexcept; + [[nodiscard]] static SHVec4 Abs (const SHVec4& v) noexcept; + [[nodiscard]] static SHVec4 Min (const std::initializer_list& vs) noexcept; + [[nodiscard]] static SHVec4 Max (const std::initializer_list& vs) noexcept; [[nodiscard]] static SHVec4 Clamp (const SHVec4& v, const SHVec4& vMin, const SHVec4& vMax) noexcept; [[nodiscard]] static SHVec4 Lerp (const SHVec4& a, const SHVec4& b, float t) noexcept; [[nodiscard]] static SHVec4 ClampedLerp (const SHVec4& a, const SHVec4& b, float t, float tMin = 0.0f, float tMax = 1.0f) noexcept; diff --git a/SHADE_Engine/src/Physics/SHPhysicsSystem.cpp b/SHADE_Engine/src/Physics/SHPhysicsSystem.cpp index 7dc6c44e..a1994ad2 100644 --- a/SHADE_Engine/src/Physics/SHPhysicsSystem.cpp +++ b/SHADE_Engine/src/Physics/SHPhysicsSystem.cpp @@ -16,6 +16,7 @@ // Project Headers #include "ECS_Base/Managers/SHComponentManager.h" #include "ECS_Base/Managers/SHEntityManager.h" +#include "Math/SHMathHelpers.h" #include "Scene/SHSceneManager.h" #include "Math/Transform/SHTransformComponent.h" @@ -315,7 +316,7 @@ namespace SHADE if (TF->HasChanged()) { physicsObject.SetPosition(TF->GetWorldPosition()); - physicsObject.SetRotation(TF->GetWorldRotation()); + physicsObject.SetOrientation(TF->GetWorldOrientation()); } } } @@ -492,8 +493,7 @@ namespace SHADE // Convert RP3D Transform to SHADE auto* tfComponent = SHComponentManager::GetComponent(entityID); tfComponent->SetWorldPosition(rp3dPos); - tfComponent->SetWorldRotation(SHQuaternion{ rp3dRot }.ToEuler()); - + tfComponent->SetWorldOrientation(SHQuaternion{ rp3dRot }); // Cache transforms physicsObject.prevTransform = CURRENT_TF; -- 2.40.1 From e15f7696e62ad51896e58fde0bda32df87339d8a Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Sun, 23 Oct 2022 18:22:58 +0800 Subject: [PATCH 09/10] Rotations are stored as radians to reduce the number of conversions --- .../src/Math/Transform/SHTransformComponent.h | 4 ++-- .../src/Math/Transform/SHTransformSystem.cpp | 17 ++++++----------- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/SHADE_Engine/src/Math/Transform/SHTransformComponent.h b/SHADE_Engine/src/Math/Transform/SHTransformComponent.h index ce8bb6fe..2fe67bdd 100644 --- a/SHADE_Engine/src/Math/Transform/SHTransformComponent.h +++ b/SHADE_Engine/src/Math/Transform/SHTransformComponent.h @@ -127,8 +127,8 @@ namespace SHADE // We store euler angle rotations separately to interface with transform quaternions. // Reading quaternions are unreliable. - SHVec3 localRotation; // Stored in degrees - SHVec3 worldRotation; // Stored in degrees + SHVec3 localRotation; // Stored in Radians + SHVec3 worldRotation; // Stored in Radians SHTransform local; // Local TRS holds Local To World Transform SHTransform world; diff --git a/SHADE_Engine/src/Math/Transform/SHTransformSystem.cpp b/SHADE_Engine/src/Math/Transform/SHTransformSystem.cpp index 8bd01fb4..5a540cd4 100644 --- a/SHADE_Engine/src/Math/Transform/SHTransformSystem.cpp +++ b/SHADE_Engine/src/Math/Transform/SHTransformSystem.cpp @@ -270,7 +270,7 @@ namespace SHADE tf.world.position = SHVec3::Transform(tf.local.position, localToWorld); tf.world.scale = tf.local.scale * (parent ? parent->GetLocalScale() : SHVec3::One); - SHVec3 worldRotRad, localRotRad; + if (convertRotation) { @@ -278,10 +278,11 @@ namespace SHADE // Set the orientation // Wrap rotations between -360 and 360 and convert to radians + SHVec3 worldRotRad, localRotRad; for (size_t i = 0; i < SHVec3::SIZE; ++i) { - worldRotRad[i] = SHMath::DegreesToRadians(SHMath::Wrap(tf.worldRotation[i], -360.0f, 360.0f)); - localRotRad[i] = SHMath::DegreesToRadians(SHMath::Wrap(tf.localRotation[i], -360.0f, 360.0f)); + worldRotRad[i] = SHMath::Wrap(tf.worldRotation[i], -SHMath::TWO_PI, SHMath::TWO_PI); + localRotRad[i] = SHMath::Wrap(tf.localRotation[i], -SHMath::TWO_PI, SHMath::TWO_PI); } tf.world.orientation = SHQuaternion::FromEuler(worldRotRad); @@ -292,14 +293,8 @@ namespace SHADE tf.world.orientation = (parent ? parent->GetLocalOrientation() : SHQuaternion::Identity) * tf.local.orientation; // Set the euler angle rotations - worldRotRad = tf.world.orientation.ToEuler(); - localRotRad = tf.local.orientation.ToEuler(); - - for (size_t i = 0; i < SHVec3::SIZE; ++i) - { - tf.worldRotation[i] = SHMath::RadiansToDegrees(worldRotRad[i]); - tf.localRotation[i] = SHMath::RadiansToDegrees(localRotRad[i]); - } + tf.worldRotation = tf.world.orientation.ToEuler(); + tf.localRotation = tf.local.orientation.ToEuler(); } tf.world.ComputeTRS(); -- 2.40.1 From 57f9898e07667e00fede342e79fc4bee42551693 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Sun, 23 Oct 2022 20:03:18 +0800 Subject: [PATCH 10/10] Finished quaternion implementation --- SHADE_Engine/src/Math/SHQuaternion.cpp | 125 +++++++++++++++++++++---- SHADE_Engine/src/Math/SHQuaternion.h | 40 ++++---- SHADE_Managed/Quaternion.hxx | 17 ++++ SHADE_Managed/src/Math/Vector3.hxx | 1 + 4 files changed, 148 insertions(+), 35 deletions(-) create mode 100644 SHADE_Managed/Quaternion.hxx diff --git a/SHADE_Engine/src/Math/SHQuaternion.cpp b/SHADE_Engine/src/Math/SHQuaternion.cpp index 3878cea1..3564916a 100644 --- a/SHADE_Engine/src/Math/SHQuaternion.cpp +++ b/SHADE_Engine/src/Math/SHQuaternion.cpp @@ -211,15 +211,6 @@ namespace SHADE return XMVectorGetX(XMQuaternionDot(*this, rhs)); } - SHQuaternion SHQuaternion::RotateTowards(const SHQuaternion&, float) const noexcept - { - SHQuaternion result; - - // TODO (Diren) - - return result; - } - SHVec3 SHQuaternion::ToEuler() const noexcept { const float XX = x * x; @@ -317,19 +308,32 @@ namespace SHADE } - float SHQuaternion::Angle(const SHQuaternion&, const SHQuaternion&) noexcept + float SHQuaternion::Angle(const SHQuaternion& q1, const SHQuaternion& q2) noexcept { - // TODO (Diren) + XMVECTOR R = XMQuaternionMultiply(XMQuaternionConjugate(q1), q2); - return 0.0f; + const float RS = XMVectorGetW(R); + R = XMVector3Length(R); + return 2.0f * atan2f(XMVectorGetX(R), RS); } - SHQuaternion SHQuaternion::Lerp(const SHQuaternion&, const SHQuaternion&, float) noexcept + SHQuaternion SHQuaternion::Lerp(const SHQuaternion& q1, const SHQuaternion& q2, float t) noexcept { SHQuaternion result; - // TODO (Diren) + XMVECTOR R = XMVectorZero(); + if (XMVector4GreaterOrEqual(XMVector4Dot(q1, q2), R)) + { + R = XMVectorLerp(q1, q2, t); + } + else + { + const XMVECTOR X0 = XMVectorMultiply(q1, XMVectorReplicate(1.f - t)); + const XMVECTOR X1 = XMVectorMultiply(q2, XMVectorReplicate(t)); + R = XMVectorSubtract(X0, X1); + } + XMStoreFloat4(&result, XMQuaternionNormalize(R)); return result; } @@ -341,13 +345,102 @@ namespace SHADE return result; } - SHQuaternion SHQuaternion::Rotate(const SHVec3& , const SHVec3&) noexcept + SHQuaternion SHQuaternion::ClampedLerp(const SHQuaternion& q1, const SHQuaternion& q2, float t, float tMin, float tMax) noexcept { + return Lerp(q1, q2, std::clamp(t, tMin, tMax)); + } + + + SHQuaternion SHQuaternion::ClampedSlerp(const SHQuaternion& q1, const SHQuaternion& q2, float t, float tMin, float tMax) noexcept + { + return Slerp(q1, q2, std::clamp(t, tMin, tMax)); + } + + SHQuaternion SHQuaternion::FromToRotation(const SHVec3& from, const SHVec3& to) noexcept + { + // Melax, "The Shortest Arc Quaternion", Game Programming Gems + SHQuaternion result; - // TODO (Diren) + const XMVECTOR F = XMVector3Normalize(from); + const XMVECTOR T = XMVector3Normalize(to); + + const float dot = XMVectorGetX(XMVector3Dot(F, T)); + if (dot >= 1.f) + { + result = Identity; + } + else if (dot <= -1.f) + { + XMVECTOR axis = XMVector3Cross(F, SHVec3::Right); + if (XMVector3NearEqual(XMVector3LengthSq(axis), g_XMZero, g_XMEpsilon)) + { + axis = XMVector3Cross(F, SHVec3::Up); + } + + const XMVECTOR Q = XMQuaternionRotationAxis(axis, XM_PI); + XMStoreFloat4(&result, Q); + } + else + { + const XMVECTOR C = XMVector3Cross(F, T); + XMStoreFloat4(&result, C); + + const float s = sqrtf((1.f + dot) * 2.f); + result.x /= s; + result.y /= s; + result.z /= s; + result.w = s * 0.5f; + } return result; } + SHQuaternion SHQuaternion::LookRotation(const SHVec3& forward, const SHVec3& up) noexcept + { + SHQuaternion result; + + const SHQuaternion Q1 = FromToRotation(SHVec3::Forward, forward); + + const XMVECTOR C = XMVector3Cross(forward, up); + if (XMVector3NearEqual(XMVector3LengthSq(C), g_XMZero, g_XMEpsilon)) + { + // forward and up are co-linear + return Q1; + } + + SHVec3 qU; + XMStoreFloat3(&qU, XMQuaternionMultiply(Q1, SHVec3::Up)); + + const SHQuaternion Q2 = FromToRotation(qU, up); + + XMStoreFloat4(&result, XMQuaternionMultiply(Q2, Q1)); + + return result; + } + + SHQuaternion SHQuaternion::RotateTowards(const SHQuaternion& from, const SHQuaternion& to, float maxAngleInRad) noexcept + { + SHQuaternion result; + + // We can use the conjugate here instead of inverse assuming q1 & q2 are normalized. + const XMVECTOR R = XMQuaternionMultiply(XMQuaternionConjugate(from), to); + + const float RS = XMVectorGetW(R); + const XMVECTOR L = XMVector3Length(R); + const float angle = 2.f * atan2f(XMVectorGetX(L), RS); + if (angle > maxAngleInRad) + { + const XMVECTOR delta = XMQuaternionRotationAxis(R, maxAngleInRad); + const XMVECTOR Q = XMQuaternionMultiply(delta, from); + XMStoreFloat4(&result, Q); + } + else + { + // Don't overshoot. + result = to; + } + return result; + } + } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Math/SHQuaternion.h b/SHADE_Engine/src/Math/SHQuaternion.h index cc1b5ff4..fa5b5d36 100644 --- a/SHADE_Engine/src/Math/SHQuaternion.h +++ b/SHADE_Engine/src/Math/SHQuaternion.h @@ -51,7 +51,6 @@ namespace SHADE SHQuaternion () noexcept; SHQuaternion (const SHVec4& vec4) noexcept; SHQuaternion (float x, float y, float z, float w) noexcept; - SHQuaternion (float yaw, float pitch, float roll) noexcept; // Conversion from other math types @@ -98,34 +97,37 @@ namespace SHADE /* Function Members */ /*---------------------------------------------------------------------------------*/ - void Invert () noexcept; + void Invert () noexcept; - [[nodiscard]] float Length () const noexcept; - [[nodiscard]] float LengthSquared () const noexcept; - [[nodiscard]] float Dot (const SHQuaternion& rhs) const noexcept; - [[nodiscard]] SHQuaternion RotateTowards (const SHQuaternion& target, float maxAngleInRad) const noexcept; + [[nodiscard]] float Length () const noexcept; + [[nodiscard]] float LengthSquared () const noexcept; + [[nodiscard]] float Dot (const SHQuaternion& rhs) const noexcept; - [[nodiscard]] SHVec3 ToEuler () const noexcept; - [[nodiscard]] std::string ToString () const noexcept; + [[nodiscard]] SHVec3 ToEuler () const noexcept; + [[nodiscard]] std::string ToString () const noexcept; /*---------------------------------------------------------------------------------*/ /* Static Function Members */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] static SHQuaternion FromEuler (const SHVec3& eulerAngles) noexcept; - [[nodiscard]] static SHQuaternion FromPitchYawRoll (float pitch, float yaw, float roll) noexcept; - [[nodiscard]] static SHQuaternion FromAxisAngle (const SHVec3& axis, float angle) noexcept; - [[nodiscard]] static SHQuaternion FromRotationMatrix(const SHMatrix& rotationMatrix) noexcept; + [[nodiscard]] static SHQuaternion FromEuler (const SHVec3& eulerAngles) noexcept; + [[nodiscard]] static SHQuaternion FromPitchYawRoll (float pitch, float yaw, float roll) noexcept; + [[nodiscard]] static SHQuaternion FromAxisAngle (const SHVec3& axis, float angle) noexcept; + [[nodiscard]] static SHQuaternion FromRotationMatrix(const SHMatrix& rotationMatrix) noexcept; - [[nodiscard]] static SHQuaternion Normalise (const SHQuaternion& q) noexcept; - [[nodiscard]] static SHQuaternion Conjugate (const SHQuaternion& q) noexcept; - [[nodiscard]] static SHQuaternion Inverse (const SHQuaternion& q) noexcept; - [[nodiscard]] static float Angle (const SHQuaternion& q1, const SHQuaternion& q2) noexcept; + [[nodiscard]] static SHQuaternion Normalise (const SHQuaternion& q) noexcept; + [[nodiscard]] static SHQuaternion Conjugate (const SHQuaternion& q) noexcept; + [[nodiscard]] static SHQuaternion Inverse (const SHQuaternion& q) noexcept; + [[nodiscard]] static float Angle (const SHQuaternion& q1, const SHQuaternion& q2) noexcept; - [[nodiscard]] static SHQuaternion Lerp (const SHQuaternion& q1, const SHQuaternion& q2, float t) noexcept; - [[nodiscard]] static SHQuaternion Slerp (const SHQuaternion& q1, const SHQuaternion& q2, float t) noexcept; + [[nodiscard]] static SHQuaternion Lerp (const SHQuaternion& q1, const SHQuaternion& q2, float t) noexcept; + [[nodiscard]] static SHQuaternion Slerp (const SHQuaternion& q1, const SHQuaternion& q2, float t) noexcept; + [[nodiscard]] static SHQuaternion ClampedLerp (const SHQuaternion& q1, const SHQuaternion& q2, float t, float tMin = 0.0f, float tMax = 1.0f) noexcept; + [[nodiscard]] static SHQuaternion ClampedSlerp (const SHQuaternion& q1, const SHQuaternion& q2, float t, float tMin = 0.0f, float tMax = 1.0f) noexcept; - [[nodiscard]] static SHQuaternion Rotate (const SHVec3& from, const SHVec3& to) noexcept; + [[nodiscard]] static SHQuaternion FromToRotation (const SHVec3& from, const SHVec3& to) noexcept; + [[nodiscard]] static SHQuaternion LookRotation (const SHVec3& forward, const SHVec3& up) noexcept; + [[nodiscard]] static SHQuaternion RotateTowards (const SHQuaternion& from, const SHQuaternion& to, float maxAngleInRad) noexcept; }; SHQuaternion operator*(float lhs, const SHQuaternion& rhs) noexcept; diff --git a/SHADE_Managed/Quaternion.hxx b/SHADE_Managed/Quaternion.hxx new file mode 100644 index 00000000..0b07a34e --- /dev/null +++ b/SHADE_Managed/Quaternion.hxx @@ -0,0 +1,17 @@ +/************************************************************************************//*! +\file Quaternion.hxx +\author Diren D Bharwani, diren.dbharwani, 390002520 +\par email: diren.dbharwani\@digipen.edu +\date Oct 23, 2022 +\brief Contains the definitions of Quaternion struct. + + Note: This file is written in C++17/CLI. + +Copyright (C) 2021 DigiPen Institute of Technology. +Reproduction or disclosure of this file or its contents without the prior written consent +of DigiPen Institute of Technology is prohibited. +*//*************************************************************************************/ + +#pragma once + +// TODO(Diren) diff --git a/SHADE_Managed/src/Math/Vector3.hxx b/SHADE_Managed/src/Math/Vector3.hxx index e6cdc7d4..8b66439c 100644 --- a/SHADE_Managed/src/Math/Vector3.hxx +++ b/SHADE_Managed/src/Math/Vector3.hxx @@ -11,6 +11,7 @@ Copyright (C) 2021 DigiPen Institute of Technology. Reproduction or disclosure of this file or its contents without the prior written consent of DigiPen Institute of Technology is prohibited. *//*************************************************************************************/ + #pragma once // Standard Libraries -- 2.40.1