Terrain/jjjoness/3172 axis aligned box shape component (#3981)

* CHanges to Push/Pop Matrix

Signed-off-by: John Jones-Steele <jjjoness@amazon.com>

* Fixed bad commit

Signed-off-by: John Jones-Steele <jjjoness@amazon.com>

* REmoved the AxisAlignedBoxShapeComponentBux and AxisAlignedBoxShapeConfig

Signed-off-by: John Jones-Steele <jjjoness@amazon.com>

* Fixed derivation of AxisAlignedBoxShape

Signed-off-by: John Jones-Steele <jjjoness@amazon.com>

* Added ShowChildrenOnly to Component

Signed-off-by: John Jones-Steele <jjjoness@amazon.com>

* Fixed cmake file

Signed-off-by: John Jones-Steele <jjjoness@amazon.com>

* Changes from review

Signed-off-by: John Jones-Steele <jjjoness@amazon.com>

* Added tests for AxisAlignedBoxShape

Signed-off-by: John Jones-Steele <jjjoness@amazon.com>

* Addressed PR comments and added one further test.

Signed-off-by: John Jones-Steele <jjjoness@amazon.com>

* Removed dead code.

Signed-off-by: John Jones-Steele <jjjoness@amazon.com>

* Changes from review

Signed-off-by: John Jones-Steele <jjjoness@amazon.com>

* Spelling fix and changed link to docs

Signed-off-by: John Jones-Steele <jjjoness@amazon.com>

* Fixed profile bug in Linux

Signed-off-by: John Jones-Steele <jjjoness@amazon.com>

* Fixed problem with Unity Profile Build in Tests

Signed-off-by: John Jones-Steele <jjjoness@amazon.com>
monroegm-disable-blank-issue-2
John Jones-Steele 4 years ago committed by GitHub
parent 6863e9cf9e
commit 817f8ce4c1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -14,6 +14,7 @@
#include <AzCore/Math/Vector2.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/Math/Vector4.h>
#include <AzCore/Math/Matrix3x4.h>
#include <AzCore/Math/Color.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/Component/ComponentBus.h>
@ -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;

@ -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];

@ -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:

@ -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(),

@ -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(),

