Files
o3de/Gems/PhysX/Code/Source/PhysXCharacters/Components/EditorCharacterControllerComponent.cpp
T
Esteban Papp 1f9b284de2 Merge branch 'development' into cmake/SPEC-7179
Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com>

# Conflicts:
#	Code/Editor/Plugins/ComponentEntityEditorPlugin/ComponentEntityEditorPlugin_precompiled.h
#	Code/Editor/Plugins/EditorCommon/EditorCommon_precompiled.h
#	Code/Editor/Plugins/EditorCommon/stdafx.cpp
#	Code/Editor/Plugins/FFMPEGPlugin/FFMPEGPlugin_precompiled.h
#	Code/Editor/Plugins/PerforcePlugin/PerforcePlugin_precompiled.h
#	Code/Editor/Plugins/ProjectSettingsTool/ProjectSettingsTool_precompiled.h
#	Code/Framework/AzToolsFramework/AzToolsFramework/AzToolsFramework_precompiled.h
#	Code/Tools/AssetProcessor/native/precompiled.h
#	Code/Tools/Standalone/StandaloneTools_precompiled.h
#	Gems/AssetMemoryAnalyzer/Code/Source/AssetMemoryAnalyzer_precompiled.h
#	Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageProcessing_precompiled.h
#	Gems/Atom/RHI/DX12/Code/Source/RHI/Atom_RHI_DX12_precompiled.h
#	Gems/Atom/RHI/Metal/Code/Include/Platform/Mac/Atom_RHI_Metal_precompiled_Platform.h
#	Gems/Atom/RHI/Metal/Code/Include/Platform/iOS/Atom_RHI_Metal_precompiled_Platform.h
#	Gems/Atom/RHI/Metal/Code/Source/Atom_RHI_Metal_precompiled.h
#	Gems/Atom/RHI/Metal/Code/atom_rhi_metal_common_files.cmake
#	Gems/Atom/RHI/Null/Code/Source/Atom_RHI_Null_precompiled.h
#	Gems/Atom/RHI/Null/Code/atom_rhi_null_common_files.cmake
#	Gems/Atom/RHI/Vulkan/Code/Include/Platform/Android/Atom_RHI_Vulkan_precompiled_Platform.h
#	Gems/Atom/RHI/Vulkan/Code/Include/Platform/Linux/Atom_RHI_Vulkan_precompiled_Platform.h
#	Gems/Atom/RHI/Vulkan/Code/Include/Platform/Mac/Atom_RHI_Vulkan_precompiled_Platform.h
#	Gems/Atom/RHI/Vulkan/Code/Include/Platform/Windows/Atom_RHI_Vulkan_precompiled_Platform.h
#	Gems/Atom/RHI/Vulkan/Code/Source/Atom_RHI_Vulkan_precompiled.h
#	Gems/Atom/RHI/Vulkan/Code/Source/RHI/SwapChain.cpp
#	Gems/Atom/RHI/Vulkan/Code/atom_rhi_vulkan_common_files.cmake
#	Gems/AtomLyIntegration/AtomFont/Code/Include/AtomLyIntegration/AtomFont/AtomFont_precompiled.h
#	Gems/Blast/Code/Source/StdAfx.cpp
#	Gems/Camera/Code/Source/Camera_precompiled.h
#	Gems/EMotionFX/Code/Source/EMotionFX_precompiled.h
#	Gems/FastNoise/Code/Source/FastNoise_precompiled.h
#	Gems/Gestures/Code/Source/Gestures_precompiled.h
#	Gems/GradientSignal/Code/Source/GradientSignal_precompiled.h
#	Gems/GraphCanvas/Code/precompiled.h
#	Gems/ImGui/Code/Source/ImGui_precompiled.h
#	Gems/InAppPurchases/Code/Source/InAppPurchases_precompiled.h
#	Gems/LmbrCentral/Code/Source/LmbrCentral_precompiled.h
#	Gems/LmbrCentral/Code/Tests/ShapeGeometryUtilTest.cpp
#	Gems/LyShine/Code/Editor/UiCanvasEditor_precompiled.h
#	Gems/LyShine/Code/Source/Animation/LyShine_precompiled.h
#	Gems/LyShine/Code/Source/LyShine_precompiled.h
#	Gems/LyShineExamples/Code/Source/LyShineExamples_precompiled.h
#	Gems/Maestro/Code/Source/Cinematics/Maestro_precompiled.h
#	Gems/Maestro/Code/Source/Maestro_precompiled.h
#	Gems/MessagePopup/Code/Source/MessagePopup_precompiled.h
#	Gems/Metastream/Code/Source/Metastream_precompiled.h
#	Gems/Microphone/Code/Source/Microphone_precompiled.h
#	Gems/Multiplayer/Code/Source/Multiplayer_precompiled.h
#	Gems/PhysX/Code/NumericalMethods/Source/NumericalMethods_precompiled.h
#	Gems/PhysX/Code/Source/PhysXUnsupported_precompiled.h
#	Gems/PhysX/Code/Source/PhysX_precompiled.h
#	Gems/PhysX/Code/physx_unsupported_files.cmake
#	Gems/PhysXDebug/Code/Source/PhysXDebugUnsupported_precompiled.h
#	Gems/PhysXDebug/Code/Source/PhysXDebug_precompiled.h
#	Gems/ScriptCanvas/Code/Editor/precompiled.h
#	Gems/ScriptCanvas/Code/Source/precompiled.h
#	Gems/ScriptCanvasDeveloper/Code/Source/precompiled.h
#	Gems/ScriptCanvasPhysics/Code/Source/ScriptCanvasPhysics_precompiled.h
#	Gems/ScriptEvents/Code/Source/precompiled.h
#	Gems/ScriptEvents/Code/Tests/Editor/EditorTests.cpp
#	Gems/ScriptedEntityTweener/Code/Source/ScriptedEntityTweener_precompiled.h
#	Gems/SliceFavorites/Code/Source/SliceFavorites_precompiled.h
#	Gems/StartingPointCamera/Code/Source/StartingPointCamera_precompiled.h
#	Gems/StartingPointInput/Code/Source/StartingPointInput_precompiled.h
#	Gems/StartingPointMovement/Code/Source/StartingPointMovement_precompiled.h
#	Gems/SurfaceData/Code/Source/SurfaceData_precompiled.h
#	Gems/TextureAtlas/Code/Source/TextureAtlas_precompiled.h
#	Gems/TickBusOrderViewer/Code/Source/TickBusOrderViewer_precompiled.h
#	Gems/Twitch/Code/Source/Twitch_precompiled.h
#	Gems/VirtualGamepad/Code/Source/VirtualGamepad_precompiled.h
#	Gems/WhiteBox/Code/Source/WhiteBoxUnsupported_precompiled.h
#	Gems/WhiteBox/Code/Source/WhiteBox_precompiled.h
2021-07-16 15:42:37 -07:00

