From 817f8ce4c1288c0cd45cf3aa461e4152ec4bf1a8 Mon Sep 17 00:00:00 2001 From: John Jones-Steele <82226755+jjjoness@users.noreply.github.com> Date: Wed, 8 Sep 2021 16:50:29 +0100 Subject: [PATCH] Terrain/jjjoness/3172 axis aligned box shape component (#3981) * CHanges to Push/Pop Matrix Signed-off-by: John Jones-Steele * Fixed bad commit Signed-off-by: John Jones-Steele * REmoved the AxisAlignedBoxShapeComponentBux and AxisAlignedBoxShapeConfig Signed-off-by: John Jones-Steele * Fixed derivation of AxisAlignedBoxShape Signed-off-by: John Jones-Steele * Added ShowChildrenOnly to Component Signed-off-by: John Jones-Steele * Fixed cmake file Signed-off-by: John Jones-Steele * Changes from review Signed-off-by: John Jones-Steele * Added tests for AxisAlignedBoxShape Signed-off-by: John Jones-Steele * Addressed PR comments and added one further test. Signed-off-by: John Jones-Steele * Removed dead code. Signed-off-by: John Jones-Steele * Changes from review Signed-off-by: John Jones-Steele * Spelling fix and changed link to docs Signed-off-by: John Jones-Steele * Fixed profile bug in Linux Signed-off-by: John Jones-Steele * Fixed problem with Unity Profile Build in Tests Signed-off-by: John Jones-Steele --- .../Entity/EntityDebugDisplayBus.h | 3 + .../AtomDebugDisplayViewportInterface.cpp | 20 ++ .../AtomDebugDisplayViewportInterface.h | 2 + Gems/LmbrCentral/Code/Source/LmbrCentral.cpp | 3 + .../Code/Source/LmbrCentralEditor.cpp | 2 + .../Code/Source/Shape/AxisAlignedBoxShape.cpp | 59 +++++ .../Code/Source/Shape/AxisAlignedBoxShape.h | 44 ++++ .../Shape/AxisAlignedBoxShapeComponent.cpp | 159 ++++++++++++ .../Shape/AxisAlignedBoxShapeComponent.h | 76 ++++++ Gems/LmbrCentral/Code/Source/Shape/BoxShape.h | 10 +- .../Code/Source/Shape/BoxShapeComponent.cpp | 106 +------- .../EditorAxisAlignedBoxShapeComponent.cpp | 168 +++++++++++++ .../EditorAxisAlignedBoxShapeComponent.h | 71 ++++++ .../Source/Shape/EditorBoxShapeComponent.cpp | 2 +- .../Code/Tests/AxisAlignedBoxShapeTest.cpp | 238 ++++++++++++++++++ Gems/LmbrCentral/Code/Tests/DiskShapeTest.cpp | 30 +-- Gems/LmbrCentral/Code/Tests/QuadShapeTest.cpp | 32 +-- .../LmbrCentral/Shape/BoxShapeComponentBus.h | 5 +- .../Code/lmbrcentral_editor_files.cmake | 2 + Gems/LmbrCentral/Code/lmbrcentral_files.cmake | 4 + .../Code/lmbrcentral_tests_files.cmake | 1 + 21 files changed, 897 insertions(+), 140 deletions(-) create mode 100644 Gems/LmbrCentral/Code/Source/Shape/AxisAlignedBoxShape.cpp create mode 100644 Gems/LmbrCentral/Code/Source/Shape/AxisAlignedBoxShape.h create mode 100644 Gems/LmbrCentral/Code/Source/Shape/AxisAlignedBoxShapeComponent.cpp create mode 100644 Gems/LmbrCentral/Code/Source/Shape/AxisAlignedBoxShapeComponent.h create mode 100644 Gems/LmbrCentral/Code/Source/Shape/EditorAxisAlignedBoxShapeComponent.cpp create mode 100644 Gems/LmbrCentral/Code/Source/Shape/EditorAxisAlignedBoxShapeComponent.h create mode 100644 Gems/LmbrCentral/Code/Tests/AxisAlignedBoxShapeTest.cpp diff --git a/Code/Framework/AzFramework/AzFramework/Entity/EntityDebugDisplayBus.h b/Code/Framework/AzFramework/AzFramework/Entity/EntityDebugDisplayBus.h index fddaddf303..68af9dbb26 100644 --- a/Code/Framework/AzFramework/AzFramework/Entity/EntityDebugDisplayBus.h +++ b/Code/Framework/AzFramework/AzFramework/Entity/EntityDebugDisplayBus.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -100,6 +101,8 @@ namespace AzFramework virtual AZ::u32 SetState(AZ::u32 state) { (void)state; return 0; } virtual void PushMatrix(const AZ::Transform& tm) { (void)tm; } virtual void PopMatrix() {} + virtual void PushPremultipliedMatrix(const AZ::Matrix3x4& matrix) { (void)matrix; } + virtual AZ::Matrix3x4 PopPremultipliedMatrix() { return AZ::Matrix3x4::CreateIdentity(); } protected: ~DebugDisplayRequests() = default; diff --git a/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.cpp b/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.cpp index d720463ce2..b45fbe05f6 100644 --- a/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.cpp +++ b/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.cpp @@ -1552,6 +1552,26 @@ namespace AZ::AtomBridge } } + void AtomDebugDisplayViewportInterface::PushPremultipliedMatrix(const AZ::Matrix3x4& matrix) + { + AZ_Assert(m_rendState.m_currentTransform < RenderState::TransformStackSize, "Exceeded AtomDebugDisplayViewportInterface matrix stack size"); + if (m_rendState.m_currentTransform < RenderState::TransformStackSize) + { + m_rendState.m_currentTransform++; + m_rendState.m_transformStack[m_rendState.m_currentTransform] = matrix; + } + } + + AZ::Matrix3x4 AtomDebugDisplayViewportInterface::PopPremultipliedMatrix() + { + AZ_Assert(m_rendState.m_currentTransform > 0, "Underflowed AtomDebugDisplayViewportInterface matrix stack"); + if (m_rendState.m_currentTransform > 0) + { + m_rendState.m_currentTransform--; + } + return m_rendState.m_transformStack[m_rendState.m_currentTransform + 1]; + } + const AZ::Matrix3x4& AtomDebugDisplayViewportInterface::GetCurrentTransform() const { return m_rendState.m_transformStack[m_rendState.m_currentTransform]; diff --git a/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.h b/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.h index 22fb8875a4..07f4efdf50 100644 --- a/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.h +++ b/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.h @@ -193,6 +193,8 @@ namespace AZ::AtomBridge AZ::u32 SetState(AZ::u32 state) override; void PushMatrix(const AZ::Transform& tm) override; void PopMatrix() override; + void PushPremultipliedMatrix(const AZ::Matrix3x4& matrix) override; + AZ::Matrix3x4 PopPremultipliedMatrix() override; private: diff --git a/Gems/LmbrCentral/Code/Source/LmbrCentral.cpp b/Gems/LmbrCentral/Code/Source/LmbrCentral.cpp index 78742028f9..31cf68e9a0 100644 --- a/Gems/LmbrCentral/Code/Source/LmbrCentral.cpp +++ b/Gems/LmbrCentral/Code/Source/LmbrCentral.cpp @@ -72,6 +72,7 @@ // Shape components #include "Shape/SphereShapeComponent.h" #include "Shape/DiskShapeComponent.h" +#include "Shape/AxisAlignedBoxShapeComponent.h" #include "Shape/BoxShapeComponent.h" #include "Shape/QuadShapeComponent.h" #include "Shape/CylinderShapeComponent.h" @@ -202,6 +203,7 @@ namespace LmbrCentral SphereShapeComponent::CreateDescriptor(), DiskShapeComponent::CreateDescriptor(), BoxShapeComponent::CreateDescriptor(), + AxisAlignedBoxShapeComponent::CreateDescriptor(), QuadShapeComponent::CreateDescriptor(), CylinderShapeComponent::CreateDescriptor(), CapsuleShapeComponent::CreateDescriptor(), @@ -215,6 +217,7 @@ namespace LmbrCentral SphereShapeDebugDisplayComponent::CreateDescriptor(), DiskShapeDebugDisplayComponent::CreateDescriptor(), BoxShapeDebugDisplayComponent::CreateDescriptor(), + AxisAlignedBoxShapeDebugDisplayComponent::CreateDescriptor(), QuadShapeDebugDisplayComponent::CreateDescriptor(), CapsuleShapeDebugDisplayComponent::CreateDescriptor(), CylinderShapeDebugDisplayComponent::CreateDescriptor(), diff --git a/Gems/LmbrCentral/Code/Source/LmbrCentralEditor.cpp b/Gems/LmbrCentral/Code/Source/LmbrCentralEditor.cpp index b15a31f8b3..511bf98582 100644 --- a/Gems/LmbrCentral/Code/Source/LmbrCentralEditor.cpp +++ b/Gems/LmbrCentral/Code/Source/LmbrCentralEditor.cpp @@ -24,6 +24,7 @@ #include "Scripting/EditorSpawnerComponent.h" #include "Scripting/EditorTagComponent.h" +#include "Shape/EditorAxisAlignedBoxShapeComponent.h" #include "Shape/EditorBoxShapeComponent.h" #include "Shape/EditorQuadShapeComponent.h" #include "Shape/EditorSphereShapeComponent.h" @@ -67,6 +68,7 @@ namespace LmbrCentral EditorDiskShapeComponent::CreateDescriptor(), EditorTubeShapeComponent::CreateDescriptor(), EditorBoxShapeComponent::CreateDescriptor(), + EditorAxisAlignedBoxShapeComponent::CreateDescriptor(), EditorQuadShapeComponent::CreateDescriptor(), EditorLookAtComponent::CreateDescriptor(), EditorCylinderShapeComponent::CreateDescriptor(), diff --git a/Gems/LmbrCentral/Code/Source/Shape/AxisAlignedBoxShape.cpp b/Gems/LmbrCentral/Code/Source/Shape/AxisAlignedBoxShape.cpp new file mode 100644 index 0000000000..2553f8ab3e --- /dev/null +++ b/Gems/LmbrCentral/Code/Source/Shape/AxisAlignedBoxShape.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include "AxisAlignedBoxShape.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace LmbrCentral +{ + AxisAlignedBoxShape::AxisAlignedBoxShape() + : BoxShape() + { + } + + void AxisAlignedBoxShape::Reflect(AZ::ReflectContext* context) + { + if (AZ::SerializeContext* serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(1) + ; + + if (AZ::EditContext* editContext = serializeContext->GetEditContext()) + { + editContext->Class("Axis Aligned Box Shape", "Axis Aligned Box shape configuration parameters") + ; + } + } + } + + void AxisAlignedBoxShape::Activate(AZ::EntityId entityId) + { + BoxShape::Activate(entityId); + m_currentTransform.SetRotation(AZ::Quaternion::CreateIdentity()); + } + + void AxisAlignedBoxShape::OnTransformChanged(const AZ::Transform& local, const AZ::Transform& world) + { + AZ::Transform worldNoRotation(world.GetTranslation(), AZ::Quaternion::CreateIdentity(), world.GetUniformScale()); + BoxShape::OnTransformChanged(local, worldNoRotation); + } +} // namespace LmbrCentral diff --git a/Gems/LmbrCentral/Code/Source/Shape/AxisAlignedBoxShape.h b/Gems/LmbrCentral/Code/Source/Shape/AxisAlignedBoxShape.h new file mode 100644 index 0000000000..724092b608 --- /dev/null +++ b/Gems/LmbrCentral/Code/Source/Shape/AxisAlignedBoxShape.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include "BoxShape.h" + +namespace AzFramework +{ + class DebugDisplayRequests; +} + +namespace LmbrCentral +{ + struct ShapeDrawParams; + + class AxisAlignedBoxShape + : public BoxShape + { + public: + AZ_CLASS_ALLOCATOR(AxisAlignedBoxShape, AZ::SystemAllocator, 0) + AZ_RTTI(AxisAlignedBoxShape, "{CFDC96C5-287A-4033-8D7D-BA9331C13F25}", BoxShape) + + AxisAlignedBoxShape(); + + static void Reflect(AZ::ReflectContext* context); + + void Activate(AZ::EntityId entityId) override; + + // AZ::TransformNotificationBus::Handler + void OnTransformChanged(const AZ::Transform& local, const AZ::Transform& world) override; + }; +} // namespace LmbrCentral diff --git a/Gems/LmbrCentral/Code/Source/Shape/AxisAlignedBoxShapeComponent.cpp b/Gems/LmbrCentral/Code/Source/Shape/AxisAlignedBoxShapeComponent.cpp new file mode 100644 index 0000000000..7f919bc097 --- /dev/null +++ b/Gems/LmbrCentral/Code/Source/Shape/AxisAlignedBoxShapeComponent.cpp @@ -0,0 +1,159 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + + +#include "AxisAlignedBoxShapeComponent.h" +#include +#include +#include +#include +#include + +namespace LmbrCentral +{ + void AxisAlignedBoxShapeComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(AZ_CRC_CE("ShapeService")); + provided.push_back(AZ_CRC_CE("BoxShapeService")); + provided.push_back(AZ_CRC_CE("AxisAlignedBoxShapeService")); + } + + void AxisAlignedBoxShapeComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + { + incompatible.push_back(AZ_CRC_CE("ShapeService")); + incompatible.push_back(AZ_CRC_CE("AxisAlignedBoxShapeService")); + } + + void AxisAlignedBoxShapeComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) + { + required.push_back(AZ_CRC_CE("TransformService")); + } + + void AxisAlignedBoxShapeComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent) + { + dependent.push_back(AZ_CRC_CE("NonUniformScaleService")); + } + + void AxisAlignedBoxShapeDebugDisplayComponent::Reflect(AZ::ReflectContext* context) + { + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(1)->Field( + "Configuration", &AxisAlignedBoxShapeDebugDisplayComponent::m_boxShapeConfig) + ; + } + } + + void AxisAlignedBoxShapeDebugDisplayComponent::Activate() + { + EntityDebugDisplayComponent::Activate(); + ShapeComponentNotificationsBus::Handler::BusConnect(GetEntityId()); + m_nonUniformScale = AZ::Vector3::CreateOne(); + AZ::NonUniformScaleRequestBus::EventResult(m_nonUniformScale, GetEntityId(), &AZ::NonUniformScaleRequests::GetScale); + } + + void AxisAlignedBoxShapeDebugDisplayComponent::Deactivate() + { + ShapeComponentNotificationsBus::Handler::BusDisconnect(); + EntityDebugDisplayComponent::Deactivate(); + } + + void AxisAlignedBoxShapeDebugDisplayComponent::Draw(AzFramework::DebugDisplayRequests& debugDisplay) + { + AZ::Matrix3x4 saveMatrix; + ShapeDrawParams drawParams = g_defaultShapeDrawParams; + drawParams.m_shapeColor = m_boxShapeConfig.GetDrawColor(); + drawParams.m_filled = m_boxShapeConfig.IsFilled(); + AZ::Transform transform = GetCurrentTransform(); + transform.SetRotation(AZ::Quaternion::CreateIdentity()); + saveMatrix = debugDisplay.PopPremultipliedMatrix(); + debugDisplay.PushMatrix(transform); + DrawBoxShape(drawParams, m_boxShapeConfig, debugDisplay, m_nonUniformScale); + debugDisplay.PopMatrix(); + debugDisplay.PushPremultipliedMatrix(saveMatrix); + } + + bool AxisAlignedBoxShapeDebugDisplayComponent::ReadInConfig(const AZ::ComponentConfig* baseConfig) + { + if (const auto config = azrtti_cast(baseConfig)) + { + m_boxShapeConfig = *config; + return true; + } + return false; + } + + bool AxisAlignedBoxShapeDebugDisplayComponent::WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const + { + if (auto outConfig = azrtti_cast(outBaseConfig)) + { + *outConfig = m_boxShapeConfig; + return true; + } + return false; + } + + void AxisAlignedBoxShapeDebugDisplayComponent::OnShapeChanged(ShapeChangeReasons changeReason) + { + if (changeReason == ShapeChangeReasons::ShapeChanged) + { + BoxShapeComponentRequestsBus::EventResult(m_boxShapeConfig, GetEntityId(), &BoxShapeComponentRequests::GetBoxConfiguration); + AZ::NonUniformScaleRequestBus::EventResult(m_nonUniformScale, GetEntityId(), &AZ::NonUniformScaleRequests::GetScale); + } + } + + void AxisAlignedBoxShapeComponent::Reflect(AZ::ReflectContext* context) + { + AxisAlignedBoxShape::Reflect(context); + + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(1) + ->Field("AxisAlignedBoxShape", &AxisAlignedBoxShapeComponent::m_aaboxShape) + ; + } + + if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) + { + behaviorContext->Constant("AxisAlignedBoxShapeComponentTypeId", BehaviorConstant(AxisAlignedBoxShapeComponentTypeId)); + } + } + + void AxisAlignedBoxShapeComponent::Activate() + { + m_aaboxShape.Activate(GetEntityId()); + } + + void AxisAlignedBoxShapeComponent::Deactivate() + { + m_aaboxShape.Deactivate(); + } + + bool AxisAlignedBoxShapeComponent::ReadInConfig(const AZ::ComponentConfig* baseConfig) + { + if (const auto config = azrtti_cast(baseConfig)) + { + m_aaboxShape.SetBoxConfiguration(*config); + return true; + } + return false; + } + + bool AxisAlignedBoxShapeComponent::WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const + { + if (auto config = azrtti_cast(outBaseConfig)) + { + *config = m_aaboxShape.GetBoxConfiguration(); + return true; + } + return false; + } + +} // namespace LmbrCentral diff --git a/Gems/LmbrCentral/Code/Source/Shape/AxisAlignedBoxShapeComponent.h b/Gems/LmbrCentral/Code/Source/Shape/AxisAlignedBoxShapeComponent.h new file mode 100644 index 0000000000..094f9591d1 --- /dev/null +++ b/Gems/LmbrCentral/Code/Source/Shape/AxisAlignedBoxShapeComponent.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include + +#include "Rendering/EntityDebugDisplayComponent.h" +#include "AxisAlignedBoxShape.h" + +namespace LmbrCentral +{ + /// Type ID for the AxisAlignedBoxShapeComponent + static const AZ::Uuid AxisAlignedBoxShapeComponentTypeId = "{641D817E-1BC6-406A-BBB2-218541808E45}"; + + /// Type ID for the EditorAxisAlignedBoxShapeComponent + static const AZ::Uuid EditorAxisAlignedBoxShapeComponentTypeId = "{8C027DF6-E157-4159-9BF8-F1B925466F1F}"; + + /// Provide a Component interface for AxisAlignedBoxShape functionality. + class AxisAlignedBoxShapeComponent + : public AZ::Component + { + public: + AZ_COMPONENT(AxisAlignedBoxShapeComponent, AxisAlignedBoxShapeComponentTypeId) + static void Reflect(AZ::ReflectContext* context); + + // AZ::Component + void Activate() override; + void Deactivate() override; + bool ReadInConfig(const AZ::ComponentConfig* baseConfig) override; + bool WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const override; + + private: + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); + static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent); + + AxisAlignedBoxShape m_aaboxShape; ///< Stores underlying box type for this component. + }; + + /// Concrete EntityDebugDisplay implementation for BoxShape. + class AxisAlignedBoxShapeDebugDisplayComponent + : public EntityDebugDisplayComponent + , public ShapeComponentNotificationsBus::Handler + { + public: + AZ_COMPONENT(AxisAlignedBoxShapeDebugDisplayComponent, "{BA93F933-1DC9-4E0E-B930-A7E3968D5DD1}", EntityDebugDisplayComponent) + static void Reflect(AZ::ReflectContext* context); + + AxisAlignedBoxShapeDebugDisplayComponent() = default; + + // AZ::Component + void Activate() override; + void Deactivate() override; + bool ReadInConfig(const AZ::ComponentConfig* baseConfig) override; + bool WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const override; + + // EntityDebugDisplayComponent + void Draw(AzFramework::DebugDisplayRequests& debugDisplay) override; + + private: + AZ_DISABLE_COPY_MOVE(AxisAlignedBoxShapeDebugDisplayComponent) + + // ShapeComponentNotificationsBus + void OnShapeChanged(ShapeChangeReasons changeReason) override; + + BoxShapeConfig m_boxShapeConfig; ///< Stores configuration data for box shape. + AZ::Vector3 m_nonUniformScale = AZ::Vector3::CreateOne(); ///< Caches non-uniform scale for this entity. + }; +} // namespace LmbrCentral diff --git a/Gems/LmbrCentral/Code/Source/Shape/BoxShape.h b/Gems/LmbrCentral/Code/Source/Shape/BoxShape.h index a3831fbee6..a9dfd5c35f 100644 --- a/Gems/LmbrCentral/Code/Source/Shape/BoxShape.h +++ b/Gems/LmbrCentral/Code/Source/Shape/BoxShape.h @@ -37,7 +37,7 @@ namespace LmbrCentral static void Reflect(AZ::ReflectContext* context); - void Activate(AZ::EntityId entityId); + virtual void Activate(AZ::EntityId entityId); void Deactivate(); void InvalidateCache(InvalidateShapeCacheReason reason); @@ -67,12 +67,9 @@ namespace LmbrCentral void SetDrawColor(const AZ::Color& color) { m_boxShapeConfig.SetDrawColor(color); } - protected: - - friend class EditorBoxShapeComponent; BoxShapeConfig& ModifyConfiguration() { return m_boxShapeConfig; } - private: + protected: /// Runtime data - cache potentially expensive operations. class BoxIntersectionDataCache : public IntersectionTestDataCache @@ -82,6 +79,7 @@ namespace LmbrCentral const AZ::Vector3& currentNonUniformScale = AZ::Vector3::CreateOne()) override; friend BoxShape; + friend class AxisAlignedBoxShape; AZ::Aabb m_aabb; ///< Aabb representing this Box (including the effects of scale). AZ::Obb m_obb; ///< Obb representing this Box (including the effects of scale). @@ -90,12 +88,12 @@ namespace LmbrCentral bool m_axisAligned = true; ///< Indicates whether the box is axis or object aligned. }; - BoxShapeConfig m_boxShapeConfig; ///< Underlying box configuration. BoxIntersectionDataCache m_intersectionDataCache; ///< Caches transient intersection data. AZ::Transform m_currentTransform; ///< Caches the current transform for the entity on which this component lives. AZ::EntityId m_entityId; ///< Id of the entity the box shape is attached to. AZ::NonUniformScaleChangedEvent::Handler m_nonUniformScaleChangedHandler; ///< Responds to changes in non-uniform scale. AZ::Vector3 m_currentNonUniformScale = AZ::Vector3::CreateOne(); ///< Caches the current non-uniform scale. + BoxShapeConfig m_boxShapeConfig; ///< Underlying box configuration. }; void DrawBoxShape( diff --git a/Gems/LmbrCentral/Code/Source/Shape/BoxShapeComponent.cpp b/Gems/LmbrCentral/Code/Source/Shape/BoxShapeComponent.cpp index 40d5dce3cc..ce4a934e91 100644 --- a/Gems/LmbrCentral/Code/Source/Shape/BoxShapeComponent.cpp +++ b/Gems/LmbrCentral/Code/Source/Shape/BoxShapeComponent.cpp @@ -101,23 +101,15 @@ namespace LmbrCentral } } - namespace ClassConverters - { - static bool DeprecateBoxColliderConfiguration(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement); - static bool DeprecateBoxColliderComponent(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement); - } - void BoxShapeConfig::Reflect(AZ::ReflectContext* context) { + // Don't reflect again if we're already reflected to the passed in context + if (context->IsTypeReflected(BoxShapeConfigTypeId)) + { + return; + } if (auto serializeContext = azrtti_cast(context)) { - // Deprecate: BoxColliderConfiguration -> BoxShapeConfig - serializeContext->ClassDeprecate( - "BoxColliderConfiguration", - "{282E47CB-9F6D-47AE-A210-4CE879527EFD}", - &ClassConverters::DeprecateBoxColliderConfiguration) - ; - serializeContext->Class() ->Version(2) ->Field("Dimensions", &BoxShapeConfig::m_dimensions) @@ -151,13 +143,6 @@ namespace LmbrCentral if (auto serializeContext = azrtti_cast(context)) { - // Deprecate: BoxColliderComponent -> BoxShapeComponent - serializeContext->ClassDeprecate( - "BoxColliderComponent", - "{C215EB2A-1803-4EDC-B032-F7C92C142337}", - &ClassConverters::DeprecateBoxColliderComponent) - ; - serializeContext->Class() ->Version(2, &ClassConverters::UpgradeBoxShapeComponent) ->Field("BoxShape", &BoxShapeComponent::m_boxShape) @@ -205,85 +190,4 @@ namespace LmbrCentral } return false; } - - namespace ClassConverters - { - static bool DeprecateBoxColliderConfiguration(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement) - { - /* - Old: - - - - - New: - - - - */ - - // Cache the Dimensions - AZ::Vector3 oldDimensions; - const int oldIndex = classElement.FindElement(AZ_CRC("Size", 0xf7c0246a)); - if (oldIndex != -1) - { - classElement.GetSubElement(oldIndex).GetData(oldDimensions); - } - - // Convert to BoxShapeConfig - const bool result = classElement.Convert(context, "{F034FBA2-AC2F-4E66-8152-14DFB90D6283}"); - if (result) - { - const int newIndex = classElement.AddElement(context, "Dimensions"); - if (newIndex != -1) - { - classElement.GetSubElement(newIndex).SetData(context, oldDimensions); - return true; - } - } - return false; - } - - static bool DeprecateBoxColliderComponent(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement) - { - /* - Old: - - - - - - - New: - - - - - - */ - - // Cache the Configuration - BoxShapeConfig configuration; - int configIndex = classElement.FindElement(AZ_CRC("Configuration", 0xa5e2a5d7)); - if (configIndex != -1) - { - classElement.GetSubElement(configIndex).GetData(configuration); - } - - // Convert to BoxShapeComponent - const bool result = classElement.Convert(context, BoxShapeComponentTypeId); - if (result) - { - configIndex = classElement.AddElement(context, "Configuration"); - if (configIndex != -1) - { - classElement.GetSubElement(configIndex).SetData(context, configuration); - } - return true; - } - return false; - } - - } // namespace ClassConverters - } // namespace LmbrCentral diff --git a/Gems/LmbrCentral/Code/Source/Shape/EditorAxisAlignedBoxShapeComponent.cpp b/Gems/LmbrCentral/Code/Source/Shape/EditorAxisAlignedBoxShapeComponent.cpp new file mode 100644 index 0000000000..c833677b1d --- /dev/null +++ b/Gems/LmbrCentral/Code/Source/Shape/EditorAxisAlignedBoxShapeComponent.cpp @@ -0,0 +1,168 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + + +#include +#include +#include +#include +#include + +#include "AxisAlignedBoxShapeComponent.h" +#include "EditorAxisAlignedBoxShapeComponent.h" +#include "EditorShapeComponentConverters.h" +#include "ShapeDisplay.h" + +namespace LmbrCentral +{ + void EditorAxisAlignedBoxShapeComponent::Reflect(AZ::ReflectContext* context) + { + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(1) + ->Field("AxisAlignedBoxShape", &EditorAxisAlignedBoxShapeComponent::m_aaboxShape) + ->Field("ComponentMode", &EditorAxisAlignedBoxShapeComponent::m_componentModeDelegate) + ; + + if (auto editContext = serializeContext->GetEditContext()) + { + editContext->Class( + "Axis Aligned Box Shape", "The Axis Aligned Box Shape component creates a box around the associated entity") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::Category, "Shape") + ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/Box_Shape.svg") + ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Box_Shape.svg") + ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game")) + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/shape/axis-aligned-box-shape/") + ->DataElement(AZ::Edit::UIHandlers::Default, &EditorAxisAlignedBoxShapeComponent::m_aaboxShape, "Axis Aligned Box Shape", "Axis Aligned Box Shape Configuration") + ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorAxisAlignedBoxShapeComponent::ConfigurationChanged) + ->DataElement(AZ::Edit::UIHandlers::Default, &EditorAxisAlignedBoxShapeComponent::m_componentModeDelegate, "Component Mode", "Axis Aligned Box Shape Component Mode") + ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) + ; + } + } + } + + void EditorAxisAlignedBoxShapeComponent::Init() + { + EditorBaseShapeComponent::Init(); + + SetShapeComponentConfig(&m_aaboxShape.ModifyConfiguration()); + } + + void EditorAxisAlignedBoxShapeComponent::Activate() + { + EditorBaseShapeComponent::Activate(); + m_aaboxShape.Activate(GetEntityId()); + AzFramework::EntityDebugDisplayEventBus::Handler::BusConnect(GetEntityId()); + AzToolsFramework::BoxManipulatorRequestBus::Handler::BusConnect( + AZ::EntityComponentIdPair(GetEntityId(), GetId())); + + // ComponentMode + m_componentModeDelegate.ConnectWithSingleComponentMode< + EditorAxisAlignedBoxShapeComponent, AzToolsFramework::BoxComponentMode>( + AZ::EntityComponentIdPair(GetEntityId(), GetId()), this); + } + + void EditorAxisAlignedBoxShapeComponent::Deactivate() + { + m_componentModeDelegate.Disconnect(); + + AzToolsFramework::BoxManipulatorRequestBus::Handler::BusDisconnect(); + AzFramework::EntityDebugDisplayEventBus::Handler::BusDisconnect(); + m_aaboxShape.Deactivate(); + EditorBaseShapeComponent::Deactivate(); + } + + void EditorAxisAlignedBoxShapeComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + EditorBaseShapeComponent::GetProvidedServices(provided); + provided.push_back(AZ_CRC_CE("BoxShapeService")); + provided.push_back(AZ_CRC_CE("AxisAlignedBoxShapeService")); + } + + void EditorAxisAlignedBoxShapeComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent) + { + dependent.push_back(AZ_CRC_CE("NonUniformScaleService")); + } + + void EditorAxisAlignedBoxShapeComponent::DisplayEntityViewport( + [[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo, + AzFramework::DebugDisplayRequests& debugDisplay) + { + DisplayShape( + debugDisplay, [this]() { return CanDraw(); }, + [this](AzFramework::DebugDisplayRequests& debugDisplay) + { + DrawBoxShape( + { m_aaboxShape.GetBoxConfiguration().GetDrawColor(), m_shapeWireColor, m_aaboxShape.GetBoxConfiguration().IsFilled() }, + m_aaboxShape.GetBoxConfiguration(), debugDisplay, m_aaboxShape.GetCurrentNonUniformScale()); + }, + m_aaboxShape.GetCurrentTransform()); + } + + void EditorAxisAlignedBoxShapeComponent::ConfigurationChanged() + { + m_aaboxShape.InvalidateCache(InvalidateShapeCacheReason::ShapeChange); + + ShapeComponentNotificationsBus::Event(GetEntityId(), + &ShapeComponentNotificationsBus::Events::OnShapeChanged, + ShapeComponentNotifications::ShapeChangeReasons::ShapeChanged); + + AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::Broadcast( + &AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequests::Refresh, + AZ::EntityComponentIdPair(GetEntityId(), GetId())); + } + + void EditorAxisAlignedBoxShapeComponent::BuildGameEntity(AZ::Entity* gameEntity) + { + if (AxisAlignedBoxShapeComponent* boxShapeComponent = gameEntity->CreateComponent()) + { + boxShapeComponent->SetConfiguration(m_aaboxShape.GetBoxConfiguration()); + } + + if (m_visibleInGameView) + { + if (auto component = gameEntity->CreateComponent()) + { + component->SetConfiguration(m_aaboxShape.GetBoxConfiguration()); + } + } + } + + void EditorAxisAlignedBoxShapeComponent::OnTransformChanged( + const AZ::Transform& /*local*/, const AZ::Transform& /*world*/) + { + AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::Broadcast( + &AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequests::Refresh, + AZ::EntityComponentIdPair(GetEntityId(), GetId())); + } + + AZ::Vector3 EditorAxisAlignedBoxShapeComponent::GetDimensions() + { + return m_aaboxShape.GetBoxDimensions(); + } + + void EditorAxisAlignedBoxShapeComponent::SetDimensions(const AZ::Vector3& dimensions) + { + return m_aaboxShape.SetBoxDimensions(dimensions); + } + + AZ::Transform EditorAxisAlignedBoxShapeComponent::GetCurrentTransform() + { + return AzToolsFramework::TransformNormalizedScale(m_aaboxShape.GetCurrentTransform()); + } + + AZ::Vector3 EditorAxisAlignedBoxShapeComponent::GetBoxScale() + { + return AZ::Vector3(m_aaboxShape.GetCurrentTransform().GetUniformScale() * m_aaboxShape.GetCurrentNonUniformScale()); + } +} // namespace LmbrCentral diff --git a/Gems/LmbrCentral/Code/Source/Shape/EditorAxisAlignedBoxShapeComponent.h b/Gems/LmbrCentral/Code/Source/Shape/EditorAxisAlignedBoxShapeComponent.h new file mode 100644 index 0000000000..8bff4ea7e1 --- /dev/null +++ b/Gems/LmbrCentral/Code/Source/Shape/EditorAxisAlignedBoxShapeComponent.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include "AxisAlignedBoxShape.h" +#include "AxisAlignedBoxShapeComponent.h" +#include "EditorBaseShapeComponent.h" + +#include +#include +#include + + +namespace LmbrCentral +{ + /// Editor representation of Box Shape Component. + class EditorAxisAlignedBoxShapeComponent + : public EditorBaseShapeComponent + , private AzFramework::EntityDebugDisplayEventBus::Handler + , private AzToolsFramework::BoxManipulatorRequestBus::Handler + { + public: + AZ_EDITOR_COMPONENT(EditorAxisAlignedBoxShapeComponent, EditorAxisAlignedBoxShapeComponentTypeId, EditorBaseShapeComponent); + static void Reflect(AZ::ReflectContext* context); + + EditorAxisAlignedBoxShapeComponent() = default; + + // AZ::Component + void Init() override; + void Activate() override; + void Deactivate() override; + + protected: + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); + static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent); + + // EditorComponentBase + void BuildGameEntity(AZ::Entity* gameEntity) override; + + // AZ::TransformNotificationBus::Handler + void OnTransformChanged(const AZ::Transform& local, const AZ::Transform& world) override; + + private: + AZ_DISABLE_COPY_MOVE(EditorAxisAlignedBoxShapeComponent) + + // AzFramework::EntityDebugDisplayEventBus + void DisplayEntityViewport( + const AzFramework::ViewportInfo& viewportInfo, + AzFramework::DebugDisplayRequests& debugDisplay) override; + + // AzToolsFramework::BoxManipulatorRequestBus + AZ::Vector3 GetDimensions() override; + void SetDimensions(const AZ::Vector3& dimensions) override; + AZ::Transform GetCurrentTransform() override; + AZ::Vector3 GetBoxScale() override; + + void ConfigurationChanged(); + + AxisAlignedBoxShape m_aaboxShape; ///< Stores underlying box representation for this component. + + using ComponentModeDelegate = AzToolsFramework::ComponentModeFramework::ComponentModeDelegate; + ComponentModeDelegate m_componentModeDelegate; /**< Responsible for detecting ComponentMode activation + * and creating a concrete ComponentMode.*/ + }; +} // namespace LmbrCentral diff --git a/Gems/LmbrCentral/Code/Source/Shape/EditorBoxShapeComponent.cpp b/Gems/LmbrCentral/Code/Source/Shape/EditorBoxShapeComponent.cpp index f645c54610..2983ca7753 100644 --- a/Gems/LmbrCentral/Code/Source/Shape/EditorBoxShapeComponent.cpp +++ b/Gems/LmbrCentral/Code/Source/Shape/EditorBoxShapeComponent.cpp @@ -49,7 +49,7 @@ namespace LmbrCentral ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/shape/box-shape/") ->DataElement(AZ::Edit::UIHandlers::Default, &EditorBoxShapeComponent::m_boxShape, "Box Shape", "Box Shape Configuration") - // ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) // disabled - prevents ChangeNotify attribute firing correctly + ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorBoxShapeComponent::ConfigurationChanged) ->DataElement(AZ::Edit::UIHandlers::Default, &EditorBoxShapeComponent::m_componentModeDelegate, "Component Mode", "Box Shape Component Mode") ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) diff --git a/Gems/LmbrCentral/Code/Tests/AxisAlignedBoxShapeTest.cpp b/Gems/LmbrCentral/Code/Tests/AxisAlignedBoxShapeTest.cpp new file mode 100644 index 0000000000..c3e8d06791 --- /dev/null +++ b/Gems/LmbrCentral/Code/Tests/AxisAlignedBoxShapeTest.cpp @@ -0,0 +1,238 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace UnitTest +{ + class AxisAlignedBoxShapeTest : public AllocatorsFixture + { + AZStd::unique_ptr m_serializeContext; + AZStd::unique_ptr m_transformComponentDescriptor; + AZStd::unique_ptr m_axisAlignedBoxShapeComponentDescriptor; + AZStd::unique_ptr m_axisAlignedBoxShapeDebugDisplayComponentDescriptor; + AZStd::unique_ptr m_nonUniformScaleComponentDescriptor; + + public: + void SetUp() override + { + AllocatorsFixture::SetUp(); + m_serializeContext = AZStd::make_unique(); + + m_transformComponentDescriptor = + AZStd::unique_ptr(AzFramework::TransformComponent::CreateDescriptor()); + m_transformComponentDescriptor->Reflect(&(*m_serializeContext)); + m_axisAlignedBoxShapeComponentDescriptor = + AZStd::unique_ptr(LmbrCentral::AxisAlignedBoxShapeComponent::CreateDescriptor()); + m_axisAlignedBoxShapeComponentDescriptor->Reflect(&(*m_serializeContext)); + m_axisAlignedBoxShapeDebugDisplayComponentDescriptor = + AZStd::unique_ptr(LmbrCentral::AxisAlignedBoxShapeDebugDisplayComponent::CreateDescriptor()); + m_axisAlignedBoxShapeDebugDisplayComponentDescriptor->Reflect(&(*m_serializeContext)); + m_nonUniformScaleComponentDescriptor = + AZStd::unique_ptr(AzFramework::NonUniformScaleComponent::CreateDescriptor()); + m_nonUniformScaleComponentDescriptor->Reflect(&(*m_serializeContext)); + } + + void TearDown() override + { + m_transformComponentDescriptor.reset(); + m_axisAlignedBoxShapeComponentDescriptor.reset(); + m_axisAlignedBoxShapeDebugDisplayComponentDescriptor.reset(); + m_nonUniformScaleComponentDescriptor.reset(); + m_serializeContext.reset(); + AllocatorsFixture::TearDown(); + } + }; + + void CreateAxisAlignedBox(const AZ::Transform& transform, const AZ::Vector3& dimensions, AZ::Entity& entity) + { + entity.CreateComponent(); + entity.CreateComponent(); + entity.CreateComponent(); + + entity.Init(); + entity.Activate(); + + AZ::TransformBus::Event(entity.GetId(), &AZ::TransformBus::Events::SetWorldTM, transform); + LmbrCentral::BoxShapeComponentRequestsBus::Event( + entity.GetId(), &LmbrCentral::BoxShapeComponentRequestsBus::Events::SetBoxDimensions, dimensions); + } + + void CreateAxisAlignedBoxWithNonUniformScale( + const AZ::Transform& transform, const AZ::Vector3& nonUniformScale, const AZ::Vector3& dimensions, AZ::Entity& entity) + { + entity.CreateComponent(); + entity.CreateComponent(); + entity.CreateComponent(); + entity.CreateComponent(); + + entity.Init(); + entity.Activate(); + + AZ::TransformBus::Event(entity.GetId(), &AZ::TransformBus::Events::SetWorldTM, transform); + LmbrCentral::BoxShapeComponentRequestsBus::Event( + entity.GetId(), &LmbrCentral::BoxShapeComponentRequestsBus::Events::SetBoxDimensions, dimensions); + AZ::NonUniformScaleRequestBus::Event(entity.GetId(), &AZ::NonUniformScaleRequests::SetScale, nonUniformScale); + } + + void CreateDefaultAxisAlignedBox(const AZ::Transform& transform, AZ::Entity& entity) + { + CreateAxisAlignedBox(transform, AZ::Vector3(10.0f, 10.0f, 10.0f), entity); + } + + TEST_F(AxisAlignedBoxShapeTest, EntityTransformIsCorrect) + { + AZ::Entity entity; + CreateAxisAlignedBox( + AZ::Transform::CreateTranslation(AZ::Vector3(0.0f, 0.0f, 0.0f)) * AZ::Transform::CreateRotationZ(AZ::Constants::QuarterPi), + AZ::Vector3(1.0f), entity); + + AZ::Transform transform; + AZ::TransformBus::EventResult(transform, entity.GetId(), &AZ::TransformBus::Events::GetWorldTM); + + EXPECT_EQ(transform, AZ::Transform::CreateRotationZ(AZ::Constants::QuarterPi)); + } + + TEST_F(AxisAlignedBoxShapeTest, BoxWithZRotationHasCorrectRayIntersection) + { + AZ::Entity entity; + CreateAxisAlignedBox( + AZ::Transform::CreateRotationZ(AZ::Constants::QuarterPi), + AZ::Vector3(1.0f), entity); + + bool rayHit = false; + float distance; + LmbrCentral::ShapeComponentRequestsBus::EventResult( + rayHit, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay, AZ::Vector3(5.0f, 0.0f, 0.0f), + AZ::Vector3(-1.0f, 0.0f, 0.0f), distance); + + // This test creates a unit box centered on (0, 0, 0) and rotated by 45 degrees. The distance to the box should + // be 4.5 if it isn't rotated but less if there is any rotation. + EXPECT_TRUE(rayHit); + EXPECT_NEAR(distance, 4.5f, 1e-2f); + } + + TEST_F(AxisAlignedBoxShapeTest, BoxWithTranslationAndRotationsHasCorrectRayIntersection) + { + AZ::Entity entity; + CreateAxisAlignedBox( + AZ::Transform::CreateFromQuaternionAndTranslation( + AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::Constants::HalfPi) * + AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisZ(), AZ::Constants::QuarterPi), + AZ::Vector3(-10.0f, -10.0f, -10.0f)), + AZ::Vector3(4.0f, 4.0f, 2.0f), entity); + + bool rayHit = false; + float distance; + LmbrCentral::ShapeComponentRequestsBus::EventResult( + rayHit, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay, AZ::Vector3(-10.0f, -10.0f, 0.0f), + AZ::Vector3(0.0f, 0.0f, -1.0f), distance); + + // This test creates a box of dimensions (4.0, 4.0, 2.0) centered on (-10, -10, 0) and rotated in X and Z. The distance to the box should + // be 9.0 if it isn't rotated but less if there is any rotation. + EXPECT_TRUE(rayHit); + EXPECT_NEAR(distance, 9.00f, 1e-2f); + } + + TEST_F(AxisAlignedBoxShapeTest, BoxWithTranslationHasCorrectRayIntersection) + { + AZ::Entity entity; + CreateAxisAlignedBox( + AZ::Transform::CreateTranslation(AZ::Vector3(100.0f, 100.0f, 0.0f)), + AZ::Vector3(5.0f, 5.0f, 5.0f), entity); + + bool rayHit = false; + float distance; + LmbrCentral::ShapeComponentRequestsBus::EventResult( + rayHit, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay, AZ::Vector3(100.0f, 100.0f, -100.0f), + AZ::Vector3(0.0f, 0.0f, 1.0f), distance); + + // This test creates a box of dimensions (5.0, 5.0, 5.0) centered on (100, 100, 0) and not rotated. The distance to the box + // should be 97.5. + EXPECT_TRUE(rayHit); + EXPECT_NEAR(distance, 97.5f, 1e-2f); + } + + TEST_F(AxisAlignedBoxShapeTest, BoxWithTranslationRotationAndScaleHasCorrectRayIntersection) + { + AZ::Entity entity; + CreateAxisAlignedBox( + AZ::Transform( + AZ::Vector3(0.0f, 0.0f, 5.0f), AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisY(), AZ::Constants::QuarterPi), 3.0f), + AZ::Vector3(2.0f, 4.0f, 1.0f), entity); + + bool rayHit = false; + float distance; + LmbrCentral::ShapeComponentRequestsBus::EventResult( + rayHit, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay, AZ::Vector3(1.0f, -10.0f, 4.0f), + AZ::Vector3(0.0f, 1.0f, 0.0f), distance); + + // This test creates a box of dimensions (2.0, 4.0, 1.0) centered on (0, 0, 5) and rotated about the Y axis by 45 degrees. + // The distance to the box should be 4.0 if not rotated but scaled and less if it is. + EXPECT_TRUE(rayHit); + EXPECT_NEAR(distance, 4.0f, 1e-2f); + } + + TEST_F(AxisAlignedBoxShapeTest, RayIntersectWithBoxRotatedNonUniformScale) + { + AZ::Entity entity; + CreateAxisAlignedBoxWithNonUniformScale( + AZ::Transform( + AZ::Vector3(2.0f, -5.0f, 3.0f), AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisY(), AZ::Constants::QuarterPi), + 0.5f), + AZ::Vector3(2.2f, 1.8f, 0.4f), AZ::Vector3(0.2f, 2.6f, 1.2f), entity); + + // This test creates a box of dimensions (2.2, 1.8, 0.4) centered on (2.0, -5, 3) and rotated about the Y axis by 45 degrees. + // The box is tested for axis-alignment by firing various rays and ensuring they either hit or miss the box. Any failure here + // would show the box has been rotated. + + // Ray should just miss the box + bool rayHit = false; + float distance = AZ::Constants::FloatMax; + LmbrCentral::ShapeComponentRequestsBus::EventResult( + rayHit, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay, AZ::Vector3(1.8f, -6.2f, 3.0f), + AZ::Vector3(1.0f, 0.0f, 0.0f), distance); + EXPECT_FALSE(rayHit); + + // Ray should just hit the box + rayHit = false; + distance = AZ::Constants::FloatMax; + LmbrCentral::ShapeComponentRequestsBus::EventResult( + rayHit, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay, AZ::Vector3(1.8f, -6.1f, 3.0f), + AZ::Vector3(1.0f, 0.0f, 0.0f), distance); + EXPECT_TRUE(rayHit); + EXPECT_NEAR(distance, 0.09f, 1e-3f); + + // Ray should just miss the box + rayHit = false; + distance = AZ::Constants::FloatMax; + LmbrCentral::ShapeComponentRequestsBus::EventResult( + rayHit, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay, AZ::Vector3(2.2f, -6.2f, 3.0f), + AZ::Vector3(0.0f, 1.0f, 0.0f), distance); + EXPECT_FALSE(rayHit); + + // Ray should just hit the box + rayHit = false; + distance = AZ::Constants::FloatMax; + LmbrCentral::ShapeComponentRequestsBus::EventResult( + rayHit, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay, AZ::Vector3(2.1f, -6.2f, 3.0f), + AZ::Vector3(0.0f, 1.0f, 0.0f), distance); + EXPECT_TRUE(rayHit); + EXPECT_NEAR(distance, 0.03f, 1e-3f); + } +} // namespace UnitTest diff --git a/Gems/LmbrCentral/Code/Tests/DiskShapeTest.cpp b/Gems/LmbrCentral/Code/Tests/DiskShapeTest.cpp index e9c19585cc..65693a2a2e 100644 --- a/Gems/LmbrCentral/Code/Tests/DiskShapeTest.cpp +++ b/Gems/LmbrCentral/Code/Tests/DiskShapeTest.cpp @@ -34,10 +34,10 @@ namespace 0.5f, 1.0f, 2.0f, 4.0f, 8.0f, }; - const uint32_t RayCount = 5; + const uint32_t RayCountDisk = 5; // Various normalized offset directions from center of disk along disk's surface. - const AZStd::array OffsetsFromCenter = + const AZStd::array OffsetsFromCenterDisk = { AZ::Vector3(0.18f, -0.50f, 0.0f).GetNormalized(), AZ::Vector3(-0.08f, 0.59f, 0.0f).GetNormalized(), @@ -47,7 +47,7 @@ namespace }; // Various directions away from a point on the disk's surface - const AZStd::array OffsetsFromSurface = + const AZStd::array OffsetsFromSurfaceDisk = { AZ::Vector3(0.69f, 0.38f, 0.09f).GetNormalized(), AZ::Vector3(-0.98f, -0.68f, -0.28f).GetNormalized(), @@ -57,7 +57,7 @@ namespace }; // Various distance away from the surface for the rays - const AZStd::array RayDistances = + const AZStd::array RayDistancesDisk = { 0.5f, 1.0f, 2.0f, 4.0f, 8.0f }; @@ -185,7 +185,7 @@ namespace UnitTest } // Offsets from center scaled down from the disk edge so that all the rays should hit - const AZStd::array offsetFromCenterScale = + const AZStd::array offsetFromCenterScale = { 0.8f, 0.2f, @@ -197,20 +197,20 @@ namespace UnitTest // Construct rays and test against the different disks for (uint32_t diskIndex = 0; diskIndex < DiskCount; ++diskIndex) { - for (uint32_t rayIndex = 0; rayIndex < RayCount; ++rayIndex) + for (uint32_t rayIndex = 0; rayIndex < RayCountDisk; ++rayIndex) { - AZ::Vector3 scaledOffsetFromCenter = OffsetsFromCenter[rayIndex] * DiskRadii[diskIndex] * offsetFromCenterScale[rayIndex]; + AZ::Vector3 scaledOffsetFromCenter = OffsetsFromCenterDisk[rayIndex] * DiskRadii[diskIndex] * offsetFromCenterScale[rayIndex]; AZ::Vector3 positionOnDiskSurface = DiskTransforms[diskIndex].TransformPoint(scaledOffsetFromCenter); - AZ::Vector3 rayOrigin = positionOnDiskSurface + OffsetsFromSurface[rayIndex] * RayDistances[rayIndex]; + AZ::Vector3 rayOrigin = positionOnDiskSurface + OffsetsFromSurfaceDisk[rayIndex] * RayDistancesDisk[rayIndex]; bool rayHit2 = false; float distance2; LmbrCentral::ShapeComponentRequestsBus::EventResult( rayHit2, diskEntities[diskIndex].GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay, - rayOrigin, -OffsetsFromSurface[rayIndex], distance2); + rayOrigin, -OffsetsFromSurfaceDisk[rayIndex], distance2); EXPECT_TRUE(rayHit2); - EXPECT_NEAR(distance2, RayDistances[rayIndex], 1e-4f); + EXPECT_NEAR(distance2, RayDistancesDisk[rayIndex], 1e-4f); } } @@ -241,7 +241,7 @@ namespace UnitTest } // Offsets from center scaled up from the disk edge so that all the rays should miss - const AZStd::array offsetFromCenterScale = + const AZStd::array offsetFromCenterScale = { 1.8f, 1.2f, @@ -253,17 +253,17 @@ namespace UnitTest // Construct rays and test against the different disks for (uint32_t diskIndex = 0; diskIndex < DiskCount; ++diskIndex) { - for (uint32_t rayIndex = 0; rayIndex < RayCount; ++rayIndex) + for (uint32_t rayIndex = 0; rayIndex < RayCountDisk; ++rayIndex) { - AZ::Vector3 scaledOffsetFromCenter = OffsetsFromCenter[rayIndex] * DiskRadii[diskIndex] * offsetFromCenterScale[rayIndex]; + AZ::Vector3 scaledOffsetFromCenter = OffsetsFromCenterDisk[rayIndex] * DiskRadii[diskIndex] * offsetFromCenterScale[rayIndex]; AZ::Vector3 positionOnDiskSurface = DiskTransforms[diskIndex].TransformPoint(scaledOffsetFromCenter); - AZ::Vector3 rayOrigin = positionOnDiskSurface + OffsetsFromSurface[rayIndex] * RayDistances[rayIndex]; + AZ::Vector3 rayOrigin = positionOnDiskSurface + OffsetsFromSurfaceDisk[rayIndex] * RayDistancesDisk[rayIndex]; bool rayHit2 = false; float distance2; LmbrCentral::ShapeComponentRequestsBus::EventResult( rayHit2, diskEntities[diskIndex].GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay, - rayOrigin, -OffsetsFromSurface[rayIndex], distance2); + rayOrigin, -OffsetsFromSurfaceDisk[rayIndex], distance2); EXPECT_FALSE(rayHit2); } diff --git a/Gems/LmbrCentral/Code/Tests/QuadShapeTest.cpp b/Gems/LmbrCentral/Code/Tests/QuadShapeTest.cpp index a28916bb24..c02cf18aca 100644 --- a/Gems/LmbrCentral/Code/Tests/QuadShapeTest.cpp +++ b/Gems/LmbrCentral/Code/Tests/QuadShapeTest.cpp @@ -41,10 +41,10 @@ namespace LmbrCentral::QuadShapeConfig(1.0f, 0.5f), }; - const uint32_t RayCount = 5; + const uint32_t RayCountQuad = 5; // Various normalized offset directions from center of quad along quad's surface. - const AZStd::array OffsetsFromCenter = + const AZStd::array OffsetsFromCenterQuad = { AZ::Vector3( 0.18f, -0.50f, 0.0f).GetNormalized(), AZ::Vector3(-0.08f, 0.59f, 0.0f).GetNormalized(), @@ -54,7 +54,7 @@ namespace }; // Various directions away from a point on the quad's surface - const AZStd::array OffsetsFromSurface = + const AZStd::array OffsetsFromSurfaceQuad = { AZ::Vector3( 0.69f, 0.38f, 0.09f).GetNormalized(), AZ::Vector3(-0.98f, -0.68f, -0.28f).GetNormalized(), @@ -64,7 +64,7 @@ namespace }; // Various distance away from the surface for the rays - const AZStd::array RayDistances = + const AZStd::array RayDistancesQuad = { 0.5f, 1.0f, 2.0f, 4.0f, 8.0f }; @@ -248,23 +248,23 @@ namespace UnitTest // Construct rays and test against the different quads for (uint32_t quadIndex = 0; quadIndex < QuadCount; ++quadIndex) { - for (uint32_t rayIndex = 0; rayIndex < RayCount; ++rayIndex) + for (uint32_t rayIndex = 0; rayIndex < RayCountQuad; ++rayIndex) { - // OffsetsFromCenter are all less than 1, so scale by the dimensions of the quad. + // OffsetsFromCenterQuad are all less than 1, so scale by the dimensions of the quad. AZ::Vector3 scaledWidthHeight = AZ::Vector3(QuadDims[quadIndex].m_width, QuadDims[quadIndex].m_height, 0.0f); // Scale the offset and multiply by 0.5 because distance from center is half the width/height - AZ::Vector3 scaledOffsetFromCenter = OffsetsFromCenter[rayIndex] * scaledWidthHeight * 0.5f; + AZ::Vector3 scaledOffsetFromCenter = OffsetsFromCenterQuad[rayIndex] * scaledWidthHeight * 0.5f; AZ::Vector3 positionOnQuadSurface = QuadTransforms[quadIndex].TransformPoint(scaledOffsetFromCenter); - AZ::Vector3 rayOrigin = positionOnQuadSurface + OffsetsFromSurface[rayIndex] * RayDistances[rayIndex]; + AZ::Vector3 rayOrigin = positionOnQuadSurface + OffsetsFromSurfaceQuad[rayIndex] * RayDistancesQuad[rayIndex]; bool rayHit2 = false; float distance2; LmbrCentral::ShapeComponentRequestsBus::EventResult( rayHit2, quadEntities[quadIndex].GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay, - rayOrigin, -OffsetsFromSurface[rayIndex], distance2); + rayOrigin, -OffsetsFromSurfaceQuad[rayIndex], distance2); EXPECT_TRUE(rayHit2); - EXPECT_NEAR(distance2, RayDistances[rayIndex], 1e-4f); + EXPECT_NEAR(distance2, RayDistancesQuad[rayIndex], 1e-4f); } } @@ -297,20 +297,20 @@ namespace UnitTest // Construct rays and test against the different quads for (uint32_t quadIndex = 0; quadIndex < QuadCount; ++quadIndex) { - for (uint32_t rayIndex = 0; rayIndex < RayCount; ++rayIndex) + for (uint32_t rayIndex = 0; rayIndex < RayCountQuad; ++rayIndex) { - // OffsetsFromCenter are all less than 1, so scale by the dimensions of the quad. + // OffsetsFromCenterQuad are all less than 1, so scale by the dimensions of the quad. AZ::Vector3 scaledWidthHeight = AZ::Vector3(QuadDims[quadIndex].m_width, QuadDims[quadIndex].m_height, 0.0f); - // Scale the offset and add 1.0 to OffsetsFromCenter to ensure the point is outside the quad. - AZ::Vector3 scaledOffsetFromCenter = (AZ::Vector3::CreateOne() + OffsetsFromCenter[rayIndex]) * scaledWidthHeight; + // Scale the offset and add 1.0 to OffsetsFromCenterQuad to ensure the point is outside the quad. + AZ::Vector3 scaledOffsetFromCenter = (AZ::Vector3::CreateOne() + OffsetsFromCenterQuad[rayIndex]) * scaledWidthHeight; AZ::Vector3 positionOnQuadSurface = QuadTransforms[quadIndex].TransformPoint(scaledOffsetFromCenter); - AZ::Vector3 rayOrigin = positionOnQuadSurface + OffsetsFromSurface[rayIndex] * RayDistances[rayIndex]; + AZ::Vector3 rayOrigin = positionOnQuadSurface + OffsetsFromSurfaceQuad[rayIndex] * RayDistancesQuad[rayIndex]; bool rayHit2 = false; float distance2; LmbrCentral::ShapeComponentRequestsBus::EventResult( rayHit2, quadEntities[quadIndex].GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay, - rayOrigin, -OffsetsFromSurface[rayIndex], distance2); + rayOrigin, -OffsetsFromSurfaceQuad[rayIndex], distance2); EXPECT_FALSE(rayHit2); } diff --git a/Gems/LmbrCentral/Code/include/LmbrCentral/Shape/BoxShapeComponentBus.h b/Gems/LmbrCentral/Code/include/LmbrCentral/Shape/BoxShapeComponentBus.h index a84b0e2a4f..488e8b5617 100644 --- a/Gems/LmbrCentral/Code/include/LmbrCentral/Shape/BoxShapeComponentBus.h +++ b/Gems/LmbrCentral/Code/include/LmbrCentral/Shape/BoxShapeComponentBus.h @@ -21,13 +21,16 @@ namespace LmbrCentral /// Type ID for the EditorBoxShapeComponent static const AZ::Uuid EditorBoxShapeComponentTypeId = "{2ADD9043-48E8-4263-859A-72E0024372BF}"; + /// Type ID for the BoxShapeConfig + static const AZ::Uuid BoxShapeConfigTypeId = "{F034FBA2-AC2F-4E66-8152-14DFB90D6283}"; + /// Configuration data for BoxShapeComponent class BoxShapeConfig : public ShapeComponentConfig { public: AZ_CLASS_ALLOCATOR(BoxShapeConfig, AZ::SystemAllocator, 0) - AZ_RTTI(BoxShapeConfig, "{F034FBA2-AC2F-4E66-8152-14DFB90D6283}", ShapeComponentConfig) + AZ_RTTI(BoxShapeConfig, BoxShapeConfigTypeId, ShapeComponentConfig) static void Reflect(AZ::ReflectContext* context); diff --git a/Gems/LmbrCentral/Code/lmbrcentral_editor_files.cmake b/Gems/LmbrCentral/Code/lmbrcentral_editor_files.cmake index f96fd7a3b2..5c77888922 100644 --- a/Gems/LmbrCentral/Code/lmbrcentral_editor_files.cmake +++ b/Gems/LmbrCentral/Code/lmbrcentral_editor_files.cmake @@ -50,6 +50,8 @@ set(FILES Source/Shape/EditorDiskShapeComponent.cpp Source/Shape/EditorBoxShapeComponent.h Source/Shape/EditorBoxShapeComponent.cpp + Source/Shape/EditorAxisAlignedBoxShapeComponent.h + Source/Shape/EditorAxisAlignedBoxShapeComponent.cpp Source/Shape/EditorCylinderShapeComponent.h Source/Shape/EditorCylinderShapeComponent.cpp Source/Shape/EditorCapsuleShapeComponent.h diff --git a/Gems/LmbrCentral/Code/lmbrcentral_files.cmake b/Gems/LmbrCentral/Code/lmbrcentral_files.cmake index d663d2ec06..18412e2a38 100644 --- a/Gems/LmbrCentral/Code/lmbrcentral_files.cmake +++ b/Gems/LmbrCentral/Code/lmbrcentral_files.cmake @@ -106,6 +106,10 @@ set(FILES Source/Shape/SphereShape.cpp Source/Shape/SphereShapeComponent.h Source/Shape/SphereShapeComponent.cpp + Source/Shape/AxisAlignedBoxShape.h + Source/Shape/AxisAlignedBoxShape.cpp + Source/Shape/AxisAlignedBoxShapeComponent.h + Source/Shape/AxisAlignedBoxShapeComponent.cpp Source/Shape/BoxShape.h Source/Shape/BoxShape.cpp Source/Shape/BoxShapeComponent.h diff --git a/Gems/LmbrCentral/Code/lmbrcentral_tests_files.cmake b/Gems/LmbrCentral/Code/lmbrcentral_tests_files.cmake index 5c4da3db73..c0f1ffd9ec 100644 --- a/Gems/LmbrCentral/Code/lmbrcentral_tests_files.cmake +++ b/Gems/LmbrCentral/Code/lmbrcentral_tests_files.cmake @@ -8,6 +8,7 @@ set(FILES Tests/AudioComponentTests.cpp + Tests/AxisAlignedBoxShapeTest.cpp Tests/BoxShapeTest.cpp Tests/BundlingSystemComponentTests.cpp Tests/SphereShapeTest.cpp