diff --git a/AutomatedTesting/Editor/Scripts/scene_mesh_to_prefab.py b/AutomatedTesting/Editor/Scripts/scene_mesh_to_prefab.py index d5a7acfe91..4aeff06f18 100644 --- a/AutomatedTesting/Editor/Scripts/scene_mesh_to_prefab.py +++ b/AutomatedTesting/Editor/Scripts/scene_mesh_to_prefab.py @@ -74,6 +74,7 @@ def update_manifest(scene): source_filename_only = os.path.basename(clean_filename) created_entities = [] + previous_entity_id = azlmbr.entity.InvalidEntityId # Loop every mesh node in the scene for activeMeshIndex in range(len(mesh_name_list)): @@ -102,14 +103,33 @@ def update_manifest(scene): # The MeshGroup we created will be output as a product in the asset's path named mesh_group_name.azmodel # The assetHint will be converted to an AssetId later during prefab loading json_update = json.dumps({ - "Controller": { "Configuration": { "ModelAsset": { - "assetHint": os.path.join(source_relative_path, mesh_group_name) + ".azmodel" }}} - }); + "Controller": { "Configuration": { "ModelAsset": { + "assetHint": os.path.join(source_relative_path, mesh_group_name) + ".azmodel" }}} + }); # Apply the JSON above to the component we created result = azlmbr.entity.EntityUtilityBus(azlmbr.bus.Broadcast, "UpdateComponentForEntity", entity_id, editor_mesh_component, json_update) if not result: - raise RuntimeError("UpdateComponentForEntity failed") + raise RuntimeError("UpdateComponentForEntity failed for Mesh component") + + # Get the transform component + transform_component = azlmbr.entity.EntityUtilityBus(azlmbr.bus.Broadcast, "GetOrAddComponentByTypeName", entity_id, "27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0") + + # Set this entity to be a child of the last entity we created + # This is just an example of how to do parenting and isn't necessarily useful to parent everything like this + if previous_entity_id is not None: + transform_json = json.dumps({ + "Parent Entity" : previous_entity_id.to_json() + }); + + # Apply the JSON update + result = azlmbr.entity.EntityUtilityBus(azlmbr.bus.Broadcast, "UpdateComponentForEntity", entity_id, transform_component, transform_json) + + if not result: + raise RuntimeError("UpdateComponentForEntity failed for Transform component") + + # Update the last entity id for next time + previous_entity_id = entity_id # Keep track of the entity we set up, we'll add them all to the prefab we're creating later created_entities.append(entity_id) @@ -147,6 +167,8 @@ def on_update_manifest(args): except RuntimeError as err: print (f'ERROR - {err}') log_exception_traceback() + except: + log_exception_traceback() global sceneJobHandler sceneJobHandler = None diff --git a/Code/Editor/EditorPreferencesDialog.cpp b/Code/Editor/EditorPreferencesDialog.cpp index c56df15908..a1859b0f14 100644 --- a/Code/Editor/EditorPreferencesDialog.cpp +++ b/Code/Editor/EditorPreferencesDialog.cpp @@ -264,6 +264,9 @@ void EditorPreferencesDialog::SetFilter(const QString& filter) else if (m_currentPageItem) { m_currentPageItem->UpdateEditorFilter(ui->propertyEditor, m_filter); + + // Refresh the Stylesheet - when using search functionality. + AzQtComponents::StyleManager::repolishStyleSheet(this); } } diff --git a/Code/Editor/EditorPreferencesPageFiles.cpp b/Code/Editor/EditorPreferencesPageFiles.cpp index 4be3269d42..c3423c4e5b 100644 --- a/Code/Editor/EditorPreferencesPageFiles.cpp +++ b/Code/Editor/EditorPreferencesPageFiles.cpp @@ -14,6 +14,7 @@ // Editor #include "Settings.h" +#include "EditorViewportSettings.h" @@ -43,17 +44,16 @@ void CEditorPreferencesPage_Files::Reflect(AZ::SerializeContext& serialize) ->Field("MaxCount", &AutoBackup::m_maxCount) ->Field("RemindTime", &AutoBackup::m_remindTime); - serialize.Class() + serialize.Class() ->Version(1) - ->Field("Max number of items displayed", &AssetBrowserSearch::m_maxNumberOfItemsShownInSearch); + ->Field("MaxEntriesShownCount", &AssetBrowserSettings::m_maxNumberOfItemsShownInSearch); serialize.Class() ->Version(1) ->Field("Files", &CEditorPreferencesPage_Files::m_files) ->Field("Editors", &CEditorPreferencesPage_Files::m_editors) ->Field("AutoBackup", &CEditorPreferencesPage_Files::m_autoBackup) - ->Field("AssetBrowserSearch", &CEditorPreferencesPage_Files::m_assetBrowserSearch); - + ->Field("AssetBrowserSettings", &CEditorPreferencesPage_Files::m_assetBrowserSettings); AZ::EditContext* editContext = serialize.GetEditContext(); if (editContext) @@ -85,9 +85,10 @@ void CEditorPreferencesPage_Files::Reflect(AZ::SerializeContext& serialize) ->Attribute(AZ::Edit::Attributes::Max, 100) ->DataElement(AZ::Edit::UIHandlers::SpinBox, &AutoBackup::m_remindTime, "Remind Time", "Auto Remind Every (Minutes)"); - editContext->Class("Asset Browser Search View", "Asset Browser Search View") - ->DataElement(AZ::Edit::UIHandlers::SpinBox, &AssetBrowserSearch::m_maxNumberOfItemsShownInSearch, "Maximum number of displayed items", - "Maximum number of displayed items displayed in the Search View") + editContext->Class("Asset Browser Settings", "Asset Browser Settings") + ->DataElement( + AZ::Edit::UIHandlers::SpinBox, &AssetBrowserSettings::m_maxNumberOfItemsShownInSearch, "Maximum number of displayed items", + "Maximum number of items to display in the Search View.") ->Attribute(AZ::Edit::Attributes::Min, 50) ->Attribute(AZ::Edit::Attributes::Max, 5000); @@ -97,7 +98,7 @@ void CEditorPreferencesPage_Files::Reflect(AZ::SerializeContext& serialize) ->DataElement(AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_Files::m_files, "Files", "File Preferences") ->DataElement(AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_Files::m_editors, "External Editors", "External Editors") ->DataElement(AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_Files::m_autoBackup, "Auto Backup", "Auto Backup") - ->DataElement(AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_Files::m_assetBrowserSearch, "Asset Browser Search", "Asset Browser Search"); + ->DataElement(AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_Files::m_assetBrowserSettings, "Asset Browser Settings","Asset Browser Settings"); } } @@ -117,6 +118,7 @@ QIcon& CEditorPreferencesPage_Files::GetIcon() void CEditorPreferencesPage_Files::OnApply() { using namespace AzToolsFramework::SliceUtilities; + auto sliceSettings = AZ::UserSettings::CreateFind(AZ_CRC("SliceUserSettings", 0x055b32eb), AZ::UserSettings::CT_LOCAL); sliceSettings->m_autoNumber = m_files.m_autoNumberSlices; sliceSettings->m_saveLocation = m_files.m_saveLocation; @@ -137,7 +139,7 @@ void CEditorPreferencesPage_Files::OnApply() gSettings.autoBackupMaxCount = m_autoBackup.m_maxCount; gSettings.autoRemindTime = m_autoBackup.m_remindTime; - gSettings.maxNumberOfItemsShownInSearch = m_assetBrowserSearch.m_maxNumberOfItemsShownInSearch; + SandboxEditor::SetMaxItemsShownInAssetBrowserSearch(m_assetBrowserSettings.m_maxNumberOfItemsShownInSearch); } void CEditorPreferencesPage_Files::InitializeSettings() @@ -163,5 +165,5 @@ void CEditorPreferencesPage_Files::InitializeSettings() m_autoBackup.m_maxCount = gSettings.autoBackupMaxCount; m_autoBackup.m_remindTime = gSettings.autoRemindTime; - m_assetBrowserSearch.m_maxNumberOfItemsShownInSearch = gSettings.maxNumberOfItemsShownInSearch; + m_assetBrowserSettings.m_maxNumberOfItemsShownInSearch = SandboxEditor::MaxItemsShownInAssetBrowserSearch(); } diff --git a/Code/Editor/EditorPreferencesPageFiles.h b/Code/Editor/EditorPreferencesPageFiles.h index 368cd91fc3..9022032edc 100644 --- a/Code/Editor/EditorPreferencesPageFiles.h +++ b/Code/Editor/EditorPreferencesPageFiles.h @@ -69,18 +69,15 @@ private: int m_remindTime; }; - struct AssetBrowserSearch + struct AssetBrowserSettings { - AZ_TYPE_INFO(AssetBrowserSearch, "{9FBFCD24-9452-49DF-99F4-2711443CEAAE}") - - int m_maxNumberOfItemsShownInSearch; + AZ_TYPE_INFO(AssetBrowserSettings, "{5F407EC4-BBD1-4A87-92DB-D938D7127BB0}") + AZ::u64 m_maxNumberOfItemsShownInSearch; }; Files m_files; ExternalEditors m_editors; AutoBackup m_autoBackup; - AssetBrowserSearch m_assetBrowserSearch; + AssetBrowserSettings m_assetBrowserSettings; QIcon m_icon; }; - - diff --git a/Code/Editor/EditorViewportSettings.cpp b/Code/Editor/EditorViewportSettings.cpp index 2354c6d63a..2b54622d1c 100644 --- a/Code/Editor/EditorViewportSettings.cpp +++ b/Code/Editor/EditorViewportSettings.cpp @@ -15,6 +15,7 @@ namespace SandboxEditor { + constexpr AZStd::string_view AssetBrowserMaxItemsShownInSearchSetting = "/Amazon/Preferences/Editor/AssetBrowser/MaxItemsShowInSearch"; constexpr AZStd::string_view GridSnappingSetting = "/Amazon/Preferences/Editor/GridSnapping"; constexpr AZStd::string_view GridSizeSetting = "/Amazon/Preferences/Editor/GridSize"; constexpr AZStd::string_view AngleSnappingSetting = "/Amazon/Preferences/Editor/AngleSnapping"; @@ -110,6 +111,16 @@ namespace SandboxEditor return AZStd::make_unique(); } + AZ::u64 MaxItemsShownInAssetBrowserSearch() + { + return GetRegistry(AssetBrowserMaxItemsShownInSearchSetting, aznumeric_cast(50)); + } + + void SetMaxItemsShownInAssetBrowserSearch(const AZ::u64 numberOfItemsShown) + { + SetRegistry(AssetBrowserMaxItemsShownInSearchSetting, numberOfItemsShown); + } + bool GridSnappingEnabled() { return GetRegistry(GridSnappingSetting, false); diff --git a/Code/Editor/EditorViewportSettings.h b/Code/Editor/EditorViewportSettings.h index c1394f7404..c6f51cf461 100644 --- a/Code/Editor/EditorViewportSettings.h +++ b/Code/Editor/EditorViewportSettings.h @@ -32,6 +32,9 @@ namespace SandboxEditor //! event will fire when a value in the settings registry (editorpreferences.setreg) is modified. SANDBOX_API AZStd::unique_ptr CreateEditorViewportSettingsCallbacks(); + SANDBOX_API AZ::u64 MaxItemsShownInAssetBrowserSearch(); + SANDBOX_API void SetMaxItemsShownInAssetBrowserSearch(AZ::u64 numberOfItemsShown); + SANDBOX_API bool GridSnappingEnabled(); SANDBOX_API void SetGridSnapping(bool enabled); diff --git a/Code/Editor/Objects/ObjectManager.cpp b/Code/Editor/Objects/ObjectManager.cpp index 7057cc5b7b..ee7e9a8e96 100644 --- a/Code/Editor/Objects/ObjectManager.cpp +++ b/Code/Editor/Objects/ObjectManager.cpp @@ -108,15 +108,11 @@ CObjectManager::CObjectManager() m_objectsByName.reserve(1024); LoadRegistry(); - - AzToolsFramework::ViewportEditorModeNotificationsBus::Handler::BusConnect(AzToolsFramework::GetEntityContextId()); } ////////////////////////////////////////////////////////////////////////// CObjectManager::~CObjectManager() { - AzToolsFramework::ViewportEditorModeNotificationsBus::Handler::BusDisconnect(); - m_bExiting = true; SaveRegistry(); DeleteAllObjects(); @@ -2307,37 +2303,6 @@ void CObjectManager::SelectObjectInRect(CBaseObject* pObj, CViewport* view, HitC } } -void CObjectManager::OnEditorModeActivated( - [[maybe_unused]] const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) -{ - if (mode == AzToolsFramework::ViewportEditorMode::Component) - { - // hide current gizmo for entity (translate/rotate/scale) - IGizmoManager* gizmoManager = GetGizmoManager(); - const size_t gizmoCount = static_cast(gizmoManager->GetGizmoCount()); - for (size_t i = 0; i < gizmoCount; ++i) - { - gizmoManager->RemoveGizmo(gizmoManager->GetGizmoByIndex(static_cast(i))); - } - } -} - -void CObjectManager::OnEditorModeDeactivated( - [[maybe_unused]] const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) -{ - if (mode == AzToolsFramework::ViewportEditorMode::Component) - { - // show translate/rotate/scale gizmo again - if (IGizmoManager* gizmoManager = GetGizmoManager()) - { - if (CBaseObject* selectedObject = GetIEditor()->GetSelectedObject()) - { - gizmoManager->AddGizmo(new CAxisGizmo(selectedObject)); - } - } - } -} - ////////////////////////////////////////////////////////////////////////// namespace { diff --git a/Code/Editor/Objects/ObjectManager.h b/Code/Editor/Objects/ObjectManager.h index 7fb2342e40..7389dfa6a1 100644 --- a/Code/Editor/Objects/ObjectManager.h +++ b/Code/Editor/Objects/ObjectManager.h @@ -20,7 +20,6 @@ #include "ObjectManagerEventBus.h" #include -#include #include #include #include @@ -59,7 +58,6 @@ public: */ class CObjectManager : public IObjectManager - , private AzToolsFramework::ViewportEditorModeNotificationsBus::Handler { public: //! Selection functor callback. @@ -330,12 +328,6 @@ private: void FindDisplayableObjects(DisplayContext& dc, bool bDisplay); - // ViewportEditorModeNotificationsBus overrides ... - void OnEditorModeActivated( - const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) override; - void OnEditorModeDeactivated( - const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) override; - private: typedef std::map Objects; Objects m_objects; diff --git a/Code/Editor/Settings.cpp b/Code/Editor/Settings.cpp index 47743d1c42..d548cffb52 100644 --- a/Code/Editor/Settings.cpp +++ b/Code/Editor/Settings.cpp @@ -10,6 +10,7 @@ #include "EditorDefs.h" #include "Settings.h" +#include "EditorViewportSettings.h" // Qt #include @@ -487,7 +488,6 @@ void SEditorSettings::Save() SaveValue("Settings", "AutoBackupTime", autoBackupTime); SaveValue("Settings", "AutoBackupMaxCount", autoBackupMaxCount); SaveValue("Settings", "AutoRemindTime", autoRemindTime); - SaveValue("Settings", "MaxDisplayedItemsNumInSearch", maxNumberOfItemsShownInSearch); SaveValue("Settings", "CameraMoveSpeed", cameraMoveSpeed); SaveValue("Settings", "CameraRotateSpeed", cameraRotateSpeed); SaveValue("Settings", "StylusMode", stylusMode); @@ -682,7 +682,6 @@ void SEditorSettings::Load() LoadValue("Settings", "AutoBackupTime", autoBackupTime); LoadValue("Settings", "AutoBackupMaxCount", autoBackupMaxCount); LoadValue("Settings", "AutoRemindTime", autoRemindTime); - LoadValue("Settings", "MaxDisplayedItemsNumInSearch", maxNumberOfItemsShownInSearch); LoadValue("Settings", "CameraMoveSpeed", cameraMoveSpeed); LoadValue("Settings", "CameraRotateSpeed", cameraRotateSpeed); LoadValue("Settings", "StylusMode", stylusMode); @@ -1174,7 +1173,7 @@ AzToolsFramework::ConsoleColorTheme SEditorSettings::GetConsoleColorTheme() cons return consoleBackgroundColorTheme; } -int SEditorSettings::GetMaxNumberOfItemsShownInSearchView() const +AZ::u64 SEditorSettings::GetMaxNumberOfItemsShownInSearchView() const { - return SEditorSettings::maxNumberOfItemsShownInSearch; + return SandboxEditor::MaxItemsShownInAssetBrowserSearch(); } diff --git a/Code/Editor/Settings.h b/Code/Editor/Settings.h index 8bf22b43e5..426d2300d3 100644 --- a/Code/Editor/Settings.h +++ b/Code/Editor/Settings.h @@ -279,7 +279,7 @@ AZ_POP_DISABLE_DLL_EXPORT_BASECLASS_WARNING SettingOutcome GetValue(const AZStd::string_view path) override; SettingOutcome SetValue(const AZStd::string_view path, const AZStd::any& value) override; AzToolsFramework::ConsoleColorTheme GetConsoleColorTheme() const override; - int GetMaxNumberOfItemsShownInSearchView() const override; + AZ::u64 GetMaxNumberOfItemsShownInSearchView() const override; void ConvertPath(const AZStd::string_view sourcePath, AZStd::string& category, AZStd::string& attribute); @@ -353,14 +353,6 @@ AZ_POP_DISABLE_DLL_EXPORT_BASECLASS_WARNING int autoRemindTime; ////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////// - // Asset Browser Search View. - ////////////////////////////////////////////////////////////////////////// - //! Current maximum number of items that can be displayed in the AssetBrowser Search View. - int maxNumberOfItemsShownInSearch; - ////////////////////////////////////////////////////////////////////////// - - //! If true preview windows is displayed when browsing geometries. bool bPreviewGeometryWindow; diff --git a/Code/Framework/AzCore/AzCore/Console/Console.cpp b/Code/Framework/AzCore/AzCore/Console/Console.cpp index 9f207d9afd..79e1f79697 100644 --- a/Code/Framework/AzCore/AzCore/Console/Console.cpp +++ b/Code/Framework/AzCore/AzCore/Console/Console.cpp @@ -225,8 +225,16 @@ namespace AZ ConsoleCommandContainer commandSubset; - for (ConsoleFunctorBase* curr = m_head; curr != nullptr; curr = curr->m_next) + for (const auto& functor : m_commands) { + if (functor.second.empty()) + { + continue; + } + + // Filter functors registered with the same name + const ConsoleFunctorBase* curr = functor.second.front(); + if ((curr->GetFlags() & ConsoleFunctorFlags::IsInvisible) == ConsoleFunctorFlags::IsInvisible) { // Filter functors marked as invisible @@ -236,7 +244,12 @@ namespace AZ if (StringFunc::StartsWith(curr->m_name, command, false)) { AZLOG_INFO("- %s : %s\n", curr->m_name, curr->m_desc); - commandSubset.push_back(curr->m_name); + + if (commandSubset.size() < MaxConsoleCommandPlusArgsLength) + { + commandSubset.push_back(curr->m_name); + } + if (matches) { matches->push_back(curr->m_name); @@ -271,7 +284,10 @@ namespace AZ { for (auto& curr : m_commands) { - visitor(curr.second.front()); + if (!curr.second.empty()) + { + visitor(curr.second.front()); + } } } @@ -336,6 +352,11 @@ namespace AZ { iter->second.erase(iter2); } + + if (iter->second.empty()) + { + m_commands.erase(iter); + } } functor->Unlink(m_head); functor->m_console = nullptr; diff --git a/Code/Framework/AzCore/Tests/Console/ConsoleTests.cpp b/Code/Framework/AzCore/Tests/Console/ConsoleTests.cpp index d91ec5ba58..e01129a7bc 100644 --- a/Code/Framework/AzCore/Tests/Console/ConsoleTests.cpp +++ b/Code/Framework/AzCore/Tests/Console/ConsoleTests.cpp @@ -288,6 +288,21 @@ namespace AZ AZStd::string completeCommand = console->AutoCompleteCommand("testVec3"); AZ_TEST_ASSERT(completeCommand == "testVec3"); } + + // Duplicate names + { + // Register two cvars with the same name + auto id = AZ::TypeId(); + auto flag = AZ::ConsoleFunctorFlags::Null; + auto signature = AZ::ConsoleFunctor::FunctorSignature(); + AZ::ConsoleFunctor cvarOne(*console, "testAutoCompleteDuplication", "", flag, id, signature); + AZ::ConsoleFunctor cvarTwo(*console, "testAutoCompleteDuplication", "", flag, id, signature); + + // Autocomplete given name expecting one match (not two) + AZStd::vector matches; + AZStd::string completeCommand = console->AutoCompleteCommand("testAutoCompleteD", &matches); + AZ_TEST_ASSERT(matches.size() == 1 && completeCommand == "testAutoCompleteDuplication"); + } } TEST_F(ConsoleTests, ConsoleFunctor_FreeFunctorExecutionTest) diff --git a/Code/Framework/AzFramework/AzFramework/Physics/RigidBody.h b/Code/Framework/AzFramework/AzFramework/Physics/RigidBody.h deleted file mode 100644 index 13d29b6bb9..0000000000 --- a/Code/Framework/AzFramework/AzFramework/Physics/RigidBody.h +++ /dev/null @@ -1,234 +0,0 @@ -/* - * 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 - -namespace -{ - class ReflectContext; -} - -namespace Physics -{ - class ShapeConfiguration; - class World; - class Shape; - - /// Default values used for initializing RigidBodySettings. - /// These can be modified by Physics Implementation gems. // O3DE_DEPRECATED(LY-114472) - DefaultRigidBodyConfiguration values are not shared across modules. - // Use RigidBodyConfiguration default values. - struct DefaultRigidBodyConfiguration - { - static float m_mass; - static bool m_computeInertiaTensor; - static float m_linearDamping; - static float m_angularDamping; - static float m_sleepMinEnergy; - static float m_maxAngularVelocity; - }; - - enum class MassComputeFlags : AZ::u8 - { - NONE = 0, - - //! Flags indicating whether a certain mass property should be auto-computed or not. - COMPUTE_MASS = 1, - COMPUTE_INERTIA = 1 << 1, - COMPUTE_COM = 1 << 2, - - //! If set, non-simulated shapes will also be included in the mass properties calculation. - INCLUDE_ALL_SHAPES = 1 << 3, - - DEFAULT = COMPUTE_COM | COMPUTE_INERTIA | COMPUTE_MASS - }; - - class RigidBodyConfiguration - : public WorldBodyConfiguration - { - public: - AZ_CLASS_ALLOCATOR(RigidBodyConfiguration, AZ::SystemAllocator, 0); - AZ_RTTI(RigidBodyConfiguration, "{ACFA8900-8530-4744-AF00-AA533C868A8E}", WorldBodyConfiguration); - static void Reflect(AZ::ReflectContext* context); - - enum PropertyVisibility : AZ::u16 - { - InitialVelocities = 1 << 0, ///< Whether the initial linear and angular velocities are visible. - InertiaProperties = 1 << 1, ///< Whether the whole category of inertia properties (mass, compute inertia, - ///< inertia tensor etc) is visible. - Damping = 1 << 2, ///< Whether linear and angular damping are visible. - SleepOptions = 1 << 3, ///< Whether the sleep threshold and start asleep options are visible. - Interpolation = 1 << 4, ///< Whether the interpolation option is visible. - Gravity = 1 << 5, ///< Whether the effected by gravity option is visible. - Kinematic = 1 << 6, ///< Whether the option to make the body kinematic is visible. - ContinuousCollisionDetection = 1 << 7, ///< Whether the option to enable continuous collision detection is visible. - MaxVelocities = 1 << 8 ///< Whether upper limits on velocities are visible. - }; - - RigidBodyConfiguration() = default; - RigidBodyConfiguration(const RigidBodyConfiguration& settings) = default; - - // Visibility functions. - AZ::Crc32 GetPropertyVisibility(PropertyVisibility property) const; - void SetPropertyVisibility(PropertyVisibility property, bool isVisible); - - AZ::Crc32 GetInitialVelocitiesVisibility() const; - /// Returns whether the whole category of inertia settings (mass, inertia, center of mass offset etc) is visible. - AZ::Crc32 GetInertiaSettingsVisibility() const; - /// Returns whether the individual inertia tensor field is visible or is hidden because the compute inertia option is selected. - AZ::Crc32 GetInertiaVisibility() const; - /// Returns whether the mass field is visible or is hidden because compute mass option is selected. - AZ::Crc32 GetMassVisibility() const; - /// Returns whether the individual centre of mass offset field is visible or is hidden because compute CoM option is selected. - AZ::Crc32 GetCoMVisibility() const; - AZ::Crc32 GetDampingVisibility() const; - AZ::Crc32 GetSleepOptionsVisibility() const; - AZ::Crc32 GetInterpolationVisibility() const; - AZ::Crc32 GetGravityVisibility() const; - AZ::Crc32 GetKinematicVisibility() const; - AZ::Crc32 GetCCDVisibility() const; - AZ::Crc32 GetMaxVelocitiesVisibility() const; - MassComputeFlags GetMassComputeFlags() const; - void SetMassComputeFlags(MassComputeFlags flags); - - bool IsCCDEnabled() const; - - // Basic initial settings. - AZ::Vector3 m_initialLinearVelocity = AZ::Vector3::CreateZero(); - AZ::Vector3 m_initialAngularVelocity = AZ::Vector3::CreateZero(); - AZ::Vector3 m_centerOfMassOffset = AZ::Vector3::CreateZero(); - - // Simulation parameters. - float m_mass = DefaultRigidBodyConfiguration::m_mass; - AZ::Matrix3x3 m_inertiaTensor = AZ::Matrix3x3::CreateIdentity(); - float m_linearDamping = DefaultRigidBodyConfiguration::m_linearDamping; - float m_angularDamping = DefaultRigidBodyConfiguration::m_angularDamping; - float m_sleepMinEnergy = DefaultRigidBodyConfiguration::m_sleepMinEnergy; - float m_maxAngularVelocity = DefaultRigidBodyConfiguration::m_maxAngularVelocity; - - // Visibility settings. - AZ::u16 m_propertyVisibilityFlags = (std::numeric_limits::max)(); - - bool m_startAsleep = false; - bool m_interpolateMotion = false; - bool m_gravityEnabled = true; - bool m_simulated = true; - bool m_kinematic = false; - bool m_ccdEnabled = false; ///< Whether continuous collision detection is enabled. - float m_ccdMinAdvanceCoefficient = 0.15f; ///< Coefficient affecting how granularly time is subdivided in CCD. - bool m_ccdFrictionEnabled = false; ///< Whether friction is applied when resolving CCD collisions. - - bool m_computeCenterOfMass = true; - bool m_computeInertiaTensor = true; - bool m_computeMass = true; - - //! If set, non-simulated shapes will also be included in the mass properties calculation. - bool m_includeAllShapesInMassCalculation = false; - }; - - /// Dynamic rigid body. - class RigidBody - : public WorldBody - { - public: - - AZ_CLASS_ALLOCATOR(RigidBody, AZ::SystemAllocator, 0); - AZ_RTTI(RigidBody, "{156E459F-7BB7-4B4E-ADA0-2130D96B7E80}", WorldBody); - - public: - RigidBody() = default; - explicit RigidBody(const RigidBodyConfiguration& settings); - - - virtual void AddShape(AZStd::shared_ptr shape) = 0; - virtual void RemoveShape(AZStd::shared_ptr shape) = 0; - virtual AZ::u32 GetShapeCount() { return 0; } - virtual AZStd::shared_ptr GetShape(AZ::u32 /*index*/) { return nullptr; } - - virtual AZ::Vector3 GetCenterOfMassWorld() const = 0; - virtual AZ::Vector3 GetCenterOfMassLocal() const = 0; - - virtual AZ::Matrix3x3 GetInverseInertiaWorld() const = 0; - virtual AZ::Matrix3x3 GetInverseInertiaLocal() const = 0; - - virtual float GetMass() const = 0; - virtual float GetInverseMass() const = 0; - virtual void SetMass(float mass) = 0; - virtual void SetCenterOfMassOffset(const AZ::Vector3& comOffset) = 0; - - /// Retrieves the velocity at center of mass; only linear velocity, no rotational velocity contribution. - virtual AZ::Vector3 GetLinearVelocity() const = 0; - virtual void SetLinearVelocity(const AZ::Vector3& velocity) = 0; - virtual AZ::Vector3 GetAngularVelocity() const = 0; - virtual void SetAngularVelocity(const AZ::Vector3& angularVelocity) = 0; - virtual AZ::Vector3 GetLinearVelocityAtWorldPoint(const AZ::Vector3& worldPoint) = 0; - virtual void ApplyLinearImpulse(const AZ::Vector3& impulse) = 0; - virtual void ApplyLinearImpulseAtWorldPoint(const AZ::Vector3& impulse, const AZ::Vector3& worldPoint) = 0; - virtual void ApplyAngularImpulse(const AZ::Vector3& angularImpulse) = 0; - - virtual float GetLinearDamping() const = 0; - virtual void SetLinearDamping(float damping) = 0; - virtual float GetAngularDamping() const = 0; - virtual void SetAngularDamping(float damping) = 0; - - virtual bool IsAwake() const = 0; - virtual void ForceAsleep() = 0; - virtual void ForceAwake() = 0; - virtual float GetSleepThreshold() const = 0; - virtual void SetSleepThreshold(float threshold) = 0; - - virtual bool IsKinematic() const = 0; - virtual void SetKinematic(bool kinematic) = 0; - virtual void SetKinematicTarget(const AZ::Transform& targetPosition) = 0; - - virtual bool IsGravityEnabled() const = 0; - virtual void SetGravityEnabled(bool enabled) = 0; - virtual void SetSimulationEnabled(bool enabled) = 0; - virtual void SetCCDEnabled(bool enabled) = 0; - - //! Recalculates mass, inertia and center of mass based on the flags passed. - //! @param flags MassComputeFlags specifying which properties should be recomputed. - //! @param centerOfMassOffsetOverride Optional override of the center of mass. Note: This parameter will be ignored if COMPUTE_COM is passed in flags. - //! @param inertiaTensorOverride Optional override of the inertia. Note: This parameter will be ignored if COMPUTE_INERTIA is passed in flags. - //! @param massOverride Optional override of the mass. Note: This parameter will be ignored if COMPUTE_MASS is passed in flags. - virtual void UpdateMassProperties(MassComputeFlags flags = MassComputeFlags::DEFAULT, - const AZ::Vector3* centerOfMassOffsetOverride = nullptr, - const AZ::Matrix3x3* inertiaTensorOverride = nullptr, - const float* massOverride = nullptr) = 0; - }; - - /// Bitwise operators for MassComputeFlags - inline MassComputeFlags operator|(MassComputeFlags lhs, MassComputeFlags rhs) - { - return aznumeric_cast(aznumeric_cast(lhs) | aznumeric_cast(rhs)); - } - - inline MassComputeFlags operator&(MassComputeFlags lhs, MassComputeFlags rhs) - { - return aznumeric_cast(aznumeric_cast(lhs) & aznumeric_cast(rhs)); - } - - /// Static rigid body. - class RigidBodyStatic - : public WorldBody - { - public: - AZ_CLASS_ALLOCATOR(RigidBodyStatic, AZ::SystemAllocator, 0); - AZ_RTTI(RigidBodyStatic, "{13A677BB-7085-4EDB-BCC8-306548238692}", WorldBody); - - virtual void AddShape(const AZStd::shared_ptr& shape) = 0; - virtual AZ::u32 GetShapeCount() { return 0; } - virtual AZStd::shared_ptr GetShape(AZ::u32 /*index*/) { return nullptr; } - }; -} // namespace Physics diff --git a/Code/Framework/AzFramework/AzFramework/Physics/SimulatedBodies/RigidBody.h b/Code/Framework/AzFramework/AzFramework/Physics/SimulatedBodies/RigidBody.h index 3c0a1afa1d..e9bf8a7307 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/SimulatedBodies/RigidBody.h +++ b/Code/Framework/AzFramework/AzFramework/Physics/SimulatedBodies/RigidBody.h @@ -89,9 +89,9 @@ namespace AzPhysics //! @param inertiaTensorOverride Optional override of the inertia. Note: This parameter will be ignored if COMPUTE_INERTIA is passed in flags. //! @param massOverride Optional override of the mass. Note: This parameter will be ignored if COMPUTE_MASS is passed in flags. virtual void UpdateMassProperties(MassComputeFlags flags = MassComputeFlags::DEFAULT, - const AZ::Vector3* centerOfMassOffsetOverride = nullptr, - const AZ::Matrix3x3* inertiaTensorOverride = nullptr, - const float* massOverride = nullptr) = 0; + const AZ::Vector3& centerOfMassOffsetOverride = AZ::Vector3::CreateZero(), + const AZ::Matrix3x3& inertiaTensorOverride = AZ::Matrix3x3::CreateIdentity(), + const float massOverride = 1.0f) = 0; }; } // namespace AzPhysics diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.h index 4fb4191d45..966b9f8478 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.h @@ -43,8 +43,7 @@ namespace AzToolsFramework }; //! Provides a bus to notify when the different editor modes are entered/exit. - class ViewportEditorModeNotifications - : public AZ::EBusTraits + class ViewportEditorModeNotifications : public AZ::EBusTraits { public: ////////////////////////////////////////////////////////////////////////// @@ -58,14 +57,17 @@ namespace AzToolsFramework static void Reflect(AZ::ReflectContext* context); //! Notifies subscribers of the a given viewport to the activation of the specified editor mode. - virtual void OnEditorModeActivated([[maybe_unused]] const ViewportEditorModesInterface& editorModeState, [[maybe_unused]] ViewportEditorMode mode) + virtual void OnEditorModeActivated( + [[maybe_unused]] const ViewportEditorModesInterface& editorModeState, [[maybe_unused]] ViewportEditorMode mode) { } //! Notifies subscribers of the a given viewport to the deactivation of the specified editor mode. - virtual void OnEditorModeDeactivated([[maybe_unused]] const ViewportEditorModesInterface& editorModeState, [[maybe_unused]] ViewportEditorMode mode) + virtual void OnEditorModeDeactivated( + [[maybe_unused]] const ViewportEditorModesInterface& editorModeState, [[maybe_unused]] ViewportEditorMode mode) { } }; + using ViewportEditorModeNotificationsBus = AZ::EBus; } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h index 55dcbb1532..f84e6bd81c 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h @@ -53,7 +53,7 @@ namespace AzToolsFramework private slots: void SourceDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight); private: - int m_numberOfItemsDisplayed = 50; + AZ::u64 m_numberOfItemsDisplayed = 0; int m_displayedItemsCounter = 0; QPointer m_filterModel; QMap m_indexMap; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentMode/ComponentModeCollection.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentMode/ComponentModeCollection.cpp index 1768cb5920..07e025fc60 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentMode/ComponentModeCollection.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentMode/ComponentModeCollection.cpp @@ -137,6 +137,7 @@ namespace AzToolsFramework if (componentTypeIt == m_activeComponentTypes.end()) { m_activeComponentTypes.push_back(componentType); + m_viewportUiHandlers.emplace_back(componentType); } // see if we already have a ComponentModeBuilder for the specific component on this entity @@ -225,6 +226,7 @@ namespace AzToolsFramework if (!m_entitiesAndComponentModes.empty()) { RefreshActions(); + PopulateViewportUi(); } // if entering ComponentMode not as an undo/redo step (an action was @@ -285,6 +287,10 @@ namespace AzToolsFramework componentModeCommand.release(); } + // remove the component mode viewport border + ViewportUi::ViewportUiRequestBus::Event( + ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::RemoveViewportBorder); + // notify listeners the editor has left ComponentMode - listeners may // wish to modify state to indicate this (e.g. appearance, functionality etc.) m_viewportEditorModeTracker->DeactivateMode({ GetEntityContextId() }, ViewportEditorMode::Component); @@ -301,6 +307,7 @@ namespace AzToolsFramework } m_entitiesAndComponentModeBuilders.clear(); m_activeComponentTypes.clear(); + m_viewportUiHandlers.clear(); m_componentMode = false; m_selectedComponentModeIndex = 0; @@ -385,6 +392,24 @@ namespace AzToolsFramework return m_activeComponentTypes.size() > 1; } + static ComponentModeViewportUi* FindViewportUiHandlerForType( + AZStd::vector& viewportUiHandlers, const AZ::Uuid& componentType) + { + auto handler = AZStd::find_if( + viewportUiHandlers.begin(), viewportUiHandlers.end(), + [componentType](const ComponentModeViewportUi& handler) + { + return handler.GetComponentType() == componentType; + }); + + if (handler == viewportUiHandlers.end()) + { + return nullptr; + } + + return handler; + } + bool ComponentModeCollection::ActiveComponentModeChanged(const AZ::Uuid& previousComponentType) { if (m_activeComponentTypes[m_selectedComponentModeIndex] != previousComponentType) @@ -410,6 +435,20 @@ namespace AzToolsFramework // replace the current component mode by invoking the builder // for the new 'active' component mode componentMode.m_componentMode = componentModeBuilder->m_componentModeBuilder(); + + // populate the viewport UI with the new component mode + PopulateViewportUi(); + + // set the appropriate viewportUiHandler to active + if (auto viewportUiHandler = + FindViewportUiHandlerForType(m_viewportUiHandlers, m_activeComponentTypes[m_selectedComponentModeIndex])) + { + viewportUiHandler->SetComponentModeViewportUiActive(true); + } + + ViewportUi::ViewportUiRequestBus::Event( + ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateViewportBorder, + componentMode.m_componentMode->GetComponentModeName().c_str()); } RefreshActions(); @@ -519,5 +558,18 @@ namespace AzToolsFramework } } + void ComponentModeCollection::PopulateViewportUi() + { + // update viewport UI for new component type + if (m_selectedComponentModeIndex < m_activeComponentTypes.size()) + { + // iterate over all entities and their active Component Mode, populate viewport UI for the new mode + for (auto& entityAndComponentMode : m_entitiesAndComponentModes) + { + // build viewport UI based on current state + entityAndComponentMode.m_componentMode->PopulateViewportUi(); + } + } + } } // namespace ComponentModeFramework } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentMode/EditorBaseComponentMode.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentMode/EditorBaseComponentMode.cpp index 9e043a5c86..f449478306 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentMode/EditorBaseComponentMode.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentMode/EditorBaseComponentMode.cpp @@ -55,7 +55,7 @@ namespace AzToolsFramework GetEntityComponentIdPair(), elementIdsToDisplay); // create the component mode border with the specific name for this component mode ViewportUi::ViewportUiRequestBus::Event( - ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateComponentModeBorder, + ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateViewportBorder, GetComponentModeName()); // set the EntityComponentId for this ComponentMode to active in the ComponentModeViewportUi system ComponentModeViewportUiRequestBus::Event( diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Editor/EditorSettingsAPIBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Editor/EditorSettingsAPIBus.h index fedb889fbb..52d8395377 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Editor/EditorSettingsAPIBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Editor/EditorSettingsAPIBus.h @@ -38,7 +38,7 @@ namespace AzToolsFramework virtual SettingOutcome GetValue(const AZStd::string_view path) = 0; virtual SettingOutcome SetValue(const AZStd::string_view path, const AZStd::any& value) = 0; virtual ConsoleColorTheme GetConsoleColorTheme() const = 0; - virtual int GetMaxNumberOfItemsShownInSearchView() const = 0; + virtual AZ::u64 GetMaxNumberOfItemsShownInSearchView() const = 0; }; using EditorSettingsAPIBus = AZ::EBus; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp index f592c471d0..44ff603c0c 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp @@ -71,12 +71,7 @@ namespace AzToolsFramework return; } - AZ::EntityId previousFocusEntityId = m_focusRoot; - m_focusRoot = entityId; - FocusModeNotificationBus::Broadcast(&FocusModeNotifications::OnEditorFocusChanged, previousFocusEntityId, m_focusRoot); - - if (auto tracker = AZ::Interface::Get(); - tracker != nullptr) + if (auto tracker = AZ::Interface::Get()) { if (!m_focusRoot.IsValid() && entityId.IsValid()) { @@ -87,6 +82,10 @@ namespace AzToolsFramework tracker->DeactivateMode({ GetEntityContextId() }, ViewportEditorMode::Focus); } } + + AZ::EntityId previousFocusEntityId = m_focusRoot; + m_focusRoot = entityId; + FocusModeNotificationBus::Broadcast(&FocusModeNotifications::OnEditorFocusChanged, previousFocusEntityId, m_focusRoot); } void FocusModeSystemComponent::ClearFocusRoot([[maybe_unused]] AzFramework::EntityContextId entityContextId) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp index 081655a166..ee34b628bb 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp @@ -1052,10 +1052,10 @@ namespace AzToolsFramework DuplicateNestedEntitiesInInstance(commonOwningInstance->get(), entities, instanceDomAfter, duplicatedEntityAndInstanceIds, duplicateEntityAliasMap); - PrefabUndoInstance* command = aznew PrefabUndoInstance("Entity/Instance duplication"); + PrefabUndoInstance* command = aznew PrefabUndoInstance("Entity/Instance duplication", false); command->SetParent(undoBatch.GetUndoBatch()); command->Capture(instanceDomBefore, instanceDomAfter, commonOwningInstance->get().GetTemplateId()); - command->RedoBatched(); + command->Redo(); DuplicateNestedInstancesInInstance(commonOwningInstance->get(), instances, instanceDomAfter, duplicatedEntityAndInstanceIds, newInstanceAliasToOldInstanceMap); @@ -1323,7 +1323,7 @@ namespace AzToolsFramework Prefab::PrefabDom instanceDomAfter; m_instanceToTemplateInterface->GenerateDomForInstance(instanceDomAfter, parentInstance); - PrefabUndoInstance* command = aznew PrefabUndoInstance("Instance detachment"); + PrefabUndoInstance* command = aznew PrefabUndoInstance("Instance detachment", false); command->Capture(instanceDomBefore, instanceDomAfter, parentTemplateId); command->SetParent(undoBatch.GetUndoBatch()); { diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemScriptingHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemScriptingHandler.cpp index d34e2cfc92..884c463bff 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemScriptingHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemScriptingHandler.cpp @@ -6,11 +6,14 @@ * */ +#include #include #include #include #include #include +#include +#include namespace AzToolsFramework::Prefab { @@ -61,9 +64,29 @@ namespace AzToolsFramework::Prefab entities.push_back(entity); } } - - auto prefab = m_prefabSystemComponentInterface->CreatePrefab(entities, {}, AZ::IO::PathView(AZStd::string_view(filePath))); + bool result = false; + [[maybe_unused]] AZ::EntityId commonRoot; + EntityList topLevelEntities; + AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(result, &AzToolsFramework::ToolsApplicationRequestBus::Events::FindCommonRootInactive, + entities, commonRoot, &topLevelEntities); + + auto containerEntity = AZStd::make_unique(); + + for (AZ::Entity* entity : topLevelEntities) + { + AzToolsFramework::Components::TransformComponent* transformComponent = + entity->FindComponent(); + + if (transformComponent) + { + transformComponent->SetParent(containerEntity->GetId()); + } + } + + auto prefab = m_prefabSystemComponentInterface->CreatePrefab( + entities, {}, AZ::IO::PathView(AZStd::string_view(filePath)), AZStd::move(containerEntity)); + if (!prefab) { AZ_Error("PrefabSystemComponenent", false, "Failed to create prefab %s", filePath.c_str()); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.cpp index b298304e3b..6c96209d56 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.cpp @@ -17,17 +17,16 @@ namespace AzToolsFramework { PrefabUndoBase::PrefabUndoBase(const AZStd::string& undoOperationName) : UndoSystem::URSequencePoint(undoOperationName) - , m_changed(true) - , m_templateId(InvalidTemplateId) { m_instanceToTemplateInterface = AZ::Interface::Get(); AZ_Assert(m_instanceToTemplateInterface, "Failed to grab instance to template interface"); } //PrefabInstanceUndo - PrefabUndoInstance::PrefabUndoInstance(const AZStd::string& undoOperationName) + PrefabUndoInstance::PrefabUndoInstance(const AZStd::string& undoOperationName, const bool useImmediatePropagation) : PrefabUndoBase(undoOperationName) { + m_useImmediatePropagation = useImmediatePropagation; } void PrefabUndoInstance::Capture( @@ -43,17 +42,12 @@ namespace AzToolsFramework void PrefabUndoInstance::Undo() { - m_instanceToTemplateInterface->PatchTemplate(m_undoPatch, m_templateId, true); + m_instanceToTemplateInterface->PatchTemplate(m_undoPatch, m_templateId, m_useImmediatePropagation); } void PrefabUndoInstance::Redo() { - m_instanceToTemplateInterface->PatchTemplate(m_redoPatch, m_templateId, true); - } - - void PrefabUndoInstance::RedoBatched() - { - m_instanceToTemplateInterface->PatchTemplate(m_redoPatch, m_templateId); + m_instanceToTemplateInterface->PatchTemplate(m_redoPatch, m_templateId, m_useImmediatePropagation); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.h index 8669024df7..0946a36951 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.h @@ -29,14 +29,15 @@ namespace AzToolsFramework bool Changed() const override { return m_changed; } protected: - TemplateId m_templateId; + TemplateId m_templateId = InvalidTemplateId; PrefabDom m_redoPatch; PrefabDom m_undoPatch; InstanceToTemplateInterface* m_instanceToTemplateInterface = nullptr; - bool m_changed; + bool m_changed = true; + bool m_useImmediatePropagation = true; }; //! handles the addition and removal of entities from instances @@ -44,7 +45,7 @@ namespace AzToolsFramework : public PrefabUndoBase { public: - explicit PrefabUndoInstance(const AZStd::string& undoOperationName); + explicit PrefabUndoInstance(const AZStd::string& undoOperationName, const bool useImmediatePropagation = true); void Capture( const PrefabDom& initialState, @@ -53,7 +54,6 @@ namespace AzToolsFramework void Undo() override; void Redo() override; - void RedoBatched(); }; //! handles entity updates, such as when the values on an entity change diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndoHelpers.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndoHelpers.cpp index 9803b55324..31a0c60bcb 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndoHelpers.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndoHelpers.cpp @@ -23,10 +23,10 @@ namespace AzToolsFramework PrefabDom instanceDomAfterUpdate; PrefabDomUtils::StoreInstanceInPrefabDom(instance, instanceDomAfterUpdate); - PrefabUndoInstance* state = aznew Prefab::PrefabUndoInstance(undoMessage); + PrefabUndoInstance* state = aznew Prefab::PrefabUndoInstance(undoMessage, false); state->Capture(instanceDomBeforeUpdate, instanceDomAfterUpdate, instance.GetTemplateId()); state->SetParent(undoBatch); - state->RedoBatched(); + state->Redo(); } LinkId CreateLink( diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp index bb718d0dc9..6399a64635 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.cpp @@ -9,6 +9,7 @@ #include "EditorHelpers.h" #include +#include #include #include #include @@ -123,6 +124,11 @@ namespace AzToolsFramework "EditorHelpers - " "Focus Mode Interface could not be found. " "Check that it is being correctly initialized."); + + AZStd::vector> invalidClicks; + invalidClicks.push_back(AZStd::make_unique("Not in focus")); + invalidClicks.push_back(AZStd::make_unique()); + m_invalidClicks = AZStd::make_unique(AZStd::move(invalidClicks)); } AZ::EntityId EditorHelpers::HandleMouseInteraction( @@ -186,13 +192,20 @@ namespace AzToolsFramework } } - // Verify if the entity Id corresponds to an entity that is focused; if not, halt selection. - if (!IsSelectableAccordingToFocusMode(entityIdUnderCursor)) + // verify if the entity Id corresponds to an entity that is focused; if not, halt selection. + if (entityIdUnderCursor.IsValid() && !IsSelectableAccordingToFocusMode(entityIdUnderCursor)) { + if (mouseInteraction.m_mouseInteraction.m_mouseButtons.Left() && + mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Down || + mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::DoubleClick) + { + m_invalidClicks->AddInvalidClick(mouseInteraction.m_mouseInteraction.m_mousePick.m_screenCoordinates); + } + return AZ::EntityId(); } - // Container Entity support - if the entity that is being selected is part of a closed container, + // container entity support - if the entity that is being selected is part of a closed container, // change the selection to the container instead. if (ContainerEntityInterface* containerEntityInterface = AZ::Interface::Get()) { @@ -202,6 +215,12 @@ namespace AzToolsFramework return entityIdUnderCursor; } + void EditorHelpers::Display2d( + [[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) + { + m_invalidClicks->Display2d(viewportInfo, debugDisplay); + } + void EditorHelpers::DisplayHelpers( const AzFramework::ViewportInfo& viewportInfo, const AzFramework::CameraState& cameraState, @@ -263,19 +282,19 @@ namespace AzToolsFramework } } - bool EditorHelpers::IsSelectableInViewport(AZ::EntityId entityId) + bool EditorHelpers::IsSelectableInViewport(const AZ::EntityId entityId) const { return IsSelectableAccordingToFocusMode(entityId) && IsSelectableAccordingToContainerEntities(entityId); } - bool EditorHelpers::IsSelectableAccordingToFocusMode(AZ::EntityId entityId) + bool EditorHelpers::IsSelectableAccordingToFocusMode(const AZ::EntityId entityId) const { return m_focusModeInterface->IsInFocusSubTree(entityId); } - bool EditorHelpers::IsSelectableAccordingToContainerEntities(AZ::EntityId entityId) + bool EditorHelpers::IsSelectableAccordingToContainerEntities(const AZ::EntityId entityId) const { - if (ContainerEntityInterface* containerEntityInterface = AZ::Interface::Get()) + if (const auto* containerEntityInterface = AZ::Interface::Get()) { return !containerEntityInterface->IsUnderClosedContainerEntity(entityId); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.h index 909a231635..6623221bdc 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorHelpers.h @@ -11,6 +11,9 @@ #include #include #include +#include +#include +#include namespace AzFramework { @@ -58,20 +61,27 @@ namespace AzToolsFramework AzFramework::DebugDisplayRequests& debugDisplay, const AZStd::function& showIconCheck); + //! Handle 2d drawing for EditorHelper functionality. + void Display2d( + const AzFramework::ViewportInfo& viewportInfo, + AzFramework::DebugDisplayRequests& debugDisplay); + //! Returns whether the entityId can be selected in the viewport according //! to the current Editor Focus Mode and Container Entity setup. - bool IsSelectableInViewport(AZ::EntityId entityId); + bool IsSelectableInViewport(AZ::EntityId entityId) const; private: //! Returns whether the entityId can be selected in the viewport according //! to the current Editor Focus Mode setup. - bool IsSelectableAccordingToFocusMode(AZ::EntityId entityId); + bool IsSelectableAccordingToFocusMode(AZ::EntityId entityId) const; //! Returns whether the entityId can be selected in the viewport according - //! to the current Container Entityu setup. - bool IsSelectableAccordingToContainerEntities(AZ::EntityId entityId); + //! to the current Container Entity setup. + bool IsSelectableAccordingToContainerEntities(AZ::EntityId entityId) const; + + AZStd::unique_ptr m_invalidClicks; //!< Display for invalid click behavior. const EditorVisibleEntityDataCache* m_entityDataCache = nullptr; //!< Entity Data queried by the EditorHelpers. - const FocusModeInterface* m_focusModeInterface = nullptr; + const FocusModeInterface* m_focusModeInterface = nullptr; //!< API to interact with focus mode functionality. }; } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp index d5a0595049..c73742da4d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp @@ -3560,6 +3560,8 @@ namespace AzToolsFramework DrawAxisGizmo(viewportInfo, debugDisplay); m_boxSelect.Display2d(viewportInfo, debugDisplay); + + m_editorHelpers->Display2d(viewportInfo, debugDisplay); } void EditorTransformComponentSelection::RefreshSelectedEntityIds() @@ -3663,26 +3665,63 @@ namespace AzToolsFramework void EditorTransformComponentSelection::OnEditorModeActivated( [[maybe_unused]] const ViewportEditorModesInterface& editorModeState, ViewportEditorMode mode) { - if (mode == ViewportEditorMode::Component) + switch (mode) { - SetAllViewportUiVisible(false); + case ViewportEditorMode::Component: + { + SetAllViewportUiVisible(false); - EditorEntityLockComponentNotificationBus::Router::BusRouterDisconnect(); - EditorEntityVisibilityNotificationBus::Router::BusRouterDisconnect(); - ToolsApplicationNotificationBus::Handler::BusDisconnect(); + EditorEntityLockComponentNotificationBus::Router::BusRouterDisconnect(); + EditorEntityVisibilityNotificationBus::Router::BusRouterDisconnect(); + ToolsApplicationNotificationBus::Handler::BusDisconnect(); + } + break; + case ViewportEditorMode::Focus: + { + ViewportUi::ViewportUiRequestBus::Event( + ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateViewportBorder, "Focus Mode"); + } + break; + case ViewportEditorMode::Default: + case ViewportEditorMode::Pick: + // noop + break; } } void EditorTransformComponentSelection::OnEditorModeDeactivated( - [[maybe_unused]] const ViewportEditorModesInterface& editorModeState, ViewportEditorMode mode) + const ViewportEditorModesInterface& editorModeState, const ViewportEditorMode mode) { - if (mode == ViewportEditorMode::Component) + switch (mode) { - SetAllViewportUiVisible(true); + case ViewportEditorMode::Component: + { + SetAllViewportUiVisible(true); - ToolsApplicationNotificationBus::Handler::BusConnect(); - EditorEntityVisibilityNotificationBus::Router::BusRouterConnect(); - EditorEntityLockComponentNotificationBus::Router::BusRouterConnect(); + ToolsApplicationNotificationBus::Handler::BusConnect(); + EditorEntityVisibilityNotificationBus::Router::BusRouterConnect(); + EditorEntityLockComponentNotificationBus::Router::BusRouterConnect(); + + // note: when leaving component mode, we check if we're still in focus mode (i.e. component mode was + // started from within focus mode), if we are, ensure we create/update the viewport border (as leaving + // component mode will attempt to remove it) + if (editorModeState.IsModeActive(ViewportEditorMode::Focus)) + { + ViewportUi::ViewportUiRequestBus::Event( + ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateViewportBorder, "Focus Mode"); + } + } + break; + case ViewportEditorMode::Focus: + { + ViewportUi::ViewportUiRequestBus::Event( + ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::RemoveViewportBorder); + } + break; + case ViewportEditorMode::Default: + case ViewportEditorMode::Pick: + // noop + break; } } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/InvalidClicks.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/InvalidClicks.cpp new file mode 100644 index 0000000000..dfff4da08f --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/InvalidClicks.cpp @@ -0,0 +1,142 @@ +/* + * 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 + +AZ_CVAR(float, ed_invalidClickRadius, 10.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "Maximum invalid click radius to expand to"); +AZ_CVAR(float, ed_invalidClickDuration, 1.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "Duration to display the invalid click feedback"); +AZ_CVAR(float, ed_invalidClickMessageSize, 0.8f, nullptr, AZ::ConsoleFunctorFlags::Null, "Size of text for invalid message"); +AZ_CVAR( + float, + ed_invalidClickMessageVerticalOffset, + 30.0f, + nullptr, + AZ::ConsoleFunctorFlags::Null, + "Vertical offset from cursor of invalid click message"); + +namespace AzToolsFramework +{ + void ExpandingFadingCircles::Begin(const AzFramework::ScreenPoint& screenPoint) + { + FadingCircle fadingCircle; + fadingCircle.m_position = screenPoint; + fadingCircle.m_opacity = 1.0f; + fadingCircle.m_radius = 0.0f; + m_fadingCircles.push_back(fadingCircle); + } + + void ExpandingFadingCircles::Update(const float deltaTime) + { + for (auto& fadingCircle : m_fadingCircles) + { + fadingCircle.m_opacity = AZStd::max(fadingCircle.m_opacity - (deltaTime / ed_invalidClickDuration), 0.0f); + fadingCircle.m_radius += deltaTime * ed_invalidClickRadius; + } + + m_fadingCircles.erase( + AZStd::remove_if( + m_fadingCircles.begin(), m_fadingCircles.end(), + [](const FadingCircle& fadingCircle) + { + return fadingCircle.m_opacity <= 0.0f; + }), + m_fadingCircles.end()); + } + + bool ExpandingFadingCircles::Updating() + { + return !m_fadingCircles.empty(); + } + + void ExpandingFadingCircles::Display(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) + { + const AZ::Vector2 viewportSize = AzToolsFramework::GetCameraState(viewportInfo.m_viewportId).m_viewportSize; + + for (const auto& fadingCircle : m_fadingCircles) + { + const auto position = AzFramework::Vector2FromScreenPoint(fadingCircle.m_position) / viewportSize; + debugDisplay.SetColor(AZ::Color(1.0f, 1.0f, 1.0f, fadingCircle.m_opacity)); + debugDisplay.DrawWireCircle2d(position, fadingCircle.m_radius * 0.005f, 0.0f); + } + } + + void FadingText::Begin(const AzFramework::ScreenPoint& screenPoint) + { + m_opacity = 1.0f; + m_invalidClickPosition = screenPoint; + } + + void FadingText::Update(const float deltaTime) + { + m_opacity -= deltaTime / ed_invalidClickDuration; + } + + bool FadingText::Updating() + { + return m_opacity >= 0.0f; + } + + void FadingText::Display( + [[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) + { + if (constexpr float MinOpacity = 0.05f; m_opacity >= MinOpacity) + { + debugDisplay.SetColor(AZ::Color(1.0f, 1.0f, 1.0f, m_opacity)); + debugDisplay.Draw2dTextLabel( + aznumeric_cast(m_invalidClickPosition.m_x), + aznumeric_cast(m_invalidClickPosition.m_y) - ed_invalidClickMessageVerticalOffset, ed_invalidClickMessageSize, + m_message.c_str(), true); + } + } + + void InvalidClicks::AddInvalidClick(const AzFramework::ScreenPoint& screenPoint) + { + AZ::TickBus::Handler::BusConnect(); + + for (auto& invalidClickBehavior : m_invalidClickBehaviors) + { + invalidClickBehavior->Begin(screenPoint); + } + } + + void InvalidClicks::OnTick(const float deltaTime, [[maybe_unused]] const AZ::ScriptTimePoint time) + { + for (auto& invalidClickBehavior : m_invalidClickBehaviors) + { + invalidClickBehavior->Update(deltaTime); + } + + const auto updating = AZStd::any_of( + m_invalidClickBehaviors.begin(), m_invalidClickBehaviors.end(), + [](const auto& invalidClickBehavior) + { + return invalidClickBehavior->Updating(); + }); + + if (!updating && AZ::TickBus::Handler::BusIsConnected()) + { + AZ::TickBus::Handler::BusDisconnect(); + } + } + + void InvalidClicks::Display2d(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) + { + debugDisplay.DepthTestOff(); + + for (const auto& invalidClickBehavior : m_invalidClickBehaviors) + { + invalidClickBehavior->Display(viewportInfo, debugDisplay); + } + + debugDisplay.DepthTestOn(); + } +} // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/InvalidClicks.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/InvalidClicks.h new file mode 100644 index 0000000000..55a6d614ea --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/InvalidClicks.h @@ -0,0 +1,108 @@ +/* + * 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 + +namespace AzFramework +{ + class DebugDisplayRequests; + struct ViewportInfo; +} // namespace AzFramework + +namespace AzToolsFramework +{ + namespace ViewportInteraction + { + struct MouseInteractionEvent; + } + + //! An interface to provide invalid click feedback in the editor viewport. + class InvalidClick + { + public: + virtual ~InvalidClick() = default; + + //! Begin the feedback. + //! @param screenPoint The position of the click in screen coordinates. + virtual void Begin(const AzFramework::ScreenPoint& screenPoint) = 0; + //! Update the invalid click feedback + virtual void Update(float deltaTime) = 0; + //! Report if the click feedback is running or not (returning false will signal the TickBus can be disconnected from). + virtual bool Updating() = 0; + //! Display the click feedback in the viewport. + virtual void Display(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) = 0; + }; + + //! Display expanding fading circles for every click of the mouse that is invalid. + class ExpandingFadingCircles : public InvalidClick + { + public: + void Begin(const AzFramework::ScreenPoint& screenPoint) override; + void Update(float deltaTime) override; + bool Updating() override; + void Display(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) override; + + private: + //! Stores a circle representation with a lifetime to grow and fade out over time. + struct FadingCircle + { + AzFramework::ScreenPoint m_position; + float m_radius; + float m_opacity; + }; + + using FadingCircles = AZStd::vector; + FadingCircles m_fadingCircles; //!< Collection of fading circles to draw for clicks that have no effect. + }; + + //! Display fading text where an invalid click happened. + //! @note There is only one fading text, each click will update its position. + class FadingText : public InvalidClick + { + public: + explicit FadingText(AZStd::string message) + : m_message(AZStd::move(message)) + { + } + + void Begin(const AzFramework::ScreenPoint& screenPoint) override; + void Update(float deltaTime) override; + bool Updating() override; + void Display(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) override; + + private: + AZStd::string m_message; //!< Message to display for fading text. + float m_opacity = 1.0f; //!< The opacity of the invalid click message. + AzFramework::ScreenPoint m_invalidClickPosition; //!< The position to display the invalid click message. + }; + + //! Interface to begin invalid click feedback (will run all added InvalidClick behaviors). + class InvalidClicks : private AZ::TickBus::Handler + { + public: + explicit InvalidClicks(AZStd::vector> invalidClickBehaviors) + : m_invalidClickBehaviors(AZStd::move(invalidClickBehaviors)) + { + } + + //! Add an invalid click and activate one or more of the added invalid click behaviors. + void AddInvalidClick(const AzFramework::ScreenPoint& screenPoint); + + //! Handle 2d drawing for EditorHelper functionality. + void Display2d(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay); + + private: + //! AZ::TickBus overrides ... + void OnTick(float deltaTime, AZ::ScriptTimePoint time) override; + + AZStd::vector> m_invalidClickBehaviors; //!< Invalid click behaviors to run. + }; +} // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp index e27627eab6..a289d914d6 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp @@ -290,9 +290,9 @@ namespace AzToolsFramework::ViewportUi::Internal return false; } - void ViewportUiDisplay::CreateComponentModeBorder(const AZStd::string& borderTitle) + void ViewportUiDisplay::CreateViewportBorder(const AZStd::string& borderTitle) { - AZStd::string styleSheet = AZStd::string::format( + const AZStd::string styleSheet = AZStd::string::format( "border: %dpx solid %s; border-top: %dpx solid %s;", HighlightBorderSize, HighlightBorderColor, TopHighlightBorderSize, HighlightBorderColor); m_uiOverlay.setStyleSheet(styleSheet.c_str()); @@ -303,7 +303,7 @@ namespace AzToolsFramework::ViewportUi::Internal m_componentModeBorderText.setText(borderTitle.c_str()); } - void ViewportUiDisplay::RemoveComponentModeBorder() + void ViewportUiDisplay::RemoveViewportBorder() { m_componentModeBorderText.setVisible(false); m_uiOverlay.setStyleSheet("border: none;"); @@ -420,6 +420,7 @@ namespace AzToolsFramework::ViewportUi::Internal m_uiMainWindow.setVisible(true); m_uiOverlay.setVisible(true); } + m_uiMainWindow.setMask(region); } @@ -437,6 +438,7 @@ namespace AzToolsFramework::ViewportUi::Internal { return element->second; } + return ViewportUiElementInfo{ nullptr, InvalidViewportUiElementId, false }; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.h index aafaf61ff3..5020241815 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.h @@ -89,8 +89,8 @@ namespace AzToolsFramework::ViewportUi::Internal AZStd::shared_ptr GetViewportUiElement(ViewportUiElementId elementId); bool IsViewportUiElementVisible(ViewportUiElementId elementId); - void CreateComponentModeBorder(const AZStd::string& borderTitle); - void RemoveComponentModeBorder(); + void CreateViewportBorder(const AZStd::string& borderTitle); + void RemoveViewportBorder(); private: void PrepareWidgetForViewportUi(QPointer widget); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiManager.cpp index 255d11f561..1f14b12b7d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiManager.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiManager.cpp @@ -240,14 +240,14 @@ namespace AzToolsFramework::ViewportUi } } - void ViewportUiManager::CreateComponentModeBorder(const AZStd::string& borderTitle) + void ViewportUiManager::CreateViewportBorder(const AZStd::string& borderTitle) { - m_viewportUi->CreateComponentModeBorder(borderTitle); + m_viewportUi->CreateViewportBorder(borderTitle); } - void ViewportUiManager::RemoveComponentModeBorder() + void ViewportUiManager::RemoveViewportBorder() { - m_viewportUi->RemoveComponentModeBorder(); + m_viewportUi->RemoveViewportBorder(); } void ViewportUiManager::PressButton(ClusterId clusterId, ButtonId buttonId) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiManager.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiManager.h index 9ec6648451..ce7e5aafe9 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiManager.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiManager.h @@ -50,8 +50,8 @@ namespace AzToolsFramework::ViewportUi void RegisterTextFieldCallback(TextFieldId textFieldId, AZ::Event::Handler& handler) override; void RemoveTextField(TextFieldId textFieldId) override; void SetTextFieldVisible(TextFieldId textFieldId, bool visible) override; - void CreateComponentModeBorder(const AZStd::string& borderTitle) override; - void RemoveComponentModeBorder() override; + void CreateViewportBorder(const AZStd::string& borderTitle) override; + void RemoveViewportBorder() override; void PressButton(ClusterId clusterId, ButtonId buttonId) override; void PressButton(SwitcherId switcherId, ButtonId buttonId) override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiRequestBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiRequestBus.h index 108d23950a..3c6f7094cb 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiRequestBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiRequestBus.h @@ -78,7 +78,7 @@ namespace AzToolsFramework::ViewportUi virtual void RegisterSwitcherEventHandler(SwitcherId switcherId, AZ::Event::Handler& handler) = 0; //! Removes a cluster from the Viewport UI system. virtual void RemoveCluster(ClusterId clusterId) = 0; - //! + //! Removes a switcher from the Viewport UI system. virtual void RemoveSwitcher(SwitcherId switcherId) = 0; //! Sets the visibility of the cluster. virtual void SetClusterVisible(ClusterId clusterId, bool visible) = 0; @@ -96,12 +96,12 @@ namespace AzToolsFramework::ViewportUi //! Sets the visibility of the text field. virtual void SetTextFieldVisible(TextFieldId textFieldId, bool visible) = 0; //! Create the highlight border for Component Mode. - virtual void CreateComponentModeBorder(const AZStd::string& borderTitle) = 0; + virtual void CreateViewportBorder(const AZStd::string& borderTitle) = 0; //! Remove the highlight border for Component Mode. - virtual void RemoveComponentModeBorder() = 0; - //! Invoke a button press in a cluster. + virtual void RemoveViewportBorder() = 0; + //! Invoke a button press on a cluster. virtual void PressButton(ClusterId clusterId, ButtonId buttonId) = 0; - //! + //! Invoke a button press on a switcher. virtual void PressButton(SwitcherId switcherId, ButtonId buttonId) = 0; }; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake index 8dec7ab611..5db65f89f4 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake @@ -553,6 +553,8 @@ set(FILES ViewportSelection/EditorTransformComponentSelectionRequestBus.cpp ViewportSelection/EditorVisibleEntityDataCache.h ViewportSelection/EditorVisibleEntityDataCache.cpp + ViewportSelection/InvalidClicks.h + ViewportSelection/InvalidClicks.cpp ViewportSelection/ViewportEditorModeTracker.cpp ViewportSelection/ViewportEditorModeTracker.h ToolsFileUtils/ToolsFileUtils.h diff --git a/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.cpp b/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.cpp index b45fbe05f6..39d8933863 100644 --- a/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.cpp +++ b/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.cpp @@ -1353,8 +1353,9 @@ namespace AZ::AtomBridge // if 2d draw need to project pos to screen first AzFramework::TextDrawParameters params; AZ::RPI::ViewportContextPtr viewportContext = GetViewportContext(); + const auto dpiScaleFactor = viewportContext->GetDpiScalingFactor(); params.m_drawViewportId = viewportContext->GetId(); // get the viewport ID so default viewport works - params.m_position = AZ::Vector3(x, y, 1.0f); + params.m_position = AZ::Vector3(x * dpiScaleFactor, y * dpiScaleFactor, 1.0f); params.m_color = m_rendState.m_color; params.m_scale = AZ::Vector2(size); params.m_hAlign = center ? AzFramework::TextHorizontalAlignment::Center : AzFramework::TextHorizontalAlignment::Left; //! Horizontal text alignment diff --git a/Gems/Blast/Code/Tests/Mocks/BlastMocks.h b/Gems/Blast/Code/Tests/Mocks/BlastMocks.h index dddd1e275a..1fe6e35d75 100644 --- a/Gems/Blast/Code/Tests/Mocks/BlastMocks.h +++ b/Gems/Blast/Code/Tests/Mocks/BlastMocks.h @@ -344,9 +344,9 @@ namespace Blast void UpdateMassProperties( [[maybe_unused]] AzPhysics::MassComputeFlags flags, - [[maybe_unused]] const AZ::Vector3* centerOfMassOffsetOverride, - [[maybe_unused]] const AZ::Matrix3x3* inertiaTensorOverride, - [[maybe_unused]] const float* massOverride) override + [[maybe_unused]] const AZ::Vector3& centerOfMassOffsetOverride, + [[maybe_unused]] const AZ::Matrix3x3& inertiaTensorOverride, + [[maybe_unused]] const float massOverride) override { } diff --git a/Gems/Blast/Editor/Scripts/blast_asset_builder.py b/Gems/Blast/Editor/Scripts/blast_asset_builder.py index 06dc15f5c1..a570489ed1 100644 --- a/Gems/Blast/Editor/Scripts/blast_asset_builder.py +++ b/Gems/Blast/Editor/Scripts/blast_asset_builder.py @@ -95,7 +95,9 @@ def generate_assetinfo_product(request): outputFilename = os.path.join(request.tempDirPath, assetinfoFilename) # the only rule in it is to run this file again as a scene processor - currentScript = pathlib.Path(__file__).resolve() + currentScript = str(pathlib.Path(__file__).resolve()) + currentScript = currentScript.replace('\\', '/').lower() + currentScript = currentScript.replace('blast_asset_builder.py', 'blast_chunk_processor.py') aDict = {"values": [{"$type": "ScriptProcessorRule", "scriptFilename": f"{currentScript}"}]} jsonString = json.dumps(aDict) jsonFile = open(outputFilename, "w") @@ -167,124 +169,3 @@ try: pythonAssetBuilderHandler = register_asset_builder() except: pythonAssetBuilderHandler = None - -# -# SceneAPI Processor -# -blastChunksAssetType = azlmbr.math.Uuid_CreateString('{993F0B0F-37D9-48C6-9CC2-E27D3F3E343E}', 0) - -def export_chunk_asset(scene, outputDirectory, platformIdentifier, productList): - import azlmbr.scene - import azlmbr.object - import azlmbr.paths - import json, os - - jsonFilename = os.path.basename(scene.sourceFilename) - jsonFilename = os.path.join(outputDirectory, jsonFilename + '.blast_chunks') - - # prepare output folder - basePath, _ = os.path.split(jsonFilename) - outputPath = os.path.join(outputDirectory, basePath) - if not os.path.exists(outputPath): - os.makedirs(outputPath, False) - - # write out a JSON file with the chunk file info - with open(jsonFilename, "w") as jsonFile: - jsonFile.write(scene.manifest.ExportToJson()) - - exportProduct = azlmbr.scene.ExportProduct() - exportProduct.filename = jsonFilename - exportProduct.sourceId = scene.sourceGuid - exportProduct.assetType = blastChunksAssetType - exportProduct.subId = 101 - - exportProductList = azlmbr.scene.ExportProductList() - exportProductList.AddProduct(exportProduct) - return exportProductList - -def on_prepare_for_export(args): - try: - scene = args[0] # azlmbr.scene.Scene - outputDirectory = args[1] # string - platformIdentifier = args[2] # string - productList = args[3] # azlmbr.scene.ExportProductList - return export_chunk_asset(scene, outputDirectory, platformIdentifier, productList) - except: - log_exception_traceback() - -def get_mesh_node_names(sceneGraph): - import azlmbr.scene as sceneApi - import azlmbr.scene.graph - from scene_api import scene_data as sceneData - - meshDataList = [] - node = sceneGraph.get_root() - children = [] - - while node.IsValid(): - # store children to process after siblings - if sceneGraph.has_node_child(node): - children.append(sceneGraph.get_node_child(node)) - - # store any node that has mesh data content - nodeContent = sceneGraph.get_node_content(node) - if nodeContent is not None and nodeContent.CastWithTypeName('MeshData'): - if sceneGraph.is_node_end_point(node) is False: - nodeName = sceneData.SceneGraphName(sceneGraph.get_node_name(node)) - nodePath = nodeName.get_path() - if (len(nodeName.get_path())): - meshDataList.append(sceneData.SceneGraphName(sceneGraph.get_node_name(node))) - - # advance to next node - if sceneGraph.has_node_sibling(node): - node = sceneGraph.get_node_sibling(node) - elif children: - node = children.pop() - else: - node = azlmbr.scene.graph.NodeIndex() - - return meshDataList - -def update_manifest(scene): - import uuid, os - import azlmbr.scene as sceneApi - import azlmbr.scene.graph - from scene_api import scene_data as sceneData - - graph = sceneData.SceneGraph(scene.graph) - meshNameList = get_mesh_node_names(graph) - sceneManifest = sceneData.SceneManifest() - sourceFilenameOnly = os.path.basename(scene.sourceFilename) - sourceFilenameOnly = sourceFilenameOnly.replace('.','_') - - for activeMeshIndex in range(len(meshNameList)): - chunkName = meshNameList[activeMeshIndex] - chunkPath = chunkName.get_path() - meshGroupName = '{}_{}'.format(sourceFilenameOnly, chunkName.get_name()) - meshGroup = sceneManifest.add_mesh_group(meshGroupName) - meshGroup['id'] = '{' + str(uuid.uuid5(uuid.NAMESPACE_DNS, sourceFilenameOnly + chunkPath)) + '}' - sceneManifest.mesh_group_select_node(meshGroup, chunkPath) - - return sceneManifest.export() - -sceneJobHandler = None - -def on_update_manifest(args): - try: - scene = args[0] - return update_manifest(scene) - except: - global sceneJobHandler - sceneJobHandler = None - log_exception_traceback() - -# try to create SceneAPI handler for processing -try: - import azlmbr.scene as sceneApi - if (sceneJobHandler == None): - sceneJobHandler = sceneApi.ScriptBuildingNotificationBusHandler() - sceneJobHandler.connect() - sceneJobHandler.add_callback('OnUpdateManifest', on_update_manifest) - sceneJobHandler.add_callback('OnPrepareForExport', on_prepare_for_export) -except: - sceneJobHandler = None diff --git a/Gems/Blast/Editor/Scripts/blast_chunk_processor.py b/Gems/Blast/Editor/Scripts/blast_chunk_processor.py new file mode 100644 index 0000000000..d112f465e9 --- /dev/null +++ b/Gems/Blast/Editor/Scripts/blast_chunk_processor.py @@ -0,0 +1,141 @@ +""" +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 +""" + +""" +This a Python Asset Builder script examines each .blast file to see if an +associated .fbx file needs to be processed by exporting all of its chunks +into a scene manifest + +This is also a SceneAPI script that executes from a foo.fbx.assetinfo scene +manifest that writes out asset chunk data for .blast files +""" +import os, traceback, binascii, sys, json, pathlib +import azlmbr.math +import azlmbr.asset +import azlmbr.asset.entity +import azlmbr.asset.builder +import azlmbr.bus + +# +# SceneAPI Processor +# +blastChunksAssetType = azlmbr.math.Uuid_CreateString('{993F0B0F-37D9-48C6-9CC2-E27D3F3E343E}', 0) + +def export_chunk_asset(scene, outputDirectory, platformIdentifier, productList): + import azlmbr.scene + import azlmbr.object + import azlmbr.paths + import json, os + + jsonFilename = os.path.basename(scene.sourceFilename) + jsonFilename = os.path.join(outputDirectory, jsonFilename + '.blast_chunks') + + # prepare output folder + basePath, _ = os.path.split(jsonFilename) + outputPath = os.path.join(outputDirectory, basePath) + if not os.path.exists(outputPath): + os.makedirs(outputPath, False) + + # write out a JSON file with the chunk file info + with open(jsonFilename, "w") as jsonFile: + jsonFile.write(scene.manifest.ExportToJson()) + + exportProduct = azlmbr.scene.ExportProduct() + exportProduct.filename = jsonFilename + exportProduct.sourceId = scene.sourceGuid + exportProduct.assetType = blastChunksAssetType + exportProduct.subId = 101 + + exportProductList = azlmbr.scene.ExportProductList() + exportProductList.AddProduct(exportProduct) + return exportProductList + +def on_prepare_for_export(args): + try: + scene = args[0] # azlmbr.scene.Scene + outputDirectory = args[1] # string + platformIdentifier = args[2] # string + productList = args[3] # azlmbr.scene.ExportProductList + return export_chunk_asset(scene, outputDirectory, platformIdentifier, productList) + except: + log_exception_traceback() + +def get_mesh_node_names(sceneGraph): + import azlmbr.scene as sceneApi + import azlmbr.scene.graph + from scene_api import scene_data as sceneData + + meshDataList = [] + node = sceneGraph.get_root() + children = [] + + while node.IsValid(): + # store children to process after siblings + if sceneGraph.has_node_child(node): + children.append(sceneGraph.get_node_child(node)) + + # store any node that has mesh data content + nodeContent = sceneGraph.get_node_content(node) + if nodeContent is not None and nodeContent.CastWithTypeName('MeshData'): + if sceneGraph.is_node_end_point(node) is False: + nodeName = sceneData.SceneGraphName(sceneGraph.get_node_name(node)) + nodePath = nodeName.get_path() + if (len(nodeName.get_path())): + meshDataList.append(sceneData.SceneGraphName(sceneGraph.get_node_name(node))) + + # advance to next node + if sceneGraph.has_node_sibling(node): + node = sceneGraph.get_node_sibling(node) + elif children: + node = children.pop() + else: + node = azlmbr.scene.graph.NodeIndex() + + return meshDataList + +def update_manifest(scene): + import uuid, os + import azlmbr.scene as sceneApi + import azlmbr.scene.graph + from scene_api import scene_data as sceneData + + graph = sceneData.SceneGraph(scene.graph) + meshNameList = get_mesh_node_names(graph) + sceneManifest = sceneData.SceneManifest() + sourceFilenameOnly = os.path.basename(scene.sourceFilename) + sourceFilenameOnly = sourceFilenameOnly.replace('.','_') + + for activeMeshIndex in range(len(meshNameList)): + chunkName = meshNameList[activeMeshIndex] + chunkPath = chunkName.get_path() + meshGroupName = '{}_{}'.format(sourceFilenameOnly, chunkName.get_name()) + meshGroup = sceneManifest.add_mesh_group(meshGroupName) + meshGroup['id'] = '{' + str(uuid.uuid5(uuid.NAMESPACE_DNS, sourceFilenameOnly + chunkPath)) + '}' + sceneManifest.mesh_group_select_node(meshGroup, chunkPath) + + return sceneManifest.export() + +sceneJobHandler = None + +def on_update_manifest(args): + try: + scene = args[0] + return update_manifest(scene) + except: + global sceneJobHandler + sceneJobHandler = None + log_exception_traceback() + +# try to create SceneAPI handler for processing +try: + import azlmbr.scene as sceneApi + if (sceneJobHandler == None): + sceneJobHandler = sceneApi.ScriptBuildingNotificationBusHandler() + sceneJobHandler.connect() + sceneJobHandler.add_callback('OnUpdateManifest', on_update_manifest) + sceneJobHandler.add_callback('OnPrepareForExport', on_prepare_for_export) +except: + sceneJobHandler = None diff --git a/Gems/EditorPythonBindings/Code/Source/PythonProxyObject.cpp b/Gems/EditorPythonBindings/Code/Source/PythonProxyObject.cpp index 706ca48156..df246d230d 100644 --- a/Gems/EditorPythonBindings/Code/Source/PythonProxyObject.cpp +++ b/Gems/EditorPythonBindings/Code/Source/PythonProxyObject.cpp @@ -17,10 +17,16 @@ #include #include +#include +#include #include +#include #include #include +#include +#include +#include namespace EditorPythonBindings { @@ -571,6 +577,37 @@ namespace EditorPythonBindings return false; } + pybind11::object PythonProxyObject::ToJson() + { + rapidjson::Document document; + AZ::JsonSerializerSettings settings; + settings.m_keepDefaults = true; + + auto resultCode = + AZ::JsonSerialization::Store(document, document.GetAllocator(), m_wrappedObject.m_address, nullptr, m_wrappedObject.m_typeId, settings); + + if (resultCode.GetProcessing() == AZ::JsonSerializationResult::Processing::Halted) + { + AZ_Error("PythonProxyObject", false, "Failed to serialize to json"); + return pybind11::cast(Py_None); + } + + AZStd::string jsonString; + AZ::Outcome outcome = AZ::JsonSerializationUtils::WriteJsonString(document, jsonString); + + if (!outcome.IsSuccess()) + { + AZ_Error("PythonProxyObject", false, "Failed to write json string: %s", outcome.GetError().c_str()); + return pybind11::cast(Py_None); + } + + jsonString.erase(AZStd::remove(jsonString.begin(), jsonString.end(), '\n'), jsonString.end()); + auto pythonCode = AZStd::string::format( + R"PYTHON(exec("import json") or json.loads("""%s"""))PYTHON", jsonString.c_str()); + + return pybind11::eval(pythonCode.c_str()); + } + bool PythonProxyObject::DoComparisonEvaluation(pybind11::object pythonOther, Comparison comparison) { bool invertLogic = false; @@ -912,6 +949,7 @@ namespace EditorPythonBindings .def("set_property", &PythonProxyObject::SetPropertyValue) .def("get_property", &PythonProxyObject::GetPropertyValue) .def("invoke", &PythonProxyObject::Invoke) + .def("to_json", &PythonProxyObject::ToJson) .def(Operator::s_isEqual, [](PythonProxyObject& self, pybind11::object rhs) { return self.DoEqualityEvaluation(rhs); diff --git a/Gems/EditorPythonBindings/Code/Source/PythonProxyObject.h b/Gems/EditorPythonBindings/Code/Source/PythonProxyObject.h index 5b611c32c0..a9d2fad5a0 100644 --- a/Gems/EditorPythonBindings/Code/Source/PythonProxyObject.h +++ b/Gems/EditorPythonBindings/Code/Source/PythonProxyObject.h @@ -58,6 +58,8 @@ namespace EditorPythonBindings //! Performs an equality operation to compare this object with another object bool DoEqualityEvaluation(pybind11::object pythonOther); + pybind11::object ToJson(); + //! Perform a comparison of a Python operator enum class Comparison { diff --git a/Gems/PhysX/Code/Editor/DebugDraw.cpp b/Gems/PhysX/Code/Editor/DebugDraw.cpp index 440b198675..5936312ecf 100644 --- a/Gems/PhysX/Code/Editor/DebugDraw.cpp +++ b/Gems/PhysX/Code/Editor/DebugDraw.cpp @@ -148,16 +148,16 @@ namespace PhysX using VisibilityFunc = bool(*)(); editContext->Class( - "PhysX Collider Debug Draw", "Manages global and per-collider debug draw settings and logic") + "PhysX Collider Debug Draw", "Global and per-collider debug draw preferences.") ->DataElement(AZ::Edit::UIHandlers::CheckBox, &Collider::m_locallyEnabled, "Draw collider", - "Shows the geometry for the collider in the viewport") + "Display collider geometry in the viewport.") ->Attribute(AZ::Edit::Attributes::CheckboxTooltip, "If set, the geometry of this collider is visible in the viewport. 'Draw Helpers' needs to be enabled to use.") ->Attribute(AZ::Edit::Attributes::Visibility, VisibilityFunc{ []() { return IsGlobalColliderDebugCheck(GlobalCollisionDebugState::Manual); } }) ->Attribute(AZ::Edit::Attributes::ReadOnly, &IsDrawColliderReadOnly) ->DataElement(AZ::Edit::UIHandlers::Button, &Collider::m_globalButtonState, "Draw collider", - "Shows the geometry for the collider in the viewport") + "Display collider geometry in the viewport.") ->Attribute(AZ::Edit::Attributes::ButtonText, "Global override") ->Attribute(AZ::Edit::Attributes::ButtonTooltip, "A global setting is overriding this property (to disable the override, " diff --git a/Gems/PhysX/Code/Editor/EditorJointConfiguration.cpp b/Gems/PhysX/Code/Editor/EditorJointConfiguration.cpp index b9ed827ce0..02e684de63 100644 --- a/Gems/PhysX/Code/Editor/EditorJointConfiguration.cpp +++ b/Gems/PhysX/Code/Editor/EditorJointConfiguration.cpp @@ -51,23 +51,27 @@ namespace PhysX if (auto* editContext = serializeContext->GetEditContext()) { editContext->Class( - "Editor Joint Limit Config Base", "Base joint limit parameters") + "Editor Joint Limit Config Base", "Base joint limit parameters.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Category, "PhysX") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) - ->DataElement(0, &PhysX::EditorJointLimitConfig::m_isLimited, "Limit", "True if the motion about the unconstrained axes of this joint are limited") + ->DataElement(0, &PhysX::EditorJointLimitConfig::m_isLimited, "Limit", + "When active, the joint's degrees of freedom are limited.") ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree) ->Attribute(AZ::Edit::Attributes::ReadOnly, &EditorJointLimitConfig::IsInComponentMode) - ->DataElement(0, &PhysX::EditorJointLimitConfig::m_isSoftLimit, "Soft limit", "True if the joint is allowed to rotate beyond limits and spring back") + ->DataElement(0, &PhysX::EditorJointLimitConfig::m_isSoftLimit, "Soft limit", + "When active, motion beyond the joint limit with a spring-like return is allowed.") ->Attribute(AZ::Edit::Attributes::Visibility, &EditorJointLimitConfig::m_isLimited) ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree) ->Attribute(AZ::Edit::Attributes::ReadOnly, &EditorJointLimitConfig::IsInComponentMode) - ->DataElement(0, &PhysX::EditorJointLimitConfig::m_damping, "Damping", "The damping strength of the drive, the force proportional to the velocity error") + ->DataElement(0, &PhysX::EditorJointLimitConfig::m_damping, "Damping", + "Dissipation of energy and reduction in spring oscillations when outside the joint limit.") ->Attribute(AZ::Edit::Attributes::Visibility, &EditorJointLimitConfig::IsSoftLimited) ->Attribute(AZ::Edit::Attributes::Max, s_springMax) ->Attribute(AZ::Edit::Attributes::Min, s_springMin) - ->DataElement(0, &PhysX::EditorJointLimitConfig::m_stiffness, "Stiffness", "The spring strength of the drive, the force proportional to the position error") + ->DataElement(0, &PhysX::EditorJointLimitConfig::m_stiffness, "Stiffness", + "The spring's drive relative to the position of the follower when outside the joint limit.") ->Attribute(AZ::Edit::Attributes::Visibility, &EditorJointLimitConfig::IsSoftLimited) ->Attribute(AZ::Edit::Attributes::Max, s_springMax) ->Attribute(AZ::Edit::Attributes::Min, s_springMin) @@ -115,18 +119,20 @@ namespace PhysX if (auto* editContext = serializeContext->GetEditContext()) { editContext->Class( - "Angular Limit", "Rotation limitation") + "Angular Limit", "Rotation limitation.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Category, "PhysX") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(0, &PhysX::EditorJointLimitPairConfig::m_standardLimitConfig , "Standard limit configuration" - , "Common limit parameters to all joint types") - ->DataElement(0, &PhysX::EditorJointLimitPairConfig::m_limitPositive, "Positive angular limit", "Positive rotation angle") + , "Common limit parameters to all joint types.") + ->DataElement(0, &PhysX::EditorJointLimitPairConfig::m_limitPositive, "Positive angular limit", + "Positive rotation angle.") ->Attribute(AZ::Edit::Attributes::Visibility, &EditorJointLimitPairConfig::IsLimited) ->Attribute(AZ::Edit::Attributes::Max, s_angleMax) ->Attribute(AZ::Edit::Attributes::Min, s_angleMin) - ->DataElement(0, &PhysX::EditorJointLimitPairConfig::m_limitNegative, "Negative angular limit", "Negative rotation angle") + ->DataElement(0, &PhysX::EditorJointLimitPairConfig::m_limitNegative, "Negative angular limit", + "Negative rotation angle.") ->Attribute(AZ::Edit::Attributes::Visibility, &EditorJointLimitPairConfig::IsLimited) ->Attribute(AZ::Edit::Attributes::Max, s_angleMin) ->Attribute(AZ::Edit::Attributes::Min, -s_angleMax) @@ -164,18 +170,20 @@ namespace PhysX if (auto* editContext = serializeContext->GetEditContext()) { editContext->Class( - "Angular Limit", "Rotation limitation") + "Angular Limit", "Rotation limitation.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Category, "PhysX") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(0, &PhysX::EditorJointLimitConeConfig::m_standardLimitConfig , "Standard limit configuration" - , "Common limit parameters to all joint types") - ->DataElement(0, &PhysX::EditorJointLimitConeConfig::m_limitY, "Y axis angular limit", "Limit for swing angle about Y axis") + , "Common limit parameters to all joint types.") + ->DataElement(0, &PhysX::EditorJointLimitConeConfig::m_limitY, "Y axis angular limit", + "Limit for swing angle about Y axis.") ->Attribute(AZ::Edit::Attributes::Visibility, &EditorJointLimitConeConfig::IsLimited) ->Attribute(AZ::Edit::Attributes::Max, s_angleMax) ->Attribute(AZ::Edit::Attributes::Min, s_angleMin) - ->DataElement(0, &PhysX::EditorJointLimitConeConfig::m_limitZ, "Z axis angular limit", "Limit for swing angle about Z axis") + ->DataElement(0, &PhysX::EditorJointLimitConeConfig::m_limitZ, "Z axis angular limit", + "Limit for swing angle about Z axis.") ->Attribute(AZ::Edit::Attributes::Visibility, &EditorJointLimitConeConfig::IsLimited) ->Attribute(AZ::Edit::Attributes::Max, s_angleMax) ->Attribute(AZ::Edit::Attributes::Min, s_angleMin) @@ -226,33 +234,33 @@ namespace PhysX ->Attribute(AZ::Edit::Attributes::Category, "PhysX") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(0, &PhysX::EditorJointConfig::m_localPosition, "Local Position" - , "Local Position of joint, relative to its entity") + , "Local Position of joint, relative to its entity.") ->DataElement(0, &PhysX::EditorJointConfig::m_localRotation, "Local Rotation" - , "Local Rotation of joint, relative to its entity") + , "Local Rotation of joint, relative to its entity.") ->Attribute(AZ::Edit::Attributes::Min, LocalRotationMin) ->Attribute(AZ::Edit::Attributes::Max, LocalRotationMax) ->DataElement(0, &PhysX::EditorJointConfig::m_leadEntity, "Lead Entity" - , "Parent entity associated with joint") + , "Parent entity associated with joint.") ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorJointConfig::ValidateLeadEntityId) ->DataElement(0, &PhysX::EditorJointConfig::m_selfCollide, "Lead-Follower Collide" - , "Lead and follower pair will collide with each other") + , "When active, the lead and follower pair will collide with each other.") ->DataElement(0, &PhysX::EditorJointConfig::m_displayJointSetup, "Display Setup in Viewport" - , "Display joint setup in the viewport") + , "Display joint setup in the viewport.") ->Attribute(AZ::Edit::Attributes::ReadOnly, &EditorJointConfig::IsInComponentMode) ->DataElement(0, &PhysX::EditorJointConfig::m_selectLeadOnSnap, "Select Lead on Snap" - , "Select lead entity on snap to position in component mode") + , "Select lead entity on snap to position in component mode.") ->DataElement(0, &PhysX::EditorJointConfig::m_breakable , "Breakable" - , "Joint is breakable when force or torque exceeds limit") + , "Joint is breakable when force or torque exceeds limit.") ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree) ->Attribute(AZ::Edit::Attributes::ReadOnly, &EditorJointConfig::IsInComponentMode) ->DataElement(0, &PhysX::EditorJointConfig::m_forceMax, - "Maximum Force", "Amount of force joint can withstand before breakage") + "Maximum Force", "Amount of force joint can withstand before breakage.") ->Attribute(AZ::Edit::Attributes::Visibility, &EditorJointConfig::m_breakable) ->Attribute(AZ::Edit::Attributes::Max, s_breakageMax) ->Attribute(AZ::Edit::Attributes::Min, s_breakageMin) ->DataElement(0, &PhysX::EditorJointConfig::m_torqueMax, - "Maximum Torque", "Amount of torque joint can withstand before breakage") + "Maximum Torque", "Amount of torque joint can withstand before breakage.") ->Attribute(AZ::Edit::Attributes::Visibility, &EditorJointConfig::m_breakable) ->Attribute(AZ::Edit::Attributes::Max, s_breakageMax) ->Attribute(AZ::Edit::Attributes::Min, s_breakageMin) diff --git a/Gems/PhysX/Code/Source/Configuration/PhysXConfiguration.cpp b/Gems/PhysX/Code/Source/Configuration/PhysXConfiguration.cpp index 7a441a1c55..02f0bc038a 100644 --- a/Gems/PhysX/Code/Source/Configuration/PhysXConfiguration.cpp +++ b/Gems/PhysX/Code/Source/Configuration/PhysXConfiguration.cpp @@ -54,17 +54,17 @@ namespace PhysX if (AZ::EditContext* editContext = serialize->GetEditContext()) { - editContext->Class("Wind Configuration", "Wind settings for PhysX") + editContext->Class("Wind Configuration", "Wind force entity tags.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(AZ::Edit::UIHandlers::Default, &WindConfiguration::m_globalWindTag, "Global wind tag", - "Tag value that will be used to mark entities that provide global wind value.\n" - "Global wind has no bounds and affects objects across entire level.") + "Global wind provider tags.\n" + "Global winds apply to entire world.") ->DataElement(AZ::Edit::UIHandlers::Default, &WindConfiguration::m_localWindTag, "Local wind tag", - "Tag value that will be used to mark entities that provide local wind value.\n" - "Local wind is only applied within bounds defined by PhysX collider.") + "Local wind provider tags.\n" + "Local winds are constrained to a PhysX collider's boundaries.") ; } } diff --git a/Gems/PhysX/Code/Source/Debug/Configuration/PhysXDebugConfiguration.cpp b/Gems/PhysX/Code/Source/Debug/Configuration/PhysXDebugConfiguration.cpp index e14ce62537..c199159a43 100644 --- a/Gems/PhysX/Code/Source/Debug/Configuration/PhysXDebugConfiguration.cpp +++ b/Gems/PhysX/Code/Source/Debug/Configuration/PhysXDebugConfiguration.cpp @@ -31,38 +31,39 @@ namespace PhysX if (AZ::EditContext* editContext = serialize->GetEditContext()) { - editContext->Class("PhysX PVD Settings", "PhysX PVD Settings") + editContext->Class("PhysX PVD Settings", + "Connection configuration settings for the PhysX Visual Debugger (PVD). Requires PhysX Debug Gem.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(AZ::Edit::UIHandlers::ComboBox, &PvdConfiguration::m_transportType, - "PVD Transport Type", "PVD supports writing to a TCP/IP network socket or to a file.") + "PVD Transport Type", "Output PhysX Visual Debugger data to a TCP/IP network socket or to a file.") ->EnumAttribute(Debug::PvdTransportType::Network, "Network") ->EnumAttribute(Debug::PvdTransportType::File, "File") ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree) ->DataElement(AZ::Edit::UIHandlers::Default, &PvdConfiguration::m_host, - "PVD Host", "Host IP address of the PhysX Visual Debugger application") + "PVD Host", "Host IP address of the PhysX Visual Debugger server.") ->Attribute(AZ::Edit::Attributes::Visibility, &PvdConfiguration::IsNetworkDebug) ->DataElement(AZ::Edit::UIHandlers::Default, &PvdConfiguration::m_port, - "PVD Port", "Port of the PhysX Visual Debugger application") + "PVD Port", "Port of the PhysX Visual Debugger server.") ->Attribute(AZ::Edit::Attributes::Visibility, &PvdConfiguration::IsNetworkDebug) + ->Attribute(AZ::Edit::Attributes::Min, AZStd::numeric_limits::min()) + ->Attribute(AZ::Edit::Attributes::Max, AZStd::numeric_limits::max()) ->DataElement(AZ::Edit::UIHandlers::Default, &PvdConfiguration::m_timeoutInMilliseconds, - "PVD Timeout", "Timeout (in milliseconds) used when connecting to the PhysX Visual Debugger application") + "PVD Timeout", "Timeout (in milliseconds) when connecting to the PhysX Visual Debugger server.") ->Attribute(AZ::Edit::Attributes::Visibility, &PvdConfiguration::IsNetworkDebug) ->DataElement(AZ::Edit::UIHandlers::Default, &PvdConfiguration::m_fileName, - "PVD FileName", "Filename to output PhysX Visual Debugger data.") + "PVD FileName", "Output filename for PhysX Visual Debugger data.") ->Attribute(AZ::Edit::Attributes::Visibility, &PvdConfiguration::IsFileDebug) ->DataElement(AZ::Edit::UIHandlers::ComboBox, &PvdConfiguration::m_autoConnectMode, - "PVD Auto Connect", "Automatically connect to the PhysX Visual Debugger " - "(Requires PhysX Debug gem for Editor and Game modes).") + "PVD Auto Connect", "Automatically connect to the PhysX Visual Debugger.") ->EnumAttribute(Debug::PvdAutoConnectMode::Disabled, "Disabled") ->EnumAttribute(Debug::PvdAutoConnectMode::Editor, "Editor") ->EnumAttribute(Debug::PvdAutoConnectMode::Game, "Game") - ->DataElement(AZ::Edit::UIHandlers::CheckBox, &PvdConfiguration::m_reconnect, - "PVD Reconnect", "Reconnect (Disconnect and Connect) when switching between game and edit mode " - "(Requires PhysX Debug gem).") + ->DataElement(AZ::Edit::UIHandlers::CheckBox, &PvdConfiguration::m_reconnect, "PVD Reconnect", + "Reconnect (disconnect and connect) to the PhysX Visual Debugger server when switching between game and edit mode.") ; } } @@ -131,7 +132,7 @@ namespace PhysX if (AZ::EditContext* editContext = serialize->GetEditContext()) { - editContext->Class("Editor Configuration", "Editor settings for PhysX") + editContext->Class("Editor Configuration", "Editor settings for PhysX.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(AZ::Edit::UIHandlers::Slider, &DebugDisplayData::m_centerOfMassDebugSize, diff --git a/Gems/PhysX/Code/Source/EditorBallJointComponent.cpp b/Gems/PhysX/Code/Source/EditorBallJointComponent.cpp index 92952bcdaa..fa71fa0061 100644 --- a/Gems/PhysX/Code/Source/EditorBallJointComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorBallJointComponent.cpp @@ -33,14 +33,14 @@ namespace PhysX if (auto* editContext = serializeContext->GetEditContext()) { editContext->Class( - "PhysX Ball Joint", "The ball joint supports a cone limiting the maximum rotation around the y and z axes.") + "PhysX Ball Joint", "A dynamic joint constraint with swing rotation limits around the Y and Z axes of the joint.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Category, "PhysX") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/physx/ball-joint/") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->DataElement(0, &EditorBallJointComponent::m_swingLimit, "Swing Limit", "Limitations for the swing (Y and Z axis) about joint") - ->DataElement(AZ::Edit::UIHandlers::Default, &EditorBallJointComponent::m_componentModeDelegate, "Component Mode", "Ball Joint Component Mode") + ->DataElement(0, &EditorBallJointComponent::m_swingLimit, "Swing Limit", "The rotation angle limit around the joint's Y and Z axes.") + ->DataElement(AZ::Edit::UIHandlers::Default, &EditorBallJointComponent::m_componentModeDelegate, "Component Mode", "Ball Joint Component Mode.") ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) ; } diff --git a/Gems/PhysX/Code/Source/EditorColliderComponent.cpp b/Gems/PhysX/Code/Source/EditorColliderComponent.cpp index f8d0adf156..262e40cb99 100644 --- a/Gems/PhysX/Code/Source/EditorColliderComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorColliderComponent.cpp @@ -53,13 +53,15 @@ namespace PhysX if (auto editContext = serializeContext->GetEditContext()) { - editContext->Class("EditorProxyShapeConfig", "PhysX Base shape collider") + editContext->Class("EditorProxyShapeConfig", "PhysX Base collider.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->DataElement(AZ::Edit::UIHandlers::Default, &EditorProxyAssetShapeConfig::m_pxAsset, "PhysX Mesh", "PhysX mesh collider asset") + ->DataElement(AZ::Edit::UIHandlers::Default, &EditorProxyAssetShapeConfig::m_pxAsset, "PhysX Mesh", + "Specifies the PhysX mesh collider asset for this PhysX collider component.") ->Attribute(AZ_CRC_CE("EditButton"), "") ->Attribute(AZ_CRC_CE("EditDescription"), "Open in Scene Settings") - ->DataElement(AZ::Edit::UIHandlers::Default, &EditorProxyAssetShapeConfig::m_configuration, "Configuration", "Configuration of asset shape") + ->DataElement(AZ::Edit::UIHandlers::Default, &EditorProxyAssetShapeConfig::m_configuration, "Configuration", + "PhysX mesh asset collider configuration.") ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly); } } @@ -86,7 +88,7 @@ namespace PhysX { editContext->Class( "EditorProxyShapeConfig", "PhysX Base shape collider") - ->DataElement(AZ::Edit::UIHandlers::ComboBox, &EditorProxyShapeConfig::m_shapeType, "Shape", "The shape of the collider") + ->DataElement(AZ::Edit::UIHandlers::ComboBox, &EditorProxyShapeConfig::m_shapeType, "Shape", "The shape of the collider.") ->EnumAttribute(Physics::ShapeType::Sphere, "Sphere") ->EnumAttribute(Physics::ShapeType::Box, "Box") ->EnumAttribute(Physics::ShapeType::Capsule, "Capsule") @@ -96,20 +98,20 @@ namespace PhysX // potentially be different ComponentModes for different shape types) ->Attribute(AZ::Edit::Attributes::ReadOnly, &AzToolsFramework::ComponentModeFramework::InComponentMode) - ->DataElement(AZ::Edit::UIHandlers::Default, &EditorProxyShapeConfig::m_sphere, "Sphere", "Configuration of sphere shape") + ->DataElement(AZ::Edit::UIHandlers::Default, &EditorProxyShapeConfig::m_sphere, "Sphere", "Configuration of sphere shape.") ->Attribute(AZ::Edit::Attributes::Visibility, &EditorProxyShapeConfig::IsSphereConfig) ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorProxyShapeConfig::OnConfigurationChanged) - ->DataElement(AZ::Edit::UIHandlers::Default, &EditorProxyShapeConfig::m_box, "Box", "Configuration of box shape") + ->DataElement(AZ::Edit::UIHandlers::Default, &EditorProxyShapeConfig::m_box, "Box", "Configuration of box shape.") ->Attribute(AZ::Edit::Attributes::Visibility, &EditorProxyShapeConfig::IsBoxConfig) ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorProxyShapeConfig::OnConfigurationChanged) - ->DataElement(AZ::Edit::UIHandlers::Default, &EditorProxyShapeConfig::m_capsule, "Capsule", "Configuration of capsule shape") + ->DataElement(AZ::Edit::UIHandlers::Default, &EditorProxyShapeConfig::m_capsule, "Capsule", "Configuration of capsule shape.") ->Attribute(AZ::Edit::Attributes::Visibility, &EditorProxyShapeConfig::IsCapsuleConfig) ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorProxyShapeConfig::OnConfigurationChanged) - ->DataElement(AZ::Edit::UIHandlers::Default, &EditorProxyShapeConfig::m_physicsAsset, "Asset", "Configuration of asset shape") + ->DataElement(AZ::Edit::UIHandlers::Default, &EditorProxyShapeConfig::m_physicsAsset, "Asset", "Configuration of asset shape.") ->Attribute(AZ::Edit::Attributes::Visibility, &EditorProxyShapeConfig::IsAssetConfig) ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorProxyShapeConfig::OnConfigurationChanged) ->DataElement(AZ::Edit::UIHandlers::Default, &EditorProxyShapeConfig::m_subdivisionLevel, "Subdivision level", - "The level of subdivision if a primitive shape is replaced with a convex mesh due to scaling") + "The level of subdivision if a primitive shape is replaced with a convex mesh due to scaling.") ->Attribute(AZ::Edit::Attributes::Min, Utils::MinCapsuleSubdivisionLevel) ->Attribute(AZ::Edit::Attributes::Max, Utils::MaxCapsuleSubdivisionLevel) ->Attribute(AZ::Edit::Attributes::Visibility, &EditorProxyShapeConfig::ShowingSubdivisionLevel) @@ -200,7 +202,7 @@ namespace PhysX if (auto editContext = serializeContext->GetEditContext()) { editContext->Class( - "PhysX Collider", "PhysX shape collider") + "PhysX Collider", "Creates geometry in the PhysX simulation, using either a primitive shape or geometry from an asset.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Category, "PhysX") ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/PhysXCollider.svg") @@ -208,17 +210,17 @@ namespace PhysX ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/physx/collider/") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->DataElement(AZ::Edit::UIHandlers::Default, &EditorColliderComponent::m_configuration, "Collider Configuration", "Configuration of the collider") + ->DataElement(AZ::Edit::UIHandlers::Default, &EditorColliderComponent::m_configuration, "Collider Configuration", "Configuration of the collider.") ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorColliderComponent::OnConfigurationChanged) - ->DataElement(AZ::Edit::UIHandlers::Default, &EditorColliderComponent::m_shapeConfiguration, "Shape Configuration", "Configuration of the shape") + ->DataElement(AZ::Edit::UIHandlers::Default, &EditorColliderComponent::m_shapeConfiguration, "Shape Configuration", "Configuration of the shape.") ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorColliderComponent::OnConfigurationChanged) ->Attribute(AZ::Edit::Attributes::RemoveNotify, &EditorColliderComponent::ValidateRigidBodyMeshGeometryType) - ->DataElement(AZ::Edit::UIHandlers::Default, &EditorColliderComponent::m_componentModeDelegate, "Component Mode", "Collider Component Mode") + ->DataElement(AZ::Edit::UIHandlers::Default, &EditorColliderComponent::m_componentModeDelegate, "Component Mode", "Collider Component Mode.") ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) ->DataElement(AZ::Edit::UIHandlers::Default, &EditorColliderComponent::m_colliderDebugDraw, - "Debug draw settings", "Debug draw settings") + "Debug draw settings", "Debug draw settings.") ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) ; } diff --git a/Gems/PhysX/Code/Source/EditorFixedJointComponent.cpp b/Gems/PhysX/Code/Source/EditorFixedJointComponent.cpp index 94f57690ba..692671174b 100644 --- a/Gems/PhysX/Code/Source/EditorFixedJointComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorFixedJointComponent.cpp @@ -30,13 +30,14 @@ namespace PhysX if (auto* editContext = serializeContext->GetEditContext()) { editContext->Class( - "PhysX Fixed Joint", "The fixed joint constraints the position and orientation of a body to another.") + "PhysX Fixed Joint", + "A dynamic joint constraint that constrains a rigid body to the joint with no free translation or rotation on any axis.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Category, "PhysX") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/physx/fixed-joint/") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->DataElement(AZ::Edit::UIHandlers::Default, &EditorFixedJointComponent::m_componentModeDelegate, "Component Mode", "Fixed Joint Component Mode") + ->DataElement(AZ::Edit::UIHandlers::Default, &EditorFixedJointComponent::m_componentModeDelegate, "Component Mode", "Fixed Joint Component Mode.") ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) ; } diff --git a/Gems/PhysX/Code/Source/EditorForceRegionComponent.cpp b/Gems/PhysX/Code/Source/EditorForceRegionComponent.cpp index 610a0a9b2e..dff1ff86bb 100644 --- a/Gems/PhysX/Code/Source/EditorForceRegionComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorForceRegionComponent.cpp @@ -164,7 +164,7 @@ namespace PhysX { // EditorForceRegionComponent editContext->Class( - "PhysX Force Region", "The force region component is used to apply a physical force on objects within the region") + "PhysX Force Region", "The force region component is used to apply a physical force on objects within the region.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Category, "PhysX") ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/ForceVolume.svg") @@ -173,9 +173,10 @@ namespace PhysX ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/physx/force-region/") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->Attribute(AZ::Edit::Attributes::RequiredService, AZ_CRC("PhysXTriggerService", 0x3a117d7b)) - ->DataElement(AZ::Edit::UIHandlers::Default, &EditorForceRegionComponent::m_visibleInEditor, "Visible", "Always show the component in viewport") - ->DataElement(AZ::Edit::UIHandlers::Default, &EditorForceRegionComponent::m_debugForces, "Debug Forces", "Draws debug arrows when an entity enters a force region. This occurs in gameplay mode to show the force direction on an entity.") - ->DataElement(AZ::Edit::UIHandlers::Default, &EditorForceRegionComponent::m_forces, "Forces", "Forces in force region") + ->DataElement(AZ::Edit::UIHandlers::Default, &EditorForceRegionComponent::m_visibleInEditor, "Visible", "Always show the component in viewport.") + ->DataElement(AZ::Edit::UIHandlers::Default, &EditorForceRegionComponent::m_debugForces, "Debug Forces", + "Draws debug arrows when an entity enters a force region. This occurs in gameplay mode to show the force direction on an entity.") + ->DataElement(AZ::Edit::UIHandlers::Default, &EditorForceRegionComponent::m_forces, "Forces", "Forces in force region.") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorForceRegionComponent::OnForcesChanged) ; diff --git a/Gems/PhysX/Code/Source/EditorHingeJointComponent.cpp b/Gems/PhysX/Code/Source/EditorHingeJointComponent.cpp index 5537502327..1b575074e2 100644 --- a/Gems/PhysX/Code/Source/EditorHingeJointComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorHingeJointComponent.cpp @@ -33,14 +33,14 @@ namespace PhysX if (auto* editContext = serializeContext->GetEditContext()) { editContext->Class( - "PhysX Hinge Joint", "The entity constrains two actors in PhysX, keeping the origins and x-axes together, and allows free rotation around this common axis") + "PhysX Hinge Joint", "A dynamic joint that constrains a rigid body with rotation limits around a single axis.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Category, "PhysX") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/physx/hinge-joint/") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->DataElement(0, &EditorHingeJointComponent::m_angularLimit, "Angular Limit", "Limitations for the rotation about hinge axis") - ->DataElement(AZ::Edit::UIHandlers::Default, &EditorHingeJointComponent::m_componentModeDelegate, "Component Mode", "Hinge Joint Component Mode") + ->DataElement(0, &EditorHingeJointComponent::m_angularLimit, "Angular Limit", "The rotation angle limit around the joint's axis.") + ->DataElement(AZ::Edit::UIHandlers::Default, &EditorHingeJointComponent::m_componentModeDelegate, "Component Mode", "Hinge Joint Component Mode.") ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) ; } diff --git a/Gems/PhysX/Code/Source/EditorJointComponent.cpp b/Gems/PhysX/Code/Source/EditorJointComponent.cpp index 88e192026d..9a24392fd0 100644 --- a/Gems/PhysX/Code/Source/EditorJointComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorJointComponent.cpp @@ -37,11 +37,11 @@ namespace PhysX if (auto* editContext = serializeContext->GetEditContext()) { editContext->Class( - "PhysX Joint", "The joint constrains the position and orientation of a body to another.") + "PhysX Joint", "A dynamic joint that constrains the position and orientation of one rigid body to another.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Category, "PhysX") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->DataElement(0, &EditorJointComponent::m_config, "Standard Joint Parameters", "Joint parameters shared by all joint types") + ->DataElement(0, &EditorJointComponent::m_config, "Standard Joint Parameters", "Joint parameters shared by all joint types.") ; } } diff --git a/Gems/PhysX/Code/Source/EditorRigidBodyComponent.cpp b/Gems/PhysX/Code/Source/EditorRigidBodyComponent.cpp index 24b2586c9e..e384f222e8 100644 --- a/Gems/PhysX/Code/Source/EditorRigidBodyComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorRigidBodyComponent.cpp @@ -122,38 +122,38 @@ namespace PhysX ->Attribute(AZ::Edit::Attributes::Category, "PhysX") ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) ->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_initialLinearVelocity, - "Initial linear velocity", "Initial linear velocity") + "Initial linear velocity", "Linear velocity applied when the rigid body is activated.") ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetInitialVelocitiesVisibility) ->Attribute(AZ::Edit::Attributes::Suffix, " " + Physics::NameConstants::GetSpeedUnit()) ->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_initialAngularVelocity, - "Initial angular velocity", "Initial angular velocity (limited by maximum angular velocity)") + "Initial angular velocity", "Angular velocity applied when the rigid body is activated (limited by maximum angular velocity).") ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetInitialVelocitiesVisibility) ->Attribute(AZ::Edit::Attributes::Suffix, " " + Physics::NameConstants::GetAngularVelocityUnit()) ->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_linearDamping, - "Linear damping", "Linear damping (must be non-negative)") + "Linear damping", "The rate of decay over time for linear velocity even if no forces are acting on the rigid body.") ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetDampingVisibility) ->Attribute(AZ::Edit::Attributes::Min, 0.0f) ->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_angularDamping, - "Angular damping", "Angular damping (must be non-negative)") + "Angular damping", "The rate of decay over time for angular velocity even if no forces are acting on the rigid body.") ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetDampingVisibility) ->Attribute(AZ::Edit::Attributes::Min, 0.0f) ->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_sleepMinEnergy, - "Sleep threshold", "Kinetic energy per unit mass below which body can go to sleep (must be non-negative)") + "Sleep threshold", "The rigid body can go to sleep (settle) when kinetic energy per unit mass is persistently below this value.") ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetSleepOptionsVisibility) ->Attribute(AZ::Edit::Attributes::Min, 0.0f) ->Attribute(AZ::Edit::Attributes::Suffix, " " + Physics::NameConstants::GetSleepThresholdUnit()) ->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_startAsleep, - "Start asleep", "The rigid body will be asleep when spawned") + "Start asleep", "When active, the rigid body will be asleep when spawned, and wake when the body is disturbed.") ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetSleepOptionsVisibility) ->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_interpolateMotion, - "Interpolate motion", "Makes object motion look smoother") + "Interpolate motion", "When active, simulation results are interpolated resulting in smoother motion.") ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetInterpolationVisibility) ->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_gravityEnabled, - "Gravity enabled", "Rigid body will be affected by gravity") + "Gravity enabled", "When active, global gravity affects this rigid body.") ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetGravityVisibility) ->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_kinematic, - "Kinematic", "Rigid body is kinematic") + "Kinematic", "When active, the rigid body is not affected by gravity or other forces and is moved by script.") ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetKinematicVisibility) // Linear axis locking properties @@ -161,85 +161,90 @@ namespace PhysX ->Attribute(AZ::Edit::Attributes::AutoExpand, false) ->DataElement( AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_lockLinearX, "Lock X", - "Lock motion along X direction") + "When active, forces won't create translation on the X axis of the rigid body.") ->DataElement( AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_lockLinearY, "Lock Y", - "Lock motion along Y direction") + "When active, forces won't create translation on the Y axis of the rigid body.") ->DataElement( AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_lockLinearZ, "Lock Z", - "Lock motion along Z direction") + "When active, forces won't create translation on the Z axis of the rigid body.") // Angular axis locking properties ->ClassElement(AZ::Edit::ClassElements::Group, "Angular Axis Locking") ->Attribute(AZ::Edit::Attributes::AutoExpand, false) ->DataElement( AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_lockAngularX, "Lock X", - "Lock rotation around X direction") + "When active, forces won't create rotation on the X axis of the rigid body.") ->DataElement( AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_lockAngularY, "Lock Y", - "Lock rotation around Y direction") + "When active, forces won't create rotation on the Y axis of the rigid body.") ->DataElement( AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_lockAngularZ, "Lock Z", - "Lock rotation around Z direction") + "When active, forces won't create rotation on the Z axis of the rigid body.") ->ClassElement(AZ::Edit::ClassElements::Group, "Continuous Collision Detection") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetCCDVisibility) ->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_ccdEnabled, - "CCD enabled", "Whether continuous collision detection is enabled for this body") + "CCD enabled", "When active, the rigid body has continuous collision detection (CCD). Use this to ensure accurate " + "collision detection, particularly for fast moving rigid bodies. CCD must be activated in the global PhysX preferences.") ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetCCDVisibility) ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree) ->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_ccdMinAdvanceCoefficient, - "Min advance coefficient", "Lower values reduce clipping but can affect simulation smoothness") + "Min advance coefficient", "Lower values reduce clipping but can affect simulation smoothness.") ->Attribute(AZ::Edit::Attributes::Min, 0.01f) ->Attribute(AZ::Edit::Attributes::Step, 0.01f) ->Attribute(AZ::Edit::Attributes::Max, 0.99f) ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::IsCCDEnabled) ->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_ccdFrictionEnabled, - "CCD friction", "Whether friction is applied when CCD collisions are resolved") + "CCD friction", "When active, friction is applied when continuous collision detection (CCD) collisions are resolved.") ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::IsCCDEnabled) ->ClassElement(AZ::Edit::ClassElements::Group, "") // end previous group by starting new unnamed expanded group ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_maxAngularVelocity, - "Maximum angular velocity", "The PhysX solver will clamp angular velocities with magnitude exceeding this value") + "Maximum angular velocity", "Clamp angular velocities to this maximum value. " + "This prevents rigid bodies from rotating at unrealistic velocities after collisions.") ->Attribute(AZ::Edit::Attributes::Min, 0.0f) ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetMaxVelocitiesVisibility) ->Attribute(AZ::Edit::Attributes::Suffix, " " + Physics::NameConstants::GetAngularVelocityUnit()) // Mass properties ->DataElement(AZ::Edit::UIHandlers::Default, &RigidBodyConfiguration::m_computeCenterOfMass, - "Compute COM", "Whether to automatically compute the center of mass") + "Compute COM", "Compute the center of mass (COM) for this rigid body.") ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetInertiaSettingsVisibility) ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree) ->DataElement(AZ::Edit::UIHandlers::Default, &RigidBodyConfiguration::m_centerOfMassOffset, - "COM offset", "Center of mass offset in local frame") + "COM offset", "Local space offset for the center of mass (COM).") ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetCoMVisibility) ->Attribute(AZ::Edit::Attributes::Suffix, " " + Physics::NameConstants::GetLengthUnit()) ->DataElement(AZ::Edit::UIHandlers::Default, &RigidBodyConfiguration::m_computeMass, - "Compute Mass", "Whether to automatically compute the mass") + "Compute Mass", "When active, the mass of the rigid body is computed based on the volume and density values of its colliders.") ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetInertiaSettingsVisibility) ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree) ->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_mass, - "Mass", "The mass of the object (must be non-negative, with a value of zero treated as infinite)") + "Mass", "The mass of the rigid body in kilograms. A value of 0 is treated as infinite. " + "The trajectory of infinite mass bodies cannot be affected by any collisions or forces other than gravity.") ->Attribute(AZ::Edit::Attributes::Min, 0.0f) ->Attribute(AZ::Edit::Attributes::Suffix, " " + Physics::NameConstants::GetMassUnit()) ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetMassVisibility) ->DataElement(AZ::Edit::UIHandlers::Default, &RigidBodyConfiguration::m_computeInertiaTensor, - "Compute inertia", "Whether to automatically compute the inertia values based on the mass and shape of the rigid body") + "Compute inertia", "When active, inertia is computed based on the mass and shape of the rigid body.") ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetInertiaSettingsVisibility) ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree) ->DataElement(Editor::InertiaHandler, &AzPhysics::RigidBodyConfiguration::m_inertiaTensor, - "Inertia diagonal", "Diagonal elements of the inertia tensor") + "Inertia diagonal", "Inertia diagonal elements that specify an inertia tensor; determines the " + "torque required to rotate the rigid body on each axis.") ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetInertiaVisibility) ->Attribute(AZ::Edit::Attributes::Suffix, " " + Physics::NameConstants::GetInertiaUnit()) ->DataElement(AZ::Edit::UIHandlers::Default, &RigidBodyConfiguration::m_includeAllShapesInMassCalculation, - "Include non-simulated shapes in Mass", "If set, non-simulated shapes will also be included in the center of mass, inertia and mass calculations.") + "Include non-simulated shapes in Mass", + "When active, non-simulated shapes are included in the center of mass, inertia, and mass calculations.") ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetInertiaSettingsVisibility) ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree) ; @@ -250,7 +255,7 @@ namespace PhysX ->Attribute(AZ::Edit::Attributes::Category, "PhysX") ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) ->DataElement(AZ::Edit::UIHandlers::Default, &EditorRigidBodyConfiguration::m_centerOfMassDebugDraw, - "Debug draw COM", "Whether to debug draw the center of mass for this body") + "Debug draw COM", "Display the rigid body's center of mass (COM) in the viewport.") ; } } diff --git a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp index 2bbc04faae..4ee51d7787 100644 --- a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp @@ -79,7 +79,7 @@ namespace PhysX if (auto editContext = serializeContext->GetEditContext()) { editContext->Class( - "PhysX Shape Collider", "Creates geometry in the PhysX simulation based on an attached shape component") + "PhysX Shape Collider", "Create a PhysX collider using a shape provided by a Shape component.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Category, "PhysX") ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/PhysXCollider.svg") @@ -88,13 +88,14 @@ namespace PhysX ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/physx/shape-collider/") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(AZ::Edit::UIHandlers::Default, &EditorShapeColliderComponent::m_colliderConfig, - "Collider configuration", "Configuration of the collider") + "Collider configuration", "Configuration of the collider.") ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorShapeColliderComponent::OnConfigurationChanged) ->DataElement(AZ::Edit::UIHandlers::Default, &EditorShapeColliderComponent::m_colliderDebugDraw, - "Debug draw settings", "Debug draw settings") + "Debug draw settings", "Debug draw settings.") ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) - ->DataElement(AZ::Edit::UIHandlers::Default, &EditorShapeColliderComponent::m_subdivisionCount, "Subdivision count", "Number of angular subdivisions in the PhysX cylinder") + ->DataElement(AZ::Edit::UIHandlers::Default, &EditorShapeColliderComponent::m_subdivisionCount, "Subdivision count", + "Number of angular subdivisions in the PhysX cylinder.") ->Attribute(AZ::Edit::Attributes::Min, Utils::MinFrustumSubdivisions) ->Attribute(AZ::Edit::Attributes::Max, Utils::MaxFrustumSubdivisions) ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorShapeColliderComponent::OnSubdivisionCountChange) diff --git a/Gems/PhysX/Code/Source/ForceRegion.cpp b/Gems/PhysX/Code/Source/ForceRegion.cpp index 63426d304e..5314f97f9a 100644 --- a/Gems/PhysX/Code/Source/ForceRegion.cpp +++ b/Gems/PhysX/Code/Source/ForceRegion.cpp @@ -62,10 +62,10 @@ namespace PhysX if (auto editContext = serializeContext->GetEditContext()) { editContext->Class( - "Force Region", "Applies forces on entities within a region") + "Force Region", "Applies forces on entities within a region.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->DataElement(AZ::Edit::UIHandlers::Default, &ForceRegion::m_forces, "Forces", "Forces acting in the region") + ->DataElement(AZ::Edit::UIHandlers::Default, &ForceRegion::m_forces, "Forces", "Forces acting in the region.") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ; } diff --git a/Gems/PhysX/Code/Source/ForceRegionForces.cpp b/Gems/PhysX/Code/Source/ForceRegionForces.cpp index 202a073640..6ed4c3e93a 100644 --- a/Gems/PhysX/Code/Source/ForceRegionForces.cpp +++ b/Gems/PhysX/Code/Source/ForceRegionForces.cpp @@ -37,13 +37,13 @@ namespace PhysX if (auto editContext = serializeContext->GetEditContext()) { editContext->Class( - "World Space Force", "Applies a force in world space") + "World Space Force", "Applies a force in world space.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->DataElement(AZ::Edit::UIHandlers::Vector3, &ForceWorldSpace::m_direction, "Direction", "Direction of the force in world space") + ->DataElement(AZ::Edit::UIHandlers::Vector3, &ForceWorldSpace::m_direction, "Direction", "Direction of the force in world space.") ->Attribute(AZ::Edit::Attributes::Min, s_forceRegionMinValue) ->Attribute(AZ::Edit::Attributes::Max, s_forceRegionMaxValue) - ->DataElement(AZ::Edit::UIHandlers::Default, &ForceWorldSpace::m_magnitude, "Magnitude", "Magnitude of the force in world space") + ->DataElement(AZ::Edit::UIHandlers::Default, &ForceWorldSpace::m_magnitude, "Magnitude", "Magnitude of the force in world space.") ->Attribute(AZ::Edit::Attributes::Min, s_forceRegionMinValue) ->Attribute(AZ::Edit::Attributes::Max, s_forceRegionMaxValue) ; @@ -109,13 +109,13 @@ namespace PhysX if (auto editContext = serializeContext->GetEditContext()) { editContext->Class( - "Local Space Force", "Applies a force in the volume's local space") + "Local Space Force", "Applies a force in the volume's local space.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->DataElement(AZ::Edit::UIHandlers::Vector3, &ForceLocalSpace::m_direction, "Direction", "Direction of the force in local space") + ->DataElement(AZ::Edit::UIHandlers::Vector3, &ForceLocalSpace::m_direction, "Direction", "Direction of the force in local space.") ->Attribute(AZ::Edit::Attributes::Min, s_forceRegionMinValue) ->Attribute(AZ::Edit::Attributes::Max, s_forceRegionMaxValue) - ->DataElement(AZ::Edit::UIHandlers::Default, &ForceLocalSpace::m_magnitude, "Magnitude", "Magnitude of the force in local space") + ->DataElement(AZ::Edit::UIHandlers::Default, &ForceLocalSpace::m_magnitude, "Magnitude", "Magnitude of the force in local space.") ->Attribute(AZ::Edit::Attributes::Min, s_forceRegionMinValue) ->Attribute(AZ::Edit::Attributes::Max, s_forceRegionMaxValue) ; @@ -179,10 +179,10 @@ namespace PhysX if (auto editContext = serializeContext->GetEditContext()) { editContext->Class( - "Point Force", "Applies a force relative to the center of the volume") + "Point Force", "Applies a force directed towards or away from the center of the volume.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->DataElement(AZ::Edit::UIHandlers::Default, &ForcePoint::m_magnitude, "Magnitude", "Magnitude of the point force") + ->DataElement(AZ::Edit::UIHandlers::Default, &ForcePoint::m_magnitude, "Magnitude", "Magnitude of the point force.") ->Attribute(AZ::Edit::Attributes::Min, s_forceRegionMinValue) ->Attribute(AZ::Edit::Attributes::Max, s_forceRegionMaxValue) ; @@ -242,19 +242,24 @@ namespace PhysX if (auto editContext = serializeContext->GetEditContext()) { editContext->Class( - "Spline Follow Force", "Applies a force to make objects follow a spline at a given speed") + "Spline Follow Force", "Applies a force to make objects follow a spline at a given speed.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->DataElement(AZ::Edit::UIHandlers::Default, &ForceSplineFollow::m_dampingRatio, "Damping Ratio", "Amount of damping applied to an entity that is moving towards a spline") + ->DataElement(AZ::Edit::UIHandlers::Default, &ForceSplineFollow::m_dampingRatio, "Damping Ratio", + "Values below 1 cause the entity to approach the spline faster but lead to overshooting and oscillation, " + "while higher values will cause it to approach more slowly but more smoothly.") ->Attribute(AZ::Edit::Attributes::Min, s_forceRegionZeroValue) ->Attribute(AZ::Edit::Attributes::Max, s_forceRegionMaxDampingRatio) - ->DataElement(AZ::Edit::UIHandlers::Default, &ForceSplineFollow::m_frequency, "Frequency", "Frequency at which an entity moves towards a spline") + ->DataElement(AZ::Edit::UIHandlers::Default, &ForceSplineFollow::m_frequency, "Frequency", + "Affects how quickly the entity approaches the spline.") ->Attribute(AZ::Edit::Attributes::Min, s_forceRegionMinFrequency) ->Attribute(AZ::Edit::Attributes::Max, s_forceRegionMaxFrequency) - ->DataElement(AZ::Edit::UIHandlers::Default, &ForceSplineFollow::m_targetSpeed, "Target Speed", "Speed at which entities in the force region move along a spline") + ->DataElement(AZ::Edit::UIHandlers::Default, &ForceSplineFollow::m_targetSpeed, "Target Speed", + "Speed at which entities in the force region move along a spline.") ->Attribute(AZ::Edit::Attributes::Min, s_forceRegionMinValue) ->Attribute(AZ::Edit::Attributes::Max, s_forceRegionMaxValue) - ->DataElement(AZ::Edit::UIHandlers::Default, &ForceSplineFollow::m_lookAhead, "Lookahead", "Distance at which entities look ahead in their path to reach a point on a spline") + ->DataElement(AZ::Edit::UIHandlers::Default, &ForceSplineFollow::m_lookAhead, "Lookahead", + "Distance at which entities look ahead in their path to reach a point on a spline.") ->Attribute(AZ::Edit::Attributes::Min, s_forceRegionZeroValue) ->Attribute(AZ::Edit::Attributes::Max, s_forceRegionMaxValue) ; @@ -393,10 +398,10 @@ namespace PhysX if (auto editContext = serializeContext->GetEditContext()) { editContext->Class( - "Simple Drag Force", "Simulates a drag force on entities") + "Simple Drag Force", "Simulates a drag force on entities.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->DataElement(AZ::Edit::UIHandlers::Default, &ForceSimpleDrag::m_volumeDensity, "Region Density", "Density of the region") + ->DataElement(AZ::Edit::UIHandlers::Default, &ForceSimpleDrag::m_volumeDensity, "Region Density", "Density of the region.") ->Attribute(AZ::Edit::Attributes::Min, s_forceRegionZeroValue) ->Attribute(AZ::Edit::Attributes::Max, s_forceRegionMaxDensity) ; @@ -463,10 +468,10 @@ namespace PhysX if (auto editContext = serializeContext->GetEditContext()) { editContext->Class( - "Linear Damping Force", "Applies an opposite force to the entity's velocity") + "Linear Damping Force", "Applies an opposite force to the entity's velocity.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->DataElement(AZ::Edit::UIHandlers::Default, &ForceLinearDamping::m_damping, "Damping", "Amount of damping applied to an opposite force") + ->DataElement(AZ::Edit::UIHandlers::Default, &ForceLinearDamping::m_damping, "Damping", "Amount of damping applied to an opposite force.") ->Attribute(AZ::Edit::Attributes::Min, s_forceRegionZeroValue) ->Attribute(AZ::Edit::Attributes::Max, s_forceRegionMaxDamping) ; diff --git a/Gems/PhysX/Code/Source/Joint/Configuration/PhysXJointConfiguration.cpp b/Gems/PhysX/Code/Source/Joint/Configuration/PhysXJointConfiguration.cpp index 530bd03732..ae74859528 100644 --- a/Gems/PhysX/Code/Source/Joint/Configuration/PhysXJointConfiguration.cpp +++ b/Gems/PhysX/Code/Source/Joint/Configuration/PhysXJointConfiguration.cpp @@ -59,22 +59,22 @@ namespace PhysX ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) ->DataElement(AZ::Edit::UIHandlers::Default, &D6JointLimitConfiguration::m_swingLimitY, "Swing limit Y", - "Maximum angle from the Y axis of the joint frame") + "The rotation angle limit around the joint's Y axis.") ->Attribute(AZ::Edit::Attributes::Suffix, " degrees") ->Attribute(AZ::Edit::Attributes::Min, JointConstants::MinSwingLimitDegrees) ->Attribute(AZ::Edit::Attributes::Max, 180.0f) ->DataElement(AZ::Edit::UIHandlers::Default, &D6JointLimitConfiguration::m_swingLimitZ, "Swing limit Z", - "Maximum angle from the Z axis of the joint frame") + "The rotation angle limit around the joint's Z axis.") ->Attribute(AZ::Edit::Attributes::Suffix, " degrees") ->Attribute(AZ::Edit::Attributes::Min, JointConstants::MinSwingLimitDegrees) ->Attribute(AZ::Edit::Attributes::Max, 180.0f) ->DataElement(AZ::Edit::UIHandlers::Default, &D6JointLimitConfiguration::m_twistLimitLower, "Twist lower limit", - "Lower limit for rotation about the X axis of the joint frame") + "The lower rotation angle limit around the joint's X axis.") ->Attribute(AZ::Edit::Attributes::Suffix, " degrees") ->Attribute(AZ::Edit::Attributes::Min, -180.0f) ->Attribute(AZ::Edit::Attributes::Max, 180.0f) ->DataElement(AZ::Edit::UIHandlers::Default, &D6JointLimitConfiguration::m_twistLimitUpper, "Twist upper limit", - "Upper limit for rotation about the X axis of the joint frame") + "The upper rotation angle limit around the joint's X axis.") ->Attribute(AZ::Edit::Attributes::Suffix, " degrees") ->Attribute(AZ::Edit::Attributes::Min, -180.0f) ->Attribute(AZ::Edit::Attributes::Max, 180.0f) diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/API/CharacterController.cpp b/Gems/PhysX/Code/Source/PhysXCharacters/API/CharacterController.cpp index ed9030ac4f..6af08cfaeb 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/API/CharacterController.cpp +++ b/Gems/PhysX/Code/Source/PhysXCharacters/API/CharacterController.cpp @@ -42,16 +42,16 @@ namespace PhysX "PhysX Character Controller Configuration", "PhysX Character Controller Configuration") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->DataElement(AZ::Edit::UIHandlers::ComboBox, &CharacterControllerConfiguration::m_slopeBehaviour, - "Slope Behaviour", "Behaviour of the controller on surfaces above the maximum slope") + "Slope Behavior", "Behavior of the controller on surfaces that exceed the Maximum Slope Angle.") ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree) ->EnumAttribute(SlopeBehaviour::PreventClimbing, "Prevent Climbing") ->EnumAttribute(SlopeBehaviour::ForceSliding, "Force Sliding") ->DataElement(AZ::Edit::UIHandlers::Default, &CharacterControllerConfiguration::m_contactOffset, - "Contact Offset", "Extra distance outside the controller used for smoother contact resolution") + "Contact Offset", "Distance from the controller boundary where contact with surfaces can be resolved.") ->Attribute(AZ::Edit::Attributes::Min, 0.01f) ->Attribute(AZ::Edit::Attributes::Step, 0.01f) ->DataElement(AZ::Edit::UIHandlers::Default, &CharacterControllerConfiguration::m_scaleCoefficient, - "Scale", "Scalar coefficient used to scale the controller, usually slightly smaller than 1") + "Scale", "Scales the controller. Usually less than 1.0 to ensure visual contact between the character and surface.") ->Attribute(AZ::Edit::Attributes::Min, 0.01f) ->Attribute(AZ::Edit::Attributes::Step, 0.01f) ; diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterGameplayComponent.cpp b/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterGameplayComponent.cpp index 4a4f12558d..e86b81359c 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterGameplayComponent.cpp +++ b/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterGameplayComponent.cpp @@ -33,7 +33,7 @@ namespace PhysX "PhysX Character Gameplay Configuration", "PhysX Character Gameplay Configuration") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->DataElement(AZ::Edit::UIHandlers::Default, &CharacterGameplayConfiguration::m_gravityMultiplier, - "Gravity Multiplier", "Multiplier to be combined with the world gravity value for applying character gravity") + "Gravity Multiplier", "Multiplier for global gravity value that applies only to this character entity.") ->Attribute(AZ::Edit::Attributes::Step, 0.1f) ; } diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/Components/EditorCharacterControllerComponent.cpp b/Gems/PhysX/Code/Source/PhysXCharacters/Components/EditorCharacterControllerComponent.cpp index 4e24067dbf..114f9bc2f7 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/Components/EditorCharacterControllerComponent.cpp +++ b/Gems/PhysX/Code/Source/PhysXCharacters/Components/EditorCharacterControllerComponent.cpp @@ -36,18 +36,18 @@ namespace PhysX if (auto editContext = serializeContext->GetEditContext()) { editContext->Class( - "EditorCharacterControllerProxyShapeConfig", "PhysX character controller shape") + "EditorCharacterControllerProxyShapeConfig", "PhysX character controller shape.") ->DataElement(AZ::Edit::UIHandlers::ComboBox, &EditorCharacterControllerProxyShapeConfig::m_shapeType, "Shape", - "The shape associated with the character controller") + "The shape of 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") + "Configuration of box shape.") ->Attribute(AZ::Edit::Attributes::Visibility, &EditorCharacterControllerProxyShapeConfig::IsBoxConfig) ->DataElement(AZ::Edit::UIHandlers::Default, &EditorCharacterControllerProxyShapeConfig::m_capsule, "Capsule", - "Configuration of capsule shape") + "Configuration of capsule shape.") ->Attribute(AZ::Edit::Attributes::Visibility, &EditorCharacterControllerProxyShapeConfig::IsCapsuleConfig) ; } @@ -93,7 +93,8 @@ namespace PhysX if (auto editContext = serializeContext->GetEditContext()) { editContext->Class( - "PhysX Character Controller", "PhysX Character Controller") + "PhysX Character Controller", + "Provides basic character interactions with the physical world, such as preventing movement through other PhysX bodies.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Category, "PhysX") ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/PhysXCharacter.svg") @@ -101,12 +102,12 @@ namespace PhysX ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/physx/character-controller/") ->DataElement(AZ::Edit::UIHandlers::Default, &EditorCharacterControllerComponent::m_configuration, - "Configuration", "Configuration for the character controller") + "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") + "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) diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/Components/EditorCharacterGameplayComponent.cpp b/Gems/PhysX/Code/Source/PhysXCharacters/Components/EditorCharacterGameplayComponent.cpp index 81d158d81c..de4deb442f 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/Components/EditorCharacterGameplayComponent.cpp +++ b/Gems/PhysX/Code/Source/PhysXCharacters/Components/EditorCharacterGameplayComponent.cpp @@ -43,7 +43,7 @@ namespace PhysX if (auto editContext = serializeContext->GetEditContext()) { editContext->Class( - "PhysX Character Gameplay", "PhysX Character Gameplay") + "PhysX Character Gameplay", "An example implementation of character physics behavior such as gravity.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Category, "PhysX") ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/PhysXCharacter.svg") @@ -51,7 +51,7 @@ namespace PhysX ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/physx/character-gameplay/") ->DataElement(AZ::Edit::UIHandlers::Default, &EditorCharacterGameplayComponent::m_gameplayConfig, - "Gameplay Configuration", "Gameplay Configuration") + "Gameplay Configuration", "Gameplay Configuration.") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ; } diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.cpp b/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.cpp index 6fa3bdbffd..57972fea3e 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.cpp +++ b/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.cpp @@ -82,7 +82,7 @@ namespace PhysX if (editContext) { editContext->Class( - "PhysX Ragdoll", "Provides simulation of characters in PhysX.") + "PhysX Ragdoll", "Creates a PhysX ragdoll simulation for an animation actor.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Category, "PhysX") ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/PhysXRagdoll.svg") @@ -91,26 +91,28 @@ namespace PhysX ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/physx/ragdoll/") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(AZ::Edit::UIHandlers::Default, &RagdollComponent::m_positionIterations, "Position Iteration Count", - "A higher iteration count generally improves fidelity at the cost of performance, but note that very high " - "values may lead to severe instability if ragdoll colliders interfere with satisfying joint constraints") + "The frequency at which ragdoll collider positions are resolved. Higher values can increase fidelity but decrease " + "performance. Very high values might introduce instability.") ->Attribute(AZ::Edit::Attributes::Min, 1) ->Attribute(AZ::Edit::Attributes::Max, 255) ->DataElement(AZ::Edit::UIHandlers::Default, &RagdollComponent::m_velocityIterations, "Velocity Iteration Count", - "A higher iteration count generally improves fidelity at the cost of performance, but note that very high " - "values may lead to severe instability if ragdoll colliders interfere with satisfying joint constraints") + "The frequency at which ragdoll collider velocities are resolved. Higher values can increase fidelity but decrease " + "performance. Very high values might introduce instability.") ->Attribute(AZ::Edit::Attributes::Min, 1) ->Attribute(AZ::Edit::Attributes::Max, 255) ->DataElement(AZ::Edit::UIHandlers::Default, &RagdollComponent::m_enableJointProjection, - "Enable Joint Projection", "Whether to use joint projection to preserve joint constraints " - "in demanding situations at the expense of potentially reducing physical correctness") + "Enable Joint Projection", "When active, preserves joint constraints in volatile simulations. " + "Might not be physically correct in all simulations.") ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree) ->DataElement(AZ::Edit::UIHandlers::Default, &RagdollComponent::m_jointProjectionLinearTolerance, - "Joint Projection Linear Tolerance", "Linear joint error above which projection will be applied") + "Joint Projection Linear Tolerance", + "Maximum linear joint error. Projection is applied to linear joint errors above this value.") ->Attribute(AZ::Edit::Attributes::Min, 0.0f) ->Attribute(AZ::Edit::Attributes::Step, 1e-3f) ->Attribute(AZ::Edit::Attributes::Visibility, &RagdollComponent::IsJointProjectionVisible) ->DataElement(AZ::Edit::UIHandlers::Default, &RagdollComponent::m_jointProjectionAngularToleranceDegrees, - "Joint Projection Angular Tolerance", "Angular joint error (in degrees) above which projection will be applied") + "Joint Projection Angular Tolerance", + "Maximum angular joint error. Projection is applied to angular joint errors above this value.") ->Attribute(AZ::Edit::Attributes::Min, 0.0f) ->Attribute(AZ::Edit::Attributes::Step, 0.1f) ->Attribute(AZ::Edit::Attributes::Suffix, " degrees") diff --git a/Gems/PhysX/Code/Source/RigidBody.cpp b/Gems/PhysX/Code/Source/RigidBody.cpp index f8b22affe0..5b36376da5 100644 --- a/Gems/PhysX/Code/Source/RigidBody.cpp +++ b/Gems/PhysX/Code/Source/RigidBody.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -23,6 +24,28 @@ namespace PhysX { + namespace + { + const AZ::Vector3 DefaultCenterOfMass = AZ::Vector3::CreateZero(); + const float DefaultMass = 1.0f; + const AZ::Matrix3x3 DefaultInertiaTensor = AZ::Matrix3x3::CreateIdentity(); + + bool IsSimulationShape(const physx::PxShape& pxShape) + { + return (pxShape.getFlags() & physx::PxShapeFlag::eSIMULATION_SHAPE); + } + + bool CanShapeComputeMassProperties(const physx::PxShape& pxShape) + { + // Note: List based on computeMassAndInertia function in ExtRigidBodyExt.cpp file in PhysX. + const physx::PxGeometryType::Enum geometryType = pxShape.getGeometryType(); + return geometryType == physx::PxGeometryType::eSPHERE + || geometryType == physx::PxGeometryType::eBOX + || geometryType == physx::PxGeometryType::eCAPSULE + || geometryType == physx::PxGeometryType::eCONVEXMESH; + } + } + void RigidBody::Reflect(AZ::ReflectContext* context) { AZ::SerializeContext* serializeContext = azrtti_cast(context); @@ -152,104 +175,120 @@ namespace PhysX m_shapes.erase(found); } - void RigidBody::UpdateMassProperties(AzPhysics::MassComputeFlags flags, const AZ::Vector3* centerOfMassOffsetOverride, const AZ::Matrix3x3* inertiaTensorOverride, const float* massOverride) + void RigidBody::UpdateMassProperties(AzPhysics::MassComputeFlags flags, const AZ::Vector3& centerOfMassOffsetOverride, const AZ::Matrix3x3& inertiaTensorOverride, const float massOverride) { - // Input validation - bool computeCenterOfMass = AzPhysics::MassComputeFlags::COMPUTE_COM == (flags & AzPhysics::MassComputeFlags::COMPUTE_COM); - AZ_Assert(computeCenterOfMass || centerOfMassOffsetOverride, - "UpdateMassProperties: MassComputeFlags::COMPUTE_COM is not set but COM offset is not specified"); - computeCenterOfMass = computeCenterOfMass || !centerOfMassOffsetOverride; + const bool computeCenterOfMass = AzPhysics::MassComputeFlags::COMPUTE_COM == (flags & AzPhysics::MassComputeFlags::COMPUTE_COM); + const bool computeInertiaTensor = AzPhysics::MassComputeFlags::COMPUTE_INERTIA == (flags & AzPhysics::MassComputeFlags::COMPUTE_INERTIA); + const bool computeMass = AzPhysics::MassComputeFlags::COMPUTE_MASS == (flags & AzPhysics::MassComputeFlags::COMPUTE_MASS); + const bool needsCompute = computeCenterOfMass || computeInertiaTensor || computeMass; + const bool includeAllShapesInMassCalculation = AzPhysics::MassComputeFlags::INCLUDE_ALL_SHAPES == (flags & AzPhysics::MassComputeFlags::INCLUDE_ALL_SHAPES); - bool computeInertiaTensor = AzPhysics::MassComputeFlags::COMPUTE_INERTIA == (flags & AzPhysics::MassComputeFlags::COMPUTE_INERTIA); - AZ_Assert(computeInertiaTensor || inertiaTensorOverride, - "UpdateMassProperties: MassComputeFlags::COMPUTE_INERTIA is not set but inertia tensor is not specified"); - computeInertiaTensor = computeInertiaTensor || !inertiaTensorOverride; - - bool computeMass = AzPhysics::MassComputeFlags::COMPUTE_MASS == (flags & AzPhysics::MassComputeFlags::COMPUTE_MASS); - AZ_Assert(computeMass || massOverride, - "UpdateMassProperties: MassComputeFlags::COMPUTE_MASS is not set but mass is not specified"); - computeMass = computeMass || !massOverride; - - AZ::u32 shapesCount = GetShapeCount(); - - // Basic cases when we don't need to compute anything - if (shapesCount == 0 || flags == AzPhysics::MassComputeFlags::NONE) + // Basic case where all properties are set directly. + if (!needsCompute) { - if (massOverride) - { - SetMass(*massOverride); - } - - if (inertiaTensorOverride) - { - SetInertia(*inertiaTensorOverride); - } - - if (centerOfMassOffsetOverride) - { - SetCenterOfMassOffset(*centerOfMassOffsetOverride); - } + SetCenterOfMassOffset(centerOfMassOffsetOverride); + SetMass(massOverride); + SetInertia(inertiaTensorOverride); return; } - // Setup center of mass offset pointer for PxRigidBodyExt::updateMassAndInertia function - AZStd::optional optionalComOverride; - if (!computeCenterOfMass && centerOfMassOffsetOverride) + // If there are no shapes then set the properties directly without computing anything. + if (m_shapes.empty()) { - optionalComOverride = PxMathConvert(*centerOfMassOffsetOverride); - } - - const physx::PxVec3* massLocalPose = optionalComOverride.has_value() ? &optionalComOverride.value() : nullptr; - - bool includeAllShapesInMassCalculation = - AzPhysics::MassComputeFlags::INCLUDE_ALL_SHAPES == (flags & AzPhysics::MassComputeFlags::INCLUDE_ALL_SHAPES); - - // Handle the case when we don't compute mass - if (!computeMass) - { - { - PHYSX_SCENE_WRITE_LOCK(m_pxRigidActor->getScene()); - physx::PxRigidBodyExt::setMassAndUpdateInertia(*m_pxRigidActor, *massOverride, massLocalPose, - includeAllShapesInMassCalculation); - } - - if (!computeInertiaTensor) - { - SetInertia(*inertiaTensorOverride); - } - + SetCenterOfMassOffset(computeCenterOfMass ? DefaultCenterOfMass : centerOfMassOffsetOverride); + SetMass(computeMass ? DefaultMass : massOverride); + SetInertia(computeInertiaTensor ? DefaultInertiaTensor : inertiaTensorOverride); return; } - // Handle the cases when mass should be computed from density - if (shapesCount == 1) + auto cannotComputeMassProperties = [this, includeAllShapesInMassCalculation] { - AZStd::shared_ptr shape = GetShape(0); - float density = shape->GetMaterial()->GetDensity(); + PHYSX_SCENE_READ_LOCK(m_pxRigidActor->getScene()); + return AZStd::any_of(m_shapes.cbegin(), m_shapes.cend(), + [includeAllShapesInMassCalculation](const AZStd::shared_ptr& shape) + { + const physx::PxShape& pxShape = *shape->GetPxShape(); + const bool includeShape = includeAllShapesInMassCalculation || IsSimulationShape(pxShape); - PHYSX_SCENE_WRITE_LOCK(m_pxRigidActor->getScene()); - physx::PxRigidBodyExt::updateMassAndInertia(*m_pxRigidActor, density, massLocalPose, - includeAllShapesInMassCalculation); + return includeShape && !CanShapeComputeMassProperties(pxShape); + }); + }; + + // If contains shapes that cannot compute mass properties (triangle mesh, + // plane or heightfield) then default values will be used. + if (cannotComputeMassProperties()) + { + AZ_Warning("RigidBody", !computeCenterOfMass, + "Rigid body '%s' cannot compute COM because it contains triangle mesh, plane or heightfield shapes, it will default to %s.", + GetName().c_str(), AZ::ToString(DefaultCenterOfMass).c_str()); + AZ_Warning("RigidBody", !computeMass, + "Rigid body '%s' cannot compute Mass because it contains triangle mesh, plane or heightfield shapes, it will default to %0.1f.", + GetName().c_str(), DefaultMass); + AZ_Warning("RigidBody", !computeInertiaTensor, + "Rigid body '%s' cannot compute Inertia because it contains triangle mesh, plane or heightfield shapes, it will default to %s.", + GetName().c_str(), AZ::ToString(DefaultInertiaTensor.RetrieveScale()).c_str()); + + SetCenterOfMassOffset(computeCenterOfMass ? DefaultCenterOfMass : centerOfMassOffsetOverride); + SetMass(computeMass ? DefaultMass : massOverride); + SetInertia(computeInertiaTensor ? DefaultInertiaTensor : inertiaTensorOverride); + return; + } + + // Center of mass needs to be considered first since + // it's needed when computing mass and inertia. + if (computeCenterOfMass) + { + // Compute Center of Mass + UpdateCenterOfMass(includeAllShapesInMassCalculation); } else { - AZStd::vector densities(shapesCount); - for (AZ::u32 i = 0; i < shapesCount; ++i) + SetCenterOfMassOffset(centerOfMassOffsetOverride); + } + const physx::PxVec3 pxCenterOfMass = PxMathConvert(GetCenterOfMassLocal()); + + if (computeMass) + { + // Gather material densities from all shapes, + // mass computation is based on them. + AZStd::vector densities; + densities.reserve(m_shapes.size()); + for (const auto& shape : m_shapes) { - densities[i] = GetShape(i)->GetMaterial()->GetDensity(); + densities.emplace_back(shape->GetMaterial()->GetDensity()); } - PHYSX_SCENE_WRITE_LOCK(m_pxRigidActor->getScene()); - physx::PxRigidBodyExt::updateMassAndInertia(*m_pxRigidActor, densities.data(), - shapesCount, massLocalPose, includeAllShapesInMassCalculation); - } + // Compute Mass + Inertia + { + PHYSX_SCENE_WRITE_LOCK(m_pxRigidActor->getScene()); + physx::PxRigidBodyExt::updateMassAndInertia(*m_pxRigidActor, + densities.data(), static_cast(densities.size()), + &pxCenterOfMass, includeAllShapesInMassCalculation); + } - // Set the overrides if provided. - // Note: We don't set the center of mass here because it was already provided - // to PxRigidBodyExt::updateMassAndInertia above - if (!computeInertiaTensor) + // There is no physx function to only compute the mass without + // computing the inertia. So now that both have been computed + // we can override the inertia if it's suppose to use a + // specific value set by the user. + if (!computeInertiaTensor) + { + SetInertia(inertiaTensorOverride); + } + } + else { - SetInertia(*inertiaTensorOverride); + if (computeInertiaTensor) + { + // Set Mass + Compute Inertia + PHYSX_SCENE_WRITE_LOCK(m_pxRigidActor->getScene()); + physx::PxRigidBodyExt::setMassAndUpdateInertia(*m_pxRigidActor, massOverride, + &pxCenterOfMass, includeAllShapesInMassCalculation); + } + else + { + SetMass(massOverride); + SetInertia(inertiaTensorOverride); + } } } @@ -344,52 +383,49 @@ namespace PhysX } } - void RigidBody::UpdateComputedCenterOfMass() + void RigidBody::UpdateCenterOfMass(bool includeAllShapesInMassCalculation) { - if (m_pxRigidActor) + if (m_shapes.empty()) { - physx::PxU32 shapeCount = 0; + SetCenterOfMassOffset(DefaultCenterOfMass); + return; + } + + AZStd::vector pxShapes; + pxShapes.reserve(m_shapes.size()); + { + // Filter shapes in the same way that updateMassAndInertia function does. + PHYSX_SCENE_READ_LOCK(m_pxRigidActor->getScene()); + for (const auto& shape : m_shapes) { - PHYSX_SCENE_READ_LOCK(m_pxRigidActor->getScene()); - shapeCount = m_pxRigidActor->getNbShapes(); - } - if (shapeCount > 0) - { - AZStd::vector shapes; - shapes.resize(shapeCount); + const physx::PxShape& pxShape = *shape->GetPxShape(); + const bool includeShape = includeAllShapesInMassCalculation || IsSimulationShape(pxShape); + if (includeShape && CanShapeComputeMassProperties(pxShape)) { - PHYSX_SCENE_READ_LOCK(m_pxRigidActor->getScene()); - m_pxRigidActor->getShapes(&shapes[0], shapeCount); + pxShapes.emplace_back(&pxShape); } - - shapes.erase(AZStd::remove_if(shapes.begin() - , shapes.end() - , [](const physx::PxShape* shape) - { - return shape->getFlags() & physx::PxShapeFlag::eTRIGGER_SHAPE; - }) - , shapes.end()); - shapeCount = static_cast(shapes.size()); - - if (shapeCount == 0) - { - SetZeroCenterOfMass(); - return; - } - - const auto properties = physx::PxRigidBodyExt::computeMassPropertiesFromShapes(&shapes[0], shapeCount); - const physx::PxTransform computedCenterOfMass(properties.centerOfMass); - { - PHYSX_SCENE_WRITE_LOCK(m_pxRigidActor->getScene()); - m_pxRigidActor->setCMassLocalPose(computedCenterOfMass); - } - } - else - { - SetZeroCenterOfMass(); } } + + if (pxShapes.empty()) + { + SetCenterOfMassOffset(DefaultCenterOfMass); + return; + } + + const physx::PxMassProperties pxMassProperties = [this, &pxShapes] + { + // Note: PhysX computeMassPropertiesFromShapes function does not use densities + // to compute the shape's masses, which are needed to calculate the center of mass. + // This differs from updateMassAndInertia function, which uses material density values. + // So the masses used during center of mass calculation do not match the masses + // used during mass/inertia calculation. This is an inconsistency in PhysX. + PHYSX_SCENE_READ_LOCK(m_pxRigidActor->getScene()); + return physx::PxRigidBodyExt::computeMassPropertiesFromShapes(pxShapes.data(), static_cast(pxShapes.size())); + }(); + + SetCenterOfMassOffset(PxMathConvert(pxMassProperties.centerOfMass)); } void RigidBody::SetInertia(const AZ::Matrix3x3& inertia) @@ -401,16 +437,6 @@ namespace PhysX } } - void RigidBody::ComputeInertia() - { - if (m_pxRigidActor) - { - PHYSX_SCENE_WRITE_LOCK(m_pxRigidActor->getScene()); - auto localPose = m_pxRigidActor->getCMassLocalPose().p; - physx::PxRigidBodyExt::setMassAndUpdateInertia(*m_pxRigidActor, m_pxRigidActor->getMass(), &localPose); - } - } - AZ::Vector3 RigidBody::GetLinearVelocity() const { if (m_pxRigidActor) @@ -783,13 +809,4 @@ namespace PhysX { return m_name; } - - void RigidBody::SetZeroCenterOfMass() - { - if (m_pxRigidActor) - { - PHYSX_SCENE_WRITE_LOCK(m_pxRigidActor->getScene()); - m_pxRigidActor->setCMassLocalPose(physx::PxTransform(PxMathConvert(AZ::Vector3::CreateZero()))); - } - } } diff --git a/Gems/PhysX/Code/Source/RigidBody.h b/Gems/PhysX/Code/Source/RigidBody.h index 10f2ebb556..a20f79b161 100644 --- a/Gems/PhysX/Code/Source/RigidBody.h +++ b/Gems/PhysX/Code/Source/RigidBody.h @@ -109,17 +109,15 @@ namespace PhysX void RemoveShape(AZStd::shared_ptr shape) override; void UpdateMassProperties(AzPhysics::MassComputeFlags flags = AzPhysics::MassComputeFlags::DEFAULT, - const AZ::Vector3* centerOfMassOffsetOverride = nullptr, - const AZ::Matrix3x3* inertiaTensorOverride = nullptr, - const float* massOverride = nullptr) override; + const AZ::Vector3& centerOfMassOffsetOverride = AZ::Vector3::CreateZero(), + const AZ::Matrix3x3& inertiaTensorOverride = AZ::Matrix3x3::CreateIdentity(), + const float massOverride = 1.0f) override; private: void CreatePhysXActor(const AzPhysics::RigidBodyConfiguration& configuration); - void UpdateComputedCenterOfMass(); - void ComputeInertia(); + void UpdateCenterOfMass(bool includeAllShapesInMassCalculation); void SetInertia(const AZ::Matrix3x3& inertia); - void SetZeroCenterOfMass(); AZStd::shared_ptr m_pxRigidActor; AZStd::vector> m_shapes; diff --git a/Gems/PhysX/Code/Source/Scene/PhysXScene.cpp b/Gems/PhysX/Code/Source/Scene/PhysXScene.cpp index 3b5c98ba2d..86e3ceb98f 100644 --- a/Gems/PhysX/Code/Source/Scene/PhysXScene.cpp +++ b/Gems/PhysX/Code/Source/Scene/PhysXScene.cpp @@ -198,8 +198,8 @@ namespace PhysX AZ_Warning("PhysXScene", shapeAdded, "No Collider or Shape information found when creating Rigid body [%s]", configuration->m_debugName.c_str()); } const AzPhysics::MassComputeFlags& flags = configuration->GetMassComputeFlags(); - newBody->UpdateMassProperties(flags, &configuration->m_centerOfMassOffset, - &configuration->m_inertiaTensor, &configuration->m_mass); + newBody->UpdateMassProperties(flags, configuration->m_centerOfMassOffset, + configuration->m_inertiaTensor, configuration->m_mass); crc = AZ::Crc32(newBody, sizeof(*newBody)); return newBody; diff --git a/Gems/PhysX/Code/Source/SystemComponent.cpp b/Gems/PhysX/Code/Source/SystemComponent.cpp index f56ad43079..2f4d082ccb 100644 --- a/Gems/PhysX/Code/Source/SystemComponent.cpp +++ b/Gems/PhysX/Code/Source/SystemComponent.cpp @@ -101,7 +101,7 @@ namespace PhysX if (AZ::EditContext* editContext = serialize->GetEditContext()) { - editContext->Class("PhysX", "Global PhysX physics configuration") + editContext->Class("PhysX", "Global PhysX physics configuration.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Category, "PhysX") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("System", 0xc94d118b)) diff --git a/Gems/PhysX/Code/Tests/PhysXSpecificTest.cpp b/Gems/PhysX/Code/Tests/PhysXSpecificTest.cpp index 0c0eee3eb0..9e3a738370 100644 --- a/Gems/PhysX/Code/Tests/PhysXSpecificTest.cpp +++ b/Gems/PhysX/Code/Tests/PhysXSpecificTest.cpp @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include #include @@ -1283,11 +1285,13 @@ namespace PhysX EXPECT_TRUE(AZ::IsClose(expectedMass, mass, 0.001f)); } + // Valid material density values: [0.01f, 1e5f] INSTANTIATE_TEST_CASE_P(PhysX, MultiShapesDensityTestFixture, ::testing::Values( - AZStd::make_pair(std::numeric_limits::min(), std::numeric_limits::max()), - AZStd::make_pair(-std::numeric_limits::max(), 0.0f), - AZStd::make_pair(1.0f, 1e9f) + AZStd::make_pair(0.01f, 0.01f), + AZStd::make_pair(1e5f, 1e5f), + AZStd::make_pair(0.01f, 1e5f), + AZStd::make_pair(2364.0f, 10.0f) )); // Fixture for testing extreme density values @@ -1311,6 +1315,7 @@ namespace PhysX && resultingDensity <= Physics::MaterialConfiguration::MaxDensityLimit); } + // Valid material density values: [0.01f, 1e5f] INSTANTIATE_TEST_CASE_P(PhysX, DensityBoundariesTestFixture, ::testing::Values( std::numeric_limits::min(), @@ -1318,7 +1323,9 @@ namespace PhysX -std::numeric_limits::max(), 0.0f, 1.0f, - 1e9f + 1e9f, + 0.01f, + 1e5f )); enum class SimulatedShapesMode @@ -1329,7 +1336,7 @@ namespace PhysX }; class MassComputeFixture - : public ::testing::TestWithParam<::testing::tuple> + : public ::testing::TestWithParam<::testing::tuple> { public: void SetUp() override final @@ -1349,6 +1356,8 @@ namespace PhysX AzPhysics::SimulatedBodyHandle simBodyHandle = sceneInterface->AddSimulatedBody(m_testSceneHandle, &m_rigidBodyConfig); m_rigidBody = azdynamic_cast(sceneInterface->GetSimulatedBodyFromHandle(m_testSceneHandle, simBodyHandle)); } + + ASSERT_TRUE(m_rigidBody != nullptr); } void TearDown() override final @@ -1363,130 +1372,242 @@ namespace PhysX m_rigidBody = nullptr; } - SimulatedShapesMode GetShapesMode() const + Physics::ShapeType GetShapeType() const { return ::testing::get<0>(GetParam()); } - AzPhysics::MassComputeFlags GetMassComputeFlags() const + SimulatedShapesMode GetShapesMode() const { return ::testing::get<1>(GetParam()); } + AzPhysics::MassComputeFlags GetMassComputeFlags() const + { + const AzPhysics::MassComputeFlags massComputeFlags = ::testing::get<2>(GetParam()); + if (IncludeAllShapes()) + { + return massComputeFlags | AzPhysics::MassComputeFlags::INCLUDE_ALL_SHAPES; + } + else + { + return massComputeFlags; + } + } + + bool IncludeAllShapes() const + { + return ::testing::get<3>(GetParam()); + } + bool IsMultiShapeTest() const { - return ::testing::get<2>(GetParam()); + return ::testing::get<4>(GetParam()); } bool IsMassExpectedToChange() const { return m_rigidBodyConfig.m_computeMass && - (!(GetShapesMode() == SimulatedShapesMode::NONE) || m_rigidBodyConfig.m_includeAllShapesInMassCalculation); + (GetShapesMode() != SimulatedShapesMode::NONE || m_rigidBodyConfig.m_includeAllShapesInMassCalculation); } bool IsComExpectedToChange() const { return m_rigidBodyConfig.m_computeCenterOfMass && - (!(GetShapesMode() == SimulatedShapesMode::NONE) || m_rigidBodyConfig.m_includeAllShapesInMassCalculation); + (GetShapesMode() != SimulatedShapesMode::NONE || m_rigidBodyConfig.m_includeAllShapesInMassCalculation); } bool IsInertiaExpectedToChange() const { return m_rigidBodyConfig.m_computeInertiaTensor && - (!(GetShapesMode() == SimulatedShapesMode::NONE) || m_rigidBodyConfig.m_includeAllShapesInMassCalculation); + (GetShapesMode() != SimulatedShapesMode::NONE || m_rigidBodyConfig.m_includeAllShapesInMassCalculation); } + AZStd::shared_ptr CreateShape(const Physics::ColliderConfiguration& colliderConfiguration, Physics::ShapeType shapeType) + { + AZStd::shared_ptr shape; + Physics::System* physics = AZ::Interface::Get(); + switch (shapeType) + { + case Physics::ShapeType::Sphere: + shape = physics->CreateShape(colliderConfiguration, Physics::SphereShapeConfiguration()); + break; + case Physics::ShapeType::Box: + shape = physics->CreateShape(colliderConfiguration, Physics::BoxShapeConfiguration()); + break; + case Physics::ShapeType::Capsule: + shape = physics->CreateShape(colliderConfiguration, Physics::CapsuleShapeConfiguration()); + break; + } + return shape; + }; + AzPhysics::RigidBodyConfiguration m_rigidBodyConfig; - AzPhysics::RigidBody* m_rigidBody; + AzPhysics::RigidBody* m_rigidBody = nullptr; AzPhysics::SceneHandle m_testSceneHandle = AzPhysics::InvalidSceneHandle; }; TEST_P(MassComputeFixture, RigidBody_ComputeMassFlagsCombinationsTwoShapes_MassPropertiesCalculatedAccordingly) { - SimulatedShapesMode shapeMode = GetShapesMode(); - AzPhysics::MassComputeFlags massComputeFlags = GetMassComputeFlags(); - bool multiShapeTest = IsMultiShapeTest(); - Physics::System* physics = AZ::Interface::Get(); + const Physics::ShapeType shapeType = GetShapeType(); + const SimulatedShapesMode shapeMode = GetShapesMode(); + const AzPhysics::MassComputeFlags massComputeFlags = GetMassComputeFlags(); + const bool multiShapeTest = IsMultiShapeTest(); // Save initial values - AZ::Vector3 comBefore = m_rigidBody->GetCenterOfMassWorld(); - AZ::Matrix3x3 inertiaBefore = m_rigidBody->GetInverseInertiaWorld(); - float massBefore = m_rigidBody->GetMass(); + const AZ::Vector3 comBefore = m_rigidBody->GetCenterOfMassWorld(); + const AZ::Matrix3x3 inertiaBefore = m_rigidBody->GetInverseInertiaWorld(); + const float massBefore = m_rigidBody->GetMass(); - // Box shape will be simulated for ALL and MIXED shape modes - Physics::ColliderConfiguration boxColliderConfig; - boxColliderConfig.m_isSimulated = + // Shape will be simulated for ALL and MIXED shape modes + Physics::ColliderConfiguration colliderConfig; + colliderConfig.m_isSimulated = (shapeMode == SimulatedShapesMode::ALL || shapeMode == SimulatedShapesMode::MIXED); - boxColliderConfig.m_position = AZ::Vector3(1.0f, 0.0f, 0.0f); + colliderConfig.m_position = AZ::Vector3(1.0f, 0.0f, 0.0f); - AZStd::shared_ptr boxShape = - physics->CreateShape(boxColliderConfig, Physics::BoxShapeConfiguration()); - m_rigidBody->AddShape(boxShape); + AZStd::shared_ptr shape = CreateShape(colliderConfig, shapeType); + m_rigidBody->AddShape(shape); if (multiShapeTest) { // Sphere shape will be simulated only for the ALL shape mode Physics::ColliderConfiguration sphereColliderConfig; sphereColliderConfig.m_isSimulated = (shapeMode == SimulatedShapesMode::ALL); - sphereColliderConfig.m_position = AZ::Vector3(-1.0f, 0.0f, 0.0f); - AZStd::shared_ptr sphereShape = - physics->CreateShape(sphereColliderConfig, Physics::SphereShapeConfiguration()); + sphereColliderConfig.m_position = AZ::Vector3(-2.0f, 0.0f, 0.0f); + AZStd::shared_ptr sphereShape = CreateShape(sphereColliderConfig, Physics::ShapeType::Sphere); m_rigidBody->AddShape(sphereShape); } // Verify swapping materials results in changes in the mass. - m_rigidBody->UpdateMassProperties(massComputeFlags, &m_rigidBodyConfig.m_centerOfMassOffset, - &m_rigidBodyConfig.m_inertiaTensor, &m_rigidBodyConfig.m_mass); + m_rigidBody->UpdateMassProperties(massComputeFlags, m_rigidBodyConfig.m_centerOfMassOffset, + m_rigidBodyConfig.m_inertiaTensor, m_rigidBodyConfig.m_mass); - float massAfter = m_rigidBody->GetMass(); - AZ::Vector3 comAfter = m_rigidBody->GetCenterOfMassWorld(); - AZ::Matrix3x3 inertiaAfter = m_rigidBody->GetInverseInertiaWorld(); + const float massAfter = m_rigidBody->GetMass(); + const AZ::Vector3 comAfter = m_rigidBody->GetCenterOfMassWorld(); + const AZ::Matrix3x3 inertiaAfter = m_rigidBody->GetInverseInertiaWorld(); + using ::testing::Not; + using ::testing::FloatNear; + using ::UnitTest::IsClose; if (IsMassExpectedToChange()) { - EXPECT_FALSE(AZ::IsClose(massBefore, massAfter, FLT_EPSILON)); + EXPECT_THAT(massBefore, Not(FloatNear(massAfter, FLT_EPSILON))); } else { - EXPECT_TRUE(AZ::IsClose(massBefore, massAfter, FLT_EPSILON)); + EXPECT_THAT(massBefore, FloatNear(massAfter, FLT_EPSILON)); } if (IsComExpectedToChange()) { - EXPECT_FALSE(comBefore.IsClose(comAfter)); + EXPECT_THAT(comBefore, Not(IsClose(comAfter))); } else { - EXPECT_TRUE(comBefore.IsClose(comAfter)); + EXPECT_THAT(comBefore, IsClose(comAfter)); } if (IsInertiaExpectedToChange()) { - EXPECT_FALSE(inertiaBefore.IsClose(inertiaAfter)); + EXPECT_THAT(inertiaBefore, Not(IsClose(inertiaAfter))); } else { - EXPECT_TRUE(inertiaBefore.IsClose(inertiaAfter)); + EXPECT_THAT(inertiaBefore, IsClose(inertiaAfter)); } } - AzPhysics::MassComputeFlags possibleMassComputeFlags[] = { - AzPhysics::MassComputeFlags::NONE, AzPhysics::MassComputeFlags::DEFAULT, AzPhysics::MassComputeFlags::COMPUTE_MASS, - AzPhysics::MassComputeFlags::COMPUTE_COM, AzPhysics::MassComputeFlags::COMPUTE_INERTIA, - AzPhysics::MassComputeFlags::DEFAULT | AzPhysics::MassComputeFlags::INCLUDE_ALL_SHAPES, - AzPhysics::MassComputeFlags::COMPUTE_COM, AzPhysics::MassComputeFlags::COMPUTE_INERTIA, AzPhysics::MassComputeFlags::INCLUDE_ALL_SHAPES, + static const AzPhysics::MassComputeFlags PossibleMassComputeFlags[] = + { + // No compute + AzPhysics::MassComputeFlags::NONE, + + // Compute Mass only + AzPhysics::MassComputeFlags::COMPUTE_MASS, + + // Compute Inertia only + AzPhysics::MassComputeFlags::COMPUTE_INERTIA, + + // Compute COM only + AzPhysics::MassComputeFlags::COMPUTE_COM, + + // Compute combinations of 2 AzPhysics::MassComputeFlags::COMPUTE_MASS | AzPhysics::MassComputeFlags::COMPUTE_COM, - AzPhysics::MassComputeFlags::COMPUTE_MASS | AzPhysics::MassComputeFlags::COMPUTE_COM | AzPhysics::MassComputeFlags::INCLUDE_ALL_SHAPES, AzPhysics::MassComputeFlags::COMPUTE_MASS | AzPhysics::MassComputeFlags::COMPUTE_INERTIA, - AzPhysics::MassComputeFlags::COMPUTE_MASS | AzPhysics::MassComputeFlags::COMPUTE_INERTIA | AzPhysics::MassComputeFlags::INCLUDE_ALL_SHAPES, AzPhysics::MassComputeFlags::COMPUTE_COM | AzPhysics::MassComputeFlags::COMPUTE_INERTIA, - AzPhysics::MassComputeFlags::COMPUTE_COM | AzPhysics::MassComputeFlags::COMPUTE_INERTIA | AzPhysics::MassComputeFlags::INCLUDE_ALL_SHAPES + + // Compute all + AzPhysics::MassComputeFlags::DEFAULT, // COMPUTE_COM | COMPUTE_INERTIA | COMPUTE_MASS }; INSTANTIATE_TEST_CASE_P(PhysX, MassComputeFixture, ::testing::Combine( - ::testing::ValuesIn({ SimulatedShapesMode::NONE, SimulatedShapesMode::MIXED, SimulatedShapesMode::ALL }), - ::testing::ValuesIn(possibleMassComputeFlags), - ::testing::Bool())); + ::testing::ValuesIn({ Physics::ShapeType::Sphere, Physics::ShapeType::Box, Physics::ShapeType::Capsule }), // Values for GetShapeType() + ::testing::ValuesIn({ SimulatedShapesMode::NONE, SimulatedShapesMode::MIXED, SimulatedShapesMode::ALL }), // Values for GetShapesMode() + ::testing::ValuesIn(PossibleMassComputeFlags), // Values for GetMassComputeFlags() + ::testing::Bool(), // Values for IncludeAllShapes() + ::testing::Bool())); // Values for IsMultiShapeTest() + class MassPropertiesWithTriangleMesh + : public ::testing::TestWithParam + { + public: + void SetUp() override + { + if (auto* physicsSystem = AZ::Interface::Get()) + { + AzPhysics::SceneConfiguration sceneConfiguration = physicsSystem->GetDefaultSceneConfiguration(); + sceneConfiguration.m_sceneName = AzPhysics::DefaultPhysicsSceneName; + m_testSceneHandle = physicsSystem->AddScene(sceneConfiguration); + } + } + + void TearDown() override + { + // Clean up the Test scene + if (auto* physicsSystem = AZ::Interface::Get()) + { + physicsSystem->RemoveScene(m_testSceneHandle); + } + m_testSceneHandle = AzPhysics::InvalidSceneHandle; + } + + AzPhysics::MassComputeFlags GetMassComputeFlags() const + { + return GetParam(); + } + + AzPhysics::SceneHandle m_testSceneHandle = AzPhysics::InvalidSceneHandle; + }; + + TEST_P(MassPropertiesWithTriangleMesh, KinematicRigidBody_ComputeMassProperties_TriggersWarnings) + { + const AzPhysics::MassComputeFlags flags = GetMassComputeFlags(); + + const bool doesComputeCenterOfMass = AzPhysics::MassComputeFlags::COMPUTE_COM == (flags & AzPhysics::MassComputeFlags::COMPUTE_COM); + const bool doesComputeMass = AzPhysics::MassComputeFlags::COMPUTE_MASS == (flags & AzPhysics::MassComputeFlags::COMPUTE_MASS); + const bool doesComputeInertia = AzPhysics::MassComputeFlags::COMPUTE_INERTIA == (flags & AzPhysics::MassComputeFlags::COMPUTE_INERTIA); + + UnitTest::ErrorHandler computeCenterOfMassWarningHandler( + "cannot compute COM"); + UnitTest::ErrorHandler computeMassWarningHandler( + "cannot compute Mass"); + UnitTest::ErrorHandler computeIneriaWarningHandler( + "cannot compute Inertia"); + + AzPhysics::SimulatedBodyHandle rigidBodyhandle = TestUtils::AddKinematicTriangleMeshCubeToScene(m_testSceneHandle, 3.0f, flags); + + EXPECT_TRUE(rigidBodyhandle != AzPhysics::InvalidSimulatedBodyHandle); + EXPECT_EQ(computeCenterOfMassWarningHandler.GetExpectedWarningCount(), doesComputeCenterOfMass ? 1 : 0); + EXPECT_EQ(computeMassWarningHandler.GetExpectedWarningCount(), doesComputeMass ? 1 : 0); + EXPECT_EQ(computeIneriaWarningHandler.GetExpectedWarningCount(), doesComputeInertia ? 1 : 0); + + if (auto* sceneInterface = AZ::Interface::Get()) + { + sceneInterface->RemoveSimulatedBody(m_testSceneHandle, rigidBodyhandle); + } + } + + INSTANTIATE_TEST_CASE_P(PhysX, MassPropertiesWithTriangleMesh, + ::testing::ValuesIn(PossibleMassComputeFlags)); // Values for GetMassComputeFlags() } // namespace PhysX diff --git a/Gems/PhysX/Code/Tests/PhysXTestCommon.cpp b/Gems/PhysX/Code/Tests/PhysXTestCommon.cpp index 6186db6f1e..67fe67bc23 100644 --- a/Gems/PhysX/Code/Tests/PhysXTestCommon.cpp +++ b/Gems/PhysX/Code/Tests/PhysXTestCommon.cpp @@ -253,6 +253,36 @@ namespace PhysX return AzPhysics::InvalidSimulatedBodyHandle; } + AzPhysics::SimulatedBodyHandle AddKinematicTriangleMeshCubeToScene(AzPhysics::SceneHandle scene, float halfExtent, AzPhysics::MassComputeFlags massComputeFlags) + { + // Generate input data + VertexIndexData cubeMeshData = GenerateCubeMeshData(halfExtent); + AZStd::vector cookedData; + bool cookingResult = false; + Physics::SystemRequestBus::BroadcastResult(cookingResult, &Physics::SystemRequests::CookTriangleMeshToMemory, + cubeMeshData.first.data(), static_cast(cubeMeshData.first.size()), + cubeMeshData.second.data(), static_cast(cubeMeshData.second.size()), + cookedData); + AZ_Assert(cookingResult, "Failed to cook the cube mesh."); + + // Setup shape & collider configurations + auto shapeConfig = AZStd::make_shared(); + shapeConfig->SetCookedMeshData(cookedData.data(), cookedData.size(), + Physics::CookedMeshShapeConfiguration::MeshType::TriangleMesh); + + AzPhysics::RigidBodyConfiguration rigidBodyConfiguration; + rigidBodyConfiguration.m_kinematic = true; + rigidBodyConfiguration.SetMassComputeFlags(massComputeFlags); + rigidBodyConfiguration.m_colliderAndShapeData = AzPhysics::ShapeColliderPair( + AZStd::make_shared(), shapeConfig); + + if (auto* sceneInterface = AZ::Interface::Get()) + { + return sceneInterface->AddSimulatedBody(scene, &rigidBodyConfiguration); + } + return AzPhysics::InvalidSimulatedBodyHandle; + } + void SetCollisionLayer(EntityPtr& entity, const AZStd::string& layerName, const AZStd::string& colliderTag) { Physics::CollisionFilteringRequestBus::Event(entity->GetId(), &Physics::CollisionFilteringRequests::SetCollisionLayer, layerName, AZ::Crc32(colliderTag.c_str())); diff --git a/Gems/PhysX/Code/Tests/PhysXTestCommon.h b/Gems/PhysX/Code/Tests/PhysXTestCommon.h index ef12ca8350..95859a3f86 100644 --- a/Gems/PhysX/Code/Tests/PhysXTestCommon.h +++ b/Gems/PhysX/Code/Tests/PhysXTestCommon.h @@ -89,6 +89,7 @@ namespace PhysX const AzPhysics::CollisionLayer& layer = AzPhysics::CollisionLayer::Default); AzPhysics::SimulatedBodyHandle AddStaticTriangleMeshCubeToScene(AzPhysics::SceneHandle scene, float halfExtent); + AzPhysics::SimulatedBodyHandle AddKinematicTriangleMeshCubeToScene(AzPhysics::SceneHandle scene, float halfExtent, AzPhysics::MassComputeFlags massComputeFlags); // Collision Filtering void SetCollisionLayer(EntityPtr& entity, const AZStd::string& layerName, const AZStd::string& colliderTag = ""); diff --git a/Gems/WhiteBox/Code/Source/Components/WhiteBoxColliderComponent.cpp b/Gems/WhiteBox/Code/Source/Components/WhiteBoxColliderComponent.cpp index a7e6b6c707..18bddfa26f 100644 --- a/Gems/WhiteBox/Code/Source/Components/WhiteBoxColliderComponent.cpp +++ b/Gems/WhiteBox/Code/Source/Components/WhiteBoxColliderComponent.cpp @@ -91,6 +91,11 @@ namespace WhiteBox bodyConfiguration.m_position = worldTransform.GetTranslation(); bodyConfiguration.m_kinematic = true; // note: this field is ignored in the WhiteBoxBodyType::Static case bodyConfiguration.m_colliderAndShapeData = shape; + // Since the shape used is a triangle mesh the COM, Mass and Inertia + // cannot be computed. Disable them to use default values. + bodyConfiguration.m_computeCenterOfMass = false; + bodyConfiguration.m_computeMass = false; + bodyConfiguration.m_computeInertiaTensor = false; m_simulatedBodyHandle = sceneInterface->AddSimulatedBody(defaultScene, &bodyConfiguration); } break;