295 lines
14 KiB
C++

/*
* 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/Serialization/EditContext.h>
#include <AzFramework/Viewport/ViewportColors.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
#include <PhysXCharacters/API/CharacterController.h>
#include <PhysXCharacters/Components/EditorCharacterControllerComponent.h>
#include <PhysXCharacters/Components/CharacterControllerComponent.h>
#include <LmbrCentral/Geometry/GeometrySystemComponentBus.h>
#include <System/PhysXSystem.h>
namespace PhysX
{
// this epsilon is deliberately chosen to be somewhat larger than AZ::Constants::FloatEpsilon so that it does not vanish
// when compared to the typical height of a character
static const float HeightEpsilon = 1e-5f;
void EditorCharacterControllerProxyShapeConfig::Reflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<EditorCharacterControllerProxyShapeConfig>()
->Version(1)
->Field("ShapeType", &EditorCharacterControllerProxyShapeConfig::m_shapeType)
->Field("Box", &EditorCharacterControllerProxyShapeConfig::m_box)
->Field("Capsule", &EditorCharacterControllerProxyShapeConfig::m_capsule)
;
if (auto editContext = serializeContext->GetEditContext())
{
editContext->Class<EditorCharacterControllerProxyShapeConfig>(
"EditorCharacterControllerProxyShapeConfig", "PhysX character controller shape")
->DataElement(AZ::Edit::UIHandlers::ComboBox, &EditorCharacterControllerProxyShapeConfig::m_shapeType, "Shape",
"The shape associated with the character controller")
->EnumAttribute(Physics::ShapeType::Capsule, "Capsule")
->EnumAttribute(Physics::ShapeType::Box, "Box")
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
->DataElement(AZ::Edit::UIHandlers::Default, &EditorCharacterControllerProxyShapeConfig::m_box, "Box",
"Configuration of box shape")
->Attribute(AZ::Edit::Attributes::Visibility, &EditorCharacterControllerProxyShapeConfig::IsBoxConfig)
->DataElement(AZ::Edit::UIHandlers::Default, &EditorCharacterControllerProxyShapeConfig::m_capsule, "Capsule",
"Configuration of capsule shape")
->Attribute(AZ::Edit::Attributes::Visibility, &EditorCharacterControllerProxyShapeConfig::IsCapsuleConfig)
;
}
}
}
bool EditorCharacterControllerProxyShapeConfig::IsBoxConfig() const
{
return m_shapeType == Physics::ShapeType::Box;
}
bool EditorCharacterControllerProxyShapeConfig::IsCapsuleConfig() const
{
return m_shapeType == Physics::ShapeType::Capsule;
}
const Physics::ShapeConfiguration& EditorCharacterControllerProxyShapeConfig::GetCurrent() const
{
switch (m_shapeType)
{
case Physics::ShapeType::Box:
return m_box;
case Physics::ShapeType::Capsule:
return m_capsule;
default:
AZ_Warning("EditorCharacterControllerProxyShapeConfig", false, "Unsupported shape type.");
return m_capsule;
}
}
void EditorCharacterControllerComponent::Reflect(AZ::ReflectContext* context)
{
EditorCharacterControllerProxyShapeConfig::Reflect(context);
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<EditorCharacterControllerComponent, EditorComponentBase>()
->Version(1)
->Field("Configuration", &EditorCharacterControllerComponent::m_configuration)
->Field("ShapeConfig", &EditorCharacterControllerComponent::m_proxyShapeConfiguration)
;
if (auto editContext = serializeContext->GetEditContext())
{
editContext->Class<EditorCharacterControllerComponent>(
"PhysX Character Controller", "PhysX Character Controller")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Category, "PhysX")
->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/PhysXCharacter.svg")
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/PhysXCharacter.svg")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
->DataElement(AZ::Edit::UIHandlers::Default, &EditorCharacterControllerComponent::m_configuration,
"Configuration", "Configuration for the character controller")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorCharacterControllerComponent::OnControllerConfigChanged)
->DataElement(AZ::Edit::UIHandlers::Default, &EditorCharacterControllerComponent::m_proxyShapeConfiguration,
"Shape Configuration", "The configuration for the shape associated with the character controller")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorCharacterControllerComponent::OnShapeConfigChanged)
;
}
}
}
EditorCharacterControllerComponent::EditorCharacterControllerComponent()
: m_physXConfigChangedHandler(
[]([[maybe_unused]]const AzPhysics::SystemConfiguration* config)
{
AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(&AzToolsFramework::PropertyEditorGUIMessages::RequestRefresh,
AzToolsFramework::PropertyModificationRefreshLevel::Refresh_AttributesAndValues);
})
{
}
void EditorCharacterControllerComponent::Activate()
{
AzToolsFramework::Components::EditorComponentBase::Activate();
AzFramework::EntityDebugDisplayEventBus::Handler::BusConnect(GetEntityId());
AzToolsFramework::EntitySelectionEvents::Bus::Handler::BusConnect(GetEntityId());
}
void EditorCharacterControllerComponent::Deactivate()
{
AzToolsFramework::EntitySelectionEvents::Bus::Handler::BusDisconnect();
AzFramework::EntityDebugDisplayEventBus::Handler::BusDisconnect();
AzToolsFramework::Components::EditorComponentBase::Deactivate();
}
// AzToolsFramework::EntitySelectionEvents
void EditorCharacterControllerComponent::OnSelected()
{
if (auto* physXSystem = GetPhysXSystem())
{
if (!m_physXConfigChangedHandler.IsConnected())
{
physXSystem->RegisterSystemConfigurationChangedEvent(m_physXConfigChangedHandler);
}
}
}
void EditorCharacterControllerComponent::OnDeselected()
{
m_physXConfigChangedHandler.Disconnect();
}
// AzFramework::EntityDebugDisplayEventBus
void EditorCharacterControllerComponent::DisplayEntityViewport(
[[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo,
AzFramework::DebugDisplayRequests& debugDisplay)
{
if (IsSelected())
{
const AZ::Vector3 upDirectionNormalized = m_configuration.m_upDirection.IsZero()
? AZ::Vector3::CreateAxisZ()
: m_configuration.m_upDirection.GetNormalized();
// PhysX uses the x-axis as the height direction of the controller, and so takes the shortest arc from the
// x-axis to the up direction. To obtain the same orientation in the LY co-ordinate system (which uses z
// as the height direction), we need to combine a rotation from the x-axis to the up direction with a
// rotation from the z-axis to the x-axis.
const AZ::Quaternion upDirectionQuat = AZ::Quaternion::CreateShortestArc(AZ::Vector3::CreateAxisX(),
upDirectionNormalized) * AZ::Quaternion::CreateRotationY(AZ::Constants::HalfPi);
if (m_proxyShapeConfiguration.IsCapsuleConfig())
{
const auto& capsuleConfig = static_cast<const Physics::CapsuleShapeConfiguration&>(m_proxyShapeConfiguration.GetCurrent());
const float heightOffset = 0.5f * capsuleConfig.m_height + m_configuration.m_contactOffset;
const AZ::Transform controllerTransform = AZ::Transform::CreateFromQuaternionAndTranslation(
upDirectionQuat, GetWorldTM().GetTranslation() + heightOffset * upDirectionNormalized);
debugDisplay.PushMatrix(controllerTransform);
// draw the actual shape
LmbrCentral::CapsuleGeometrySystemRequestBus::Broadcast(
&LmbrCentral::CapsuleGeometrySystemRequestBus::Events::GenerateCapsuleMesh,
m_configuration.m_scaleCoefficient * capsuleConfig.m_radius,
m_configuration.m_scaleCoefficient * capsuleConfig.m_height,
16, 8,
m_vertexBuffer,
m_indexBuffer,
m_lineBuffer
);
debugDisplay.SetLineWidth(2.0f);
debugDisplay.DrawTrianglesIndexed(m_vertexBuffer, m_indexBuffer, AzFramework::ViewportColors::SelectedColor);
debugDisplay.DrawLines(m_lineBuffer, AzFramework::ViewportColors::WireColor);
// draw the shape inflated by the contact offset
LmbrCentral::CapsuleGeometrySystemRequestBus::Broadcast(
&LmbrCentral::CapsuleGeometrySystemRequestBus::Events::GenerateCapsuleMesh,
m_configuration.m_scaleCoefficient * capsuleConfig.m_radius + m_configuration.m_contactOffset,
m_configuration.m_scaleCoefficient * capsuleConfig.m_height + 2.0f * m_configuration.m_contactOffset,
16, 8,
m_vertexBuffer,
m_indexBuffer,
m_lineBuffer
);
debugDisplay.DrawLines(m_lineBuffer, AzFramework::ViewportColors::WireColor);
debugDisplay.PopMatrix();
}
if (m_proxyShapeConfiguration.IsBoxConfig())
{
const auto& boxConfig = static_cast<const Physics::BoxShapeConfiguration&>(m_proxyShapeConfiguration.GetCurrent());
const float heightOffset = 0.5f * boxConfig.m_dimensions.GetZ() + m_configuration.m_contactOffset;
const AZ::Transform controllerTransform = AZ::Transform::CreateFromQuaternionAndTranslation(
upDirectionQuat, GetWorldTM().GetTranslation() + heightOffset * upDirectionNormalized);
const AZ::Vector3 boxHalfExtentsScaled = 0.5f * m_configuration.m_scaleCoefficient * boxConfig.m_dimensions;
const AZ::Vector3 boxHalfExtentsScaledWithContactOffset = boxHalfExtentsScaled +
m_configuration.m_contactOffset * AZ::Vector3::CreateOne();
debugDisplay.PushMatrix(controllerTransform);
// draw the actual shape
debugDisplay.SetLineWidth(2.0f);
debugDisplay.SetColor(AzFramework::ViewportColors::SelectedColor);
debugDisplay.DrawSolidBox(-boxHalfExtentsScaled, boxHalfExtentsScaled);
debugDisplay.SetColor(AzFramework::ViewportColors::WireColor);
debugDisplay.DrawWireBox(-boxHalfExtentsScaled, boxHalfExtentsScaled);
// draw the shape inflated by the contact offset
debugDisplay.DrawWireBox(-boxHalfExtentsScaledWithContactOffset, boxHalfExtentsScaledWithContactOffset);
debugDisplay.PopMatrix();
}
}
}
// EditorComponentBase
void EditorCharacterControllerComponent::BuildGameEntity(AZ::Entity* gameEntity)
{
switch (m_proxyShapeConfiguration.m_shapeType)
{
case Physics::ShapeType::Box:
gameEntity->CreateComponent<CharacterControllerComponent>(
AZStd::make_unique<CharacterControllerConfiguration>(m_configuration),
AZStd::make_unique<Physics::BoxShapeConfiguration>(m_proxyShapeConfiguration.m_box));
break;
case Physics::ShapeType::Capsule:
gameEntity->CreateComponent<CharacterControllerComponent>(
AZStd::make_unique<CharacterControllerConfiguration>(m_configuration),
AZStd::make_unique<Physics::CapsuleShapeConfiguration>(m_proxyShapeConfiguration.m_capsule));
break;
}
}
// editor change notifications
AZ::u32 EditorCharacterControllerComponent::OnControllerConfigChanged()
{
if (m_proxyShapeConfiguration.IsCapsuleConfig())
{
float capsuleHeight = m_proxyShapeConfiguration.m_capsule.m_height;
m_configuration.m_stepHeight = AZ::GetClamp(m_configuration.m_stepHeight, 0.0f, capsuleHeight - HeightEpsilon);
}
else if (m_proxyShapeConfiguration.IsBoxConfig())
{
float boxHeight = m_proxyShapeConfiguration.m_box.m_dimensions.GetZ();
m_configuration.m_stepHeight = AZ::GetClamp(m_configuration.m_stepHeight, 0.0f, boxHeight - HeightEpsilon);
}
return AZ::Edit::PropertyRefreshLevels::ValuesOnly;
}
AZ::u32 EditorCharacterControllerComponent::OnShapeConfigChanged()
{
float minHeight = m_configuration.m_stepHeight + HeightEpsilon;
if (m_proxyShapeConfiguration.IsCapsuleConfig())
{
m_proxyShapeConfiguration.m_capsule.m_height = AZ::GetMax(m_proxyShapeConfiguration.m_capsule.m_height, minHeight);
}
else if (m_proxyShapeConfiguration.IsBoxConfig())
{
float height = m_proxyShapeConfiguration.m_box.m_dimensions.GetZ();
m_proxyShapeConfiguration.m_box.m_dimensions.SetZ(AZ::GetMax(height, minHeight));
}
return AZ::Edit::PropertyRefreshLevels::ValuesOnly;
}
} // namespace PhysX