@ -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 <AzCore/Math/Color.h>
#include <AzCore/Math/IntersectSegment.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/Math/Matrix3x3.h>
#include <AzCore/Math/Random.h>
#include <AzCore/Math/Sfmt.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/std/algorithm.h>
#include <AzCore/std/containers/array.h>
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
#include <Shape/ShapeDisplay.h>
#include <random>
namespace LmbrCentral
{
AxisAlignedBoxShape::AxisAlignedBoxShape()
: BoxShape()
{
}
void AxisAlignedBoxShape::Reflect(AZ::ReflectContext* context)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<AxisAlignedBoxShape, BoxShape>()
->Version(1)
;
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
{
editContext->Class<AxisAlignedBoxShape>("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

@ -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 <AzCore/Component/TransformBus.h>
#include <AzCore/Math/Aabb.h>
#include <AzCore/Math/Obb.h>
#include <AzCore/Component/NonUniformScaleBus.h>
#include <LmbrCentral/Shape/ShapeComponentBus.h>
#include <LmbrCentral/Shape/BoxShapeComponentBus.h>
#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

@ -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 <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
#include <Shape/ShapeComponentConverters.h>
#include <Shape/ShapeDisplay.h>
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<AZ::SerializeContext*>(context))
{
serializeContext->Class<AxisAlignedBoxShapeDebugDisplayComponent, EntityDebugDisplayComponent>()
->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<const BoxShapeConfig*>(baseConfig))
{
m_boxShapeConfig = *config;
return true;
}
return false;
}
bool AxisAlignedBoxShapeDebugDisplayComponent::WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const
{
if (auto outConfig = azrtti_cast<BoxShapeConfig*>(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<AZ::SerializeContext*>(context))
{
serializeContext->Class<AxisAlignedBoxShapeComponent, Component>()
->Version(1)
->Field("AxisAlignedBoxShape", &AxisAlignedBoxShapeComponent::m_aaboxShape)
;
}
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(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<const BoxShapeConfig*>(baseConfig))
{
m_aaboxShape.SetBoxConfiguration(*config);
return true;
}
return false;
}
bool AxisAlignedBoxShapeComponent::WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const
{
if (auto config = azrtti_cast<BoxShapeConfig*>(outBaseConfig))
{
*config = m_aaboxShape.GetBoxConfiguration();
return true;
}
return false;
}
} // namespace LmbrCentral

@ -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 <AzCore/Component/Component.h>
#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

@ -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<BoxShapeConfig>
@ -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(

@ -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<AZ::SerializeContext*>(context))
{
// Deprecate: BoxColliderConfiguration -> BoxShapeConfig
serializeContext->ClassDeprecate(
"BoxColliderConfiguration",
"{282E47CB-9F6D-47AE-A210-4CE879527EFD}",
&ClassConverters::DeprecateBoxColliderConfiguration)
;
serializeContext->Class<BoxShapeConfig, ShapeComponentConfig>()
->Version(2)
->Field("Dimensions", &BoxShapeConfig::m_dimensions)
@ -151,13 +143,6 @@ namespace LmbrCentral
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
// Deprecate: BoxColliderComponent -> BoxShapeComponent
serializeContext->ClassDeprecate(
"BoxColliderComponent",
"{C215EB2A-1803-4EDC-B032-F7C92C142337}",
&ClassConverters::DeprecateBoxColliderComponent)
;
serializeContext->Class<BoxShapeComponent, Component>()
->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:
<Class name="BoxColliderConfiguration" field="Configuration" version="1" type="{282E47CB-9F6D-47AE-A210-4CE879527EFD}">
<Class name="Vector3" field="Size" value="1.0000000 1.0000000 1.0000000" type="{8379EB7D-01FA-4538-B64B-A6543B4BE73D}"/>
</Class>
New:
<Class name="BoxShapeConfig" field="Configuration" version="1" type="{F034FBA2-AC2F-4E66-8152-14DFB90D6283}">
<Class name="Vector3" field="Dimensions" value="1.0000000 1.0000000 1.0000000" type="{8379EB7D-01FA-4538-B64B-A6543B4BE73D}"/>
</Class>
*/
// Cache the Dimensions
AZ::Vector3 oldDimensions;
const int oldIndex = classElement.FindElement(AZ_CRC("Size", 0xf7c0246a));
if (oldIndex != -1)
{
classElement.GetSubElement(oldIndex).GetData<AZ::Vector3>(oldDimensions);
}
// Convert to BoxShapeConfig
const bool result = classElement.Convert(context, "{F034FBA2-AC2F-4E66-8152-14DFB90D6283}");
if (result)
{
const int newIndex = classElement.AddElement<AZ::Vector3>(context, "Dimensions");
if (newIndex != -1)
{
classElement.GetSubElement(newIndex).SetData<AZ::Vector3>(context, oldDimensions);
return true;
}
}
return false;
}
static bool DeprecateBoxColliderComponent(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement)
{
/*
Old:
<Class name="BoxColliderComponent" version="1" type="{C215EB2A-1803-4EDC-B032-F7C92C142337}">
<Class name="BoxColliderConfiguration" field="Configuration" version="1" type="{282E47CB-9F6D-47AE-A210-4CE879527EFD}">
<Class name="Vector3" field="Size" value="1.0000000 1.0000000 1.0000000" type="{8379EB7D-01FA-4538-B64B-A6543B4BE73D}"/>
</Class>
</Class>
New:
<Class name="BoxShapeComponent" version="1" type="{5EDF4B9E-0D3D-40B8-8C91-5142BCFC30A6}">
<Class name="BoxShapeConfig" field="Configuration" version="1" type="{F034FBA2-AC2F-4E66-8152-14DFB90D6283}">
<Class name="Vector3" field="Dimensions" value="1.0000000 2.0000000 3.0000000" type="{8379EB7D-01FA-4538-B64B-A6543B4BE73D}"/>
</Class>
</Class>
*/
// Cache the Configuration
BoxShapeConfig configuration;
int configIndex = classElement.FindElement(AZ_CRC("Configuration", 0xa5e2a5d7));
if (configIndex != -1)
{
classElement.GetSubElement(configIndex).GetData<BoxShapeConfig>(configuration);
}
// Convert to BoxShapeComponent
const bool result = classElement.Convert(context, BoxShapeComponentTypeId);
if (result)
{
configIndex = classElement.AddElement<BoxShapeConfig>(context, "Configuration");
if (configIndex != -1)
{
classElement.GetSubElement(configIndex).SetData<BoxShapeConfig>(context, configuration);
}
return true;
}
return false;
}
} // namespace ClassConverters
} // namespace LmbrCentral

@ -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 <AzCore/Math/Transform.h>
#include <AzCore/RTTI/ReflectContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzToolsFramework/ComponentModes/BoxComponentMode.h>
#include <AzToolsFramework/Maths/TransformUtils.h>
#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<AZ::SerializeContext*>(context))
{
serializeContext->Class<EditorAxisAlignedBoxShapeComponent, EditorBaseShapeComponent>()
->Version(1)
->Field("AxisAlignedBoxShape", &EditorAxisAlignedBoxShapeComponent::m_aaboxShape)
->Field("ComponentMode", &EditorAxisAlignedBoxShapeComponent::m_componentModeDelegate)
;
if (auto editContext = serializeContext->GetEditContext())
{
editContext->Class<EditorAxisAlignedBoxShapeComponent>(
"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<AxisAlignedBoxShapeComponent>())
{
boxShapeComponent->SetConfiguration(m_aaboxShape.GetBoxConfiguration());
}
if (m_visibleInGameView)
{
if (auto component = gameEntity->CreateComponent<AxisAlignedBoxShapeDebugDisplayComponent>())
{
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

@ -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 <AzFramework/Entity/EntityDebugDisplayBus.h>
#include <AzToolsFramework/ComponentMode/ComponentModeDelegate.h>
#include <AzToolsFramework/Manipulators/BoxManipulatorRequestBus.h>
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

@ -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)

@ -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 <AzTest/AzTest.h>
#include <AZTestShared/Math/MathTestHelpers.h>
#include <AzCore/Component/ComponentApplication.h>
#include <AzCore/Math/Matrix3x3.h>
#include <AzCore/Math/Random.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzFramework/Components/NonUniformScaleComponent.h>
#include <AzFramework/Components/TransformComponent.h>
#include <AzFramework/UnitTest/TestDebugDisplayRequests.h>
#include <Shape/AxisAlignedBoxShapeComponent.h>
namespace UnitTest
{
class AxisAlignedBoxShapeTest : public AllocatorsFixture
{
AZStd::unique_ptr<AZ::SerializeContext> m_serializeContext;
AZStd::unique_ptr<AZ::ComponentDescriptor> m_transformComponentDescriptor;
AZStd::unique_ptr<AZ::ComponentDescriptor> m_axisAlignedBoxShapeComponentDescriptor;
AZStd::unique_ptr<AZ::ComponentDescriptor> m_axisAlignedBoxShapeDebugDisplayComponentDescriptor;
AZStd::unique_ptr<AZ::ComponentDescriptor> m_nonUniformScaleComponentDescriptor;
public:
void SetUp() override
{
AllocatorsFixture::SetUp();
m_serializeContext = AZStd::make_unique<AZ::SerializeContext>();
m_transformComponentDescriptor =
AZStd::unique_ptr<AZ::ComponentDescriptor>(AzFramework::TransformComponent::CreateDescriptor());
m_transformComponentDescriptor->Reflect(&(*m_serializeContext));
m_axisAlignedBoxShapeComponentDescriptor =
AZStd::unique_ptr<AZ::ComponentDescriptor>(LmbrCentral::AxisAlignedBoxShapeComponent::CreateDescriptor());
m_axisAlignedBoxShapeComponentDescriptor->Reflect(&(*m_serializeContext));
m_axisAlignedBoxShapeDebugDisplayComponentDescriptor =
AZStd::unique_ptr<AZ::ComponentDescriptor>(LmbrCentral::AxisAlignedBoxShapeDebugDisplayComponent::CreateDescriptor());
m_axisAlignedBoxShapeDebugDisplayComponentDescriptor->Reflect(&(*m_serializeContext));
m_nonUniformScaleComponentDescriptor =
AZStd::unique_ptr<AZ::ComponentDescriptor>(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<LmbrCentral::AxisAlignedBoxShapeComponent>();
entity.CreateComponent<LmbrCentral::AxisAlignedBoxShapeDebugDisplayComponent>();
entity.CreateComponent<AzFramework::TransformComponent>();
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<LmbrCentral::AxisAlignedBoxShapeComponent>();
entity.CreateComponent<LmbrCentral::AxisAlignedBoxShapeDebugDisplayComponent>();
entity.CreateComponent<AzFramework::TransformComponent>();
entity.CreateComponent<AzFramework::NonUniformScaleComponent>();
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

@ -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<AZ::Vector3, RayCount> OffsetsFromCenter =
const AZStd::array<AZ::Vector3, RayCountDisk> 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<AZ::Vector3, RayCount> OffsetsFromSurface =
const AZStd::array<AZ::Vector3, RayCountDisk> 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<float, RayCount> RayDistances =
const AZStd::array<float, RayCountDisk> 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<float, RayCount> offsetFromCenterScale =
const AZStd::array<float, RayCountDisk> 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<float, RayCount> offsetFromCenterScale =
const AZStd::array<float, RayCountDisk> 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);
}

@ -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<AZ::Vector3, RayCount> OffsetsFromCenter =
const AZStd::array<AZ::Vector3, RayCountQuad> 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<AZ::Vector3, RayCount> OffsetsFromSurface =
const AZStd::array<AZ::Vector3, RayCountQuad> 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<float, RayCount> RayDistances =
const AZStd::array<float, RayCountQuad> 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);
}

@ -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);

@ -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

@ -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

@ -8,6 +8,7 @@
set(FILES
Tests/AudioComponentTests.cpp
Tests/AxisAlignedBoxShapeTest.cpp
Tests/BoxShapeTest.cpp
Tests/BundlingSystemComponentTests.cpp
Tests/SphereShapeTest.cpp

Loading…
Cancel
Save