diff --git a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_Optimized.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_Optimized.py index 31b4cb687d..447a4ebac9 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_Optimized.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_Optimized.py @@ -67,5 +67,9 @@ class TestAutomation(EditorTestSuite): class AtomEditorComponents_PostFXLayerAdded(EditorSharedTest): from Atom.tests import hydra_AtomEditorComponents_PostFXLayerAdded as test_module + @pytest.mark.test_case_id("C36525665") + class AtomEditorComponents_PostFXShapeWeightModifierAdded(EditorSharedTest): + from Atom.tests import hydra_AtomEditorComponents_PostFxShapeWeightModifierAdded as test_module + class ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges(EditorSharedTest): from Atom.tests import hydra_ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges as test_module diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFxShapeWeightModifierAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFxShapeWeightModifierAdded.py new file mode 100644 index 0000000000..4c98bcd130 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFxShapeWeightModifierAdded.py @@ -0,0 +1,207 @@ +""" +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 +""" + +class Tests: + creation_undo = ( + "UNDO Entity creation success", + "UNDO Entity creation failed") + creation_redo = ( + "REDO Entity creation success", + "REDO Entity creation failed") + postfx_shape_weight_creation = ( + "PostFx Shape Weight Modifier Entity successfully created", + "PostFx Shape Weight Modifier Entity failed to be created") + postfx_shape_weight_component = ( + "Entity has a PostFx Shape Weight Modifier component", + "Entity failed to find PostFx Shape Weight Modifier component") + postfx_shape_weight_disabled = ( + "PostFx Shape Weight Modifier component disabled", + "PostFx Shape Weight Modifier component was not disabled.") + postfx_layer_component = ( + "Entity has a PostFX Layer component", + "Entity did not have an PostFX Layer component") + tube_shape_component = ( + "Entity has a Tube Shape component", + "Entity did not have a Tube Shape component") + postfx_shape_weight_enabled = ( + "PostFx Shape Weight Modifier component enabled", + "PostFx Shape Weight Modifier component was not enabled.") + enter_game_mode = ( + "Entered game mode", + "Failed to enter game mode") + exit_game_mode = ( + "Exited game mode", + "Couldn't exit game mode") + is_visible = ( + "Entity is visible", + "Entity was not visible") + is_hidden = ( + "Entity is hidden", + "Entity was not hidden") + entity_deleted = ( + "Entity deleted", + "Entity was not deleted") + deletion_undo = ( + "UNDO deletion success", + "UNDO deletion failed") + deletion_redo = ( + "REDO deletion success", + "REDO deletion failed") + + +def AtomEditorComponents_postfx_shape_weight_AddedToEntity(): + """ + Summary: + Tests the PostFx Shape Weight Modifier component can be added to an entity and has the expected functionality. + + Test setup: + - Wait for Editor idle loop. + - Open the "Base" level. + + Expected Behavior: + The component can be added, used in game mode, hidden/shown, deleted, and has accurate required components. + Creation and deletion undo/redo should also work. + + Test Steps: + 1) Create a PostFx Shape Weight Modifier entity with no components. + 2) Add a PostFx Shape Weight Modifier component to PostFx Shape Weight Modifier entity. + 3) UNDO the entity creation and component addition. + 4) REDO the entity creation and component addition. + 5) Verify PostFx Shape Weight Modifier component not enabled. + 6) Add PostFX Layer component since it is required by the PostFx Shape Weight Modifier component. + 7) Verify PostFx Shape Weight Modifier component is NOT enabled since it also requires a shape. + 8) Add a required shape looping over a list and checking if it enables PostFX Shape Weight Modifier. + 9) Undo to remove each added shape and verify PostFX Shape Weight Modifier is not enabled. + 10) Verify PostFx Shape Weight Modifier component is enabled by adding Spline and Tube Shape component. + 11) Enter/Exit game mode. + 12) Test IsHidden. + 13) Test IsVisible. + 14) Delete PostFx Shape Weight Modifier entity. + 15) UNDO deletion. + 16) REDO deletion. + 17) Look for errors. + + :return: None + """ + + import azlmbr.legacy.general as general + + from editor_python_test_tools.editor_entity_utils import EditorEntity + from editor_python_test_tools.utils import Report, Tracer, TestHelper + + with Tracer() as error_tracer: + # Test setup begins. + # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. + TestHelper.init_idle() + TestHelper.open_level("", "Base") + + # Test steps begin. + # 1. Create a PostFx Shape Weight Modifier entity with no components. + postfx_shape_weight_name = "PostFX Shape Weight Modifier" + postfx_shape_weight_entity = EditorEntity.create_editor_entity(postfx_shape_weight_name) + Report.critical_result(Tests.postfx_shape_weight_creation, postfx_shape_weight_entity.exists()) + + # 2. Add a PostFx Shape Weight Modifier component to PostFx Shape Weight Modifier entity. + postfx_shape_weight_component = postfx_shape_weight_entity.add_component(postfx_shape_weight_name) + Report.critical_result( + Tests.postfx_shape_weight_component, + postfx_shape_weight_entity.has_component(postfx_shape_weight_name)) + + # 3. UNDO the entity creation and component addition. + # -> UNDO component addition. + general.undo() + # -> UNDO naming entity. + general.undo() + # -> UNDO selecting entity. + general.undo() + # -> UNDO entity creation. + general.undo() + general.idle_wait_frames(1) + Report.result(Tests.creation_undo, not postfx_shape_weight_entity.exists()) + + # 4. REDO the entity creation and component addition. + # -> REDO entity creation. + general.redo() + # -> REDO selecting entity. + general.redo() + # -> REDO naming entity. + general.redo() + # -> REDO component addition. + general.redo() + general.idle_wait_frames(1) + Report.result(Tests.creation_redo, postfx_shape_weight_entity.exists()) + + # 5. Verify PostFx Shape Weight Modifier component not enabled. + Report.result(Tests.postfx_shape_weight_disabled, not postfx_shape_weight_component.is_enabled()) + + # 6. Add PostFX Layer component since it is required by the PostFx Shape Weight Modifier component. + postfx_layer_name = "PostFX Layer" + postfx_shape_weight_entity.add_component(postfx_layer_name) + Report.result(Tests.postfx_layer_component, postfx_shape_weight_entity.has_component(postfx_layer_name)) + + # 7. Verify PostFx Shape Weight Modifier component is NOT enabled since it also requires a shape. + Report.result(Tests.postfx_shape_weight_disabled, not postfx_shape_weight_component.is_enabled()) + + # 8. Add a required shape looping over a list and checking if it enables PostFX Shape Weight Modifier. + for shape in ['Axis Aligned Box Shape', 'Box Shape', 'Capsule Shape', 'Compound Shape', 'Cylinder Shape', + 'Disk Shape', 'Polygon Prism Shape', 'Quad Shape', 'Sphere Shape', 'Vegetation Reference Shape']: + postfx_shape_weight_entity.add_component(shape) + test_shape = ( + f"Entity has a {shape} component", + f"Entity did not have a {shape} component") + Report.result(test_shape, postfx_shape_weight_entity.has_component(shape)) + + # Check if required shape allows PostFX Shape Weight Modifier to be enabled + Report.result(Tests.postfx_shape_weight_enabled, postfx_shape_weight_component.is_enabled()) + + # 9. Undo to remove each added shape and verify PostFX Shape Weight Modifier is not enabled. + general.undo() + TestHelper.wait_for_condition(lambda: not postfx_shape_weight_entity.has_component(shape), 1.0) + Report.result(Tests.postfx_shape_weight_disabled, not postfx_shape_weight_component.is_enabled()) + + # 10. Verify PostFx Shape Weight Modifier component is enabled by adding Spline and Tube Shape component. + postfx_shape_weight_entity.add_components(['Spline', 'Tube Shape']) + Report.result(Tests.tube_shape_component, postfx_shape_weight_entity.has_component('Tube Shape')) + Report.result(Tests.postfx_shape_weight_enabled, postfx_shape_weight_component.is_enabled()) + + # 11. Enter/Exit game mode. + TestHelper.enter_game_mode(Tests.enter_game_mode) + general.idle_wait_frames(1) + TestHelper.exit_game_mode(Tests.exit_game_mode) + + # 12. Test IsHidden. + postfx_shape_weight_entity.set_visibility_state(False) + Report.result(Tests.is_hidden, postfx_shape_weight_entity.is_hidden() is True) + + # 13. Test IsVisible. + postfx_shape_weight_entity.set_visibility_state(True) + general.idle_wait_frames(1) + Report.result(Tests.is_visible, postfx_shape_weight_entity.is_visible() is True) + + # 14. Delete PostFx Shape Weight Modifier entity. + postfx_shape_weight_entity.delete() + Report.result(Tests.entity_deleted, not postfx_shape_weight_entity.exists()) + + # 15. UNDO deletion. + general.undo() + Report.result(Tests.deletion_undo, postfx_shape_weight_entity.exists()) + + # 16. REDO deletion. + general.redo() + Report.result(Tests.deletion_redo, not postfx_shape_weight_entity.exists()) + + # 17. Look for errors or asserts. + TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0) + for error_info in error_tracer.errors: + Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}") + for assert_info in error_tracer.asserts: + Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}") + + +if __name__ == "__main__": + from editor_python_test_tools.utils import Report + Report.start_test(AtomEditorComponents_postfx_shape_weight_AddedToEntity) diff --git a/AutomatedTesting/Objects/MorphTargets/DisplayWrinkleMaskBlendValues.material b/AutomatedTesting/Objects/MorphTargets/DisplayWrinkleMaskBlendValues.material new file mode 100644 index 0000000000..878b3ac39f --- /dev/null +++ b/AutomatedTesting/Objects/MorphTargets/DisplayWrinkleMaskBlendValues.material @@ -0,0 +1,13 @@ +{ + "description": "", + "materialType": "Materials/Types/Skin.materialtype", + "parentMaterial": "", + "propertyLayoutVersion": 3, + "properties": { + "wrinkleLayers": { + "count": 3, + "enable": true, + "showBlendValues": true + } + } +} diff --git a/AutomatedTesting/Objects/MorphTargets/morphActor.fbx b/AutomatedTesting/Objects/MorphTargets/morphActor.fbx new file mode 100644 index 0000000000..ffb75e680e --- /dev/null +++ b/AutomatedTesting/Objects/MorphTargets/morphActor.fbx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:53e17ec8155911c8b42e85436130f600bd6dddd8931a8ccb1b2f8a9f8674cc85 +size 45104 diff --git a/AutomatedTesting/Objects/MorphTargets/morphActor_wrinklemasks/Morph1_wrinklemask.tif b/AutomatedTesting/Objects/MorphTargets/morphActor_wrinklemasks/Morph1_wrinklemask.tif new file mode 100644 index 0000000000..3bc18cf450 --- /dev/null +++ b/AutomatedTesting/Objects/MorphTargets/morphActor_wrinklemasks/Morph1_wrinklemask.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0da56a05daa0ec1c476cfe25ca6d3b65267c98886cf33408f6e852fb325a8e2c +size 198084 diff --git a/AutomatedTesting/Objects/MorphTargets/morphActor_wrinklemasks/Morph2_wriklemask.tif b/AutomatedTesting/Objects/MorphTargets/morphActor_wrinklemasks/Morph2_wriklemask.tif new file mode 100644 index 0000000000..39e70f5acf --- /dev/null +++ b/AutomatedTesting/Objects/MorphTargets/morphActor_wrinklemasks/Morph2_wriklemask.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e3537fbe9205731a242251c525a67bbb5f3b8f5307537f1dc0c318b5b885ce52 +size 198112 diff --git a/AutomatedTesting/Objects/MorphTargets/morphActor_wrinklemasks/Morph3_wrinklemask.tif b/AutomatedTesting/Objects/MorphTargets/morphActor_wrinklemasks/Morph3_wrinklemask.tif new file mode 100644 index 0000000000..43e19f0d5f --- /dev/null +++ b/AutomatedTesting/Objects/MorphTargets/morphActor_wrinklemasks/Morph3_wrinklemask.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bd794d5dd4b749c3275bfab79b9b5ae3f8e007d3e6741c0566c9c2d3931123bf +size 198112 diff --git a/AutomatedTesting/Objects/MorphTargets/morphAnimation.fbx b/AutomatedTesting/Objects/MorphTargets/morphAnimation.fbx new file mode 100644 index 0000000000..c0dcc007dc --- /dev/null +++ b/AutomatedTesting/Objects/MorphTargets/morphAnimation.fbx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:45ded862987a64061deffd8e4c9aa1dff4eec3bcff5f7b505679f1959e8ae137 +size 51440 diff --git a/Code/Editor/CryEdit.cpp b/Code/Editor/CryEdit.cpp index abba2bb9c6..4f2e995ac4 100644 --- a/Code/Editor/CryEdit.cpp +++ b/Code/Editor/CryEdit.cpp @@ -57,6 +57,7 @@ AZ_POP_DISABLE_WARNING #include #include #include +#include // AzToolsFramework #include @@ -3021,6 +3022,15 @@ CCryEditApp::ECreateLevelResult CCryEditApp::CreateLevel(const QString& levelNam bool bIsDocModified = GetIEditor()->GetDocument()->IsModified(); OnSwitchPhysics(); GetIEditor()->GetDocument()->SetModifiedFlag(bIsDocModified); + + if (usePrefabSystemForLevels) + { + auto* rootSpawnableInterface = AzFramework::RootSpawnableInterface::Get(); + if (rootSpawnableInterface) + { + rootSpawnableInterface->ProcessSpawnableQueue(); + } + } } const QScopedValueRollback rollback(m_creatingNewLevel); 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/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/Editor/Util/FileUtil.cpp b/Code/Editor/Util/FileUtil.cpp index eeb6912acf..baca69d628 100644 --- a/Code/Editor/Util/FileUtil.cpp +++ b/Code/Editor/Util/FileUtil.cpp @@ -1195,7 +1195,7 @@ bool CFileUtil::IsFileExclusivelyAccessable(const QString& strFilePath) ////////////////////////////////////////////////////////////////////////// bool CFileUtil::CreatePath(const QString& strPath) { -#if defined(AZ_PLATFORM_MAC) +#if !AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS bool pathCreated = true; QString cleanPath = QDir::cleanPath(strPath); @@ -1252,7 +1252,7 @@ bool CFileUtil::CreatePath(const QString& strPath) } return true; -#endif +#endif // !AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS } ////////////////////////////////////////////////////////////////////////// diff --git a/Code/Framework/AzCore/AzCore/Math/IntersectSegment.cpp b/Code/Framework/AzCore/AzCore/Math/IntersectSegment.cpp index a7a0d5197e..5d13acc34c 100644 --- a/Code/Framework/AzCore/AzCore/Math/IntersectSegment.cpp +++ b/Code/Framework/AzCore/AzCore/Math/IntersectSegment.cpp @@ -15,7 +15,7 @@ using namespace Intersect; // IntersectSegmentTriangleCCW // [10/21/2009] //========================================================================= -int Intersect::IntersectSegmentTriangleCCW( +bool Intersect::IntersectSegmentTriangleCCW( const Vector3& p, const Vector3& q, const Vector3& a, const Vector3& b, const Vector3& c, /*float &u, float &v, float &w,*/ Vector3& normal, float& t) { @@ -34,7 +34,7 @@ int Intersect::IntersectSegmentTriangleCCW( float d = qp.Dot(normal); if (d <= 0.0f) { - return 0; + return false; } // Compute intersection t value of pq with plane of triangle. A ray @@ -46,7 +46,7 @@ int Intersect::IntersectSegmentTriangleCCW( // range segment check t[0,1] (it this case [0,d]) if (t < 0.0f || t > d) { - return 0; + return false; } // Compute barycentric coordinate components and test if within bounds @@ -54,12 +54,12 @@ int Intersect::IntersectSegmentTriangleCCW( v = ac.Dot(e); if (v < 0.0f || v > d) { - return 0; + return false; } w = -ab.Dot(e); if (w < 0.0f || v + w > d) { - return 0; + return false; } // Segment/ray intersects triangle. Perform delayed division and @@ -72,14 +72,14 @@ int Intersect::IntersectSegmentTriangleCCW( normal.Normalize(); - return 1; + return true; } //========================================================================= // IntersectSegmentTriangle // [10/21/2009] //========================================================================= -int +bool Intersect::IntersectSegmentTriangle( const Vector3& p, const Vector3& q, const Vector3& a, const Vector3& b, const Vector3& c, /*float &u, float &v, float &w,*/ Vector3& normal, float& t) @@ -111,7 +111,7 @@ Intersect::IntersectSegmentTriangle( // so either have a parallel ray or our normal is flipped if (d >= -Constants::FloatEpsilon) { - return 0; // parallel + return false; // parallel } d = -d; e = ap.Cross(qp); @@ -125,19 +125,19 @@ Intersect::IntersectSegmentTriangle( // range segment check t[0,1] (it this case [0,d]) if (t < 0.0f || t > d) { - return 0; + return false; } // Compute barycentric coordinate components and test if within bounds v = ac.Dot(e); if (v < 0.0f || v > d) { - return 0; + return false; } w = -ab.Dot(e); if (w < 0.0f || v + w > d) { - return 0; + return false; } // Segment/ray intersects the triangle. Perform delayed division and @@ -150,14 +150,14 @@ Intersect::IntersectSegmentTriangle( normal.Normalize(); - return 1; + return true; } //========================================================================= // TestSegmentAABBOrigin // [10/21/2009] //========================================================================= -int +bool AZ::Intersect::TestSegmentAABBOrigin(const Vector3& midPoint, const Vector3& halfVector, const Vector3& aabbExtends) { const Vector3 EPSILON(0.001f); // \todo this is slow load move to a const @@ -168,7 +168,7 @@ AZ::Intersect::TestSegmentAABBOrigin(const Vector3& midPoint, const Vector3& hal // Try world coordinate axes as separating axes if (!absMidpoint.IsLessEqualThan(absHalfMidpoint)) { - return 0; + return false; } // Add in an epsilon term to counteract arithmetic errors when segment is @@ -188,11 +188,11 @@ AZ::Intersect::TestSegmentAABBOrigin(const Vector3& midPoint, const Vector3& hal Vector3 ead(ey * adz + ez * ady, ex * adz + ez * adx, ex * ady + ey * adx); if (!absMDCross.IsLessEqualThan(ead)) { - return 0; + return false; } // No separating axis found; segment must be overlapping AABB - return 1; + return true; } @@ -200,7 +200,7 @@ AZ::Intersect::TestSegmentAABBOrigin(const Vector3& midPoint, const Vector3& hal // IntersectRayAABB // [10/21/2009] //========================================================================= -int +RayAABBIsectTypes AZ::Intersect::IntersectRayAABB( const Vector3& rayStart, const Vector3& dir, const Vector3& dirRCP, const Aabb& aabb, float& tStart, float& tEnd, Vector3& startNormal /*, Vector3& inter*/) @@ -356,7 +356,7 @@ AZ::Intersect::IntersectRayAABB( // IntersectRayAABB2 // [2/18/2011] //========================================================================= -int +RayAABBIsectTypes AZ::Intersect::IntersectRayAABB2(const Vector3& rayStart, const Vector3& dirRCP, const Aabb& aabb, float& start, float& end) { float tmin, tmax, tymin, tymax, tzmin, tzmax; @@ -408,7 +408,7 @@ AZ::Intersect::IntersectRayAABB2(const Vector3& rayStart, const Vector3& dirRCP, return ISECT_RAY_AABB_ISECT; } -int AZ::Intersect::IntersectRayDisk( +bool AZ::Intersect::IntersectRayDisk( const Vector3& rayOrigin, const Vector3& rayDir, const Vector3& diskCenter, const float diskRadius, const Vector3& diskNormal, float& t) { // First intersect with the plane of the disk @@ -421,10 +421,10 @@ int AZ::Intersect::IntersectRayDisk( if (pointOnPlane.GetDistance(diskCenter) < diskRadius) { t = planeIntersectionDistance; - return 1; + return true; } } - return 0; + return false; } // Reference: Real-Time Collision Detection - 5.3.7 Intersecting Ray or Segment Against Cylinder, and the book's errata. @@ -1012,7 +1012,7 @@ int AZ::Intersect::IntersectRayQuad( } // reference: Real-Time Collision Detection, 5.3.3 Intersecting Ray or Segment Against Box -int AZ::Intersect::IntersectRayBox( +bool AZ::Intersect::IntersectRayBox( const Vector3& rayOrigin, const Vector3& rayDir, const Vector3& boxCenter, const Vector3& boxAxis1, const Vector3& boxAxis2, const Vector3& boxAxis3, float boxHalfExtent1, float boxHalfExtent2, float boxHalfExtent3, float& t) { @@ -1044,7 +1044,7 @@ int AZ::Intersect::IntersectRayBox( // If the ray is parallel to the slab and the ray origin is outside, return no intersection. if (tp < 0.0f || tn < 0.0f) { - return 0; + return false; } } else @@ -1065,7 +1065,7 @@ int AZ::Intersect::IntersectRayBox( tmax = AZ::GetMin(tmax, t2); if (tmin > tmax) { - return 0; + return false; } } @@ -1085,7 +1085,7 @@ int AZ::Intersect::IntersectRayBox( // If the ray is parallel to the slab and the ray origin is outside, return no intersection. if (tp < 0.0f || tn < 0.0f) { - return 0; + return false; } } else @@ -1106,7 +1106,7 @@ int AZ::Intersect::IntersectRayBox( tmax = AZ::GetMin(tmax, t2); if (tmin > tmax) { - return 0; + return false; } } @@ -1126,7 +1126,7 @@ int AZ::Intersect::IntersectRayBox( // If the ray is parallel to the slab and the ray origin is outside, return no intersection. if (tp < 0.0f || tn < 0.0f) { - return 0; + return false; } } else @@ -1147,15 +1147,15 @@ int AZ::Intersect::IntersectRayBox( tmax = AZ::GetMin(tmax, t2); if (tmin > tmax) { - return 0; + return false; } } t = (isRayOriginInsideBox ? tmax : tmin); - return 1; + return true; } -int AZ::Intersect::IntersectRayObb(const Vector3& rayOrigin, const Vector3& rayDir, const Obb& obb, float& t) +bool AZ::Intersect::IntersectRayObb(const Vector3& rayOrigin, const Vector3& rayDir, const Obb& obb, float& t) { return AZ::Intersect::IntersectRayBox(rayOrigin, rayDir, obb.GetPosition(), obb.GetAxisX(), obb.GetAxisY(), obb.GetAxisZ(), @@ -1166,7 +1166,7 @@ int AZ::Intersect::IntersectRayObb(const Vector3& rayOrigin, const Vector3& rayD // IntersectSegmentCylinder // [10/21/2009] //========================================================================= -int +CylinderIsectTypes AZ::Intersect::IntersectSegmentCylinder( const Vector3& sa, const Vector3& dir, const Vector3& p, const Vector3& q, const float r, float& t) { @@ -1225,7 +1225,7 @@ AZ::Intersect::IntersectSegmentCylinder( return RR_ISECT_RAY_CYL_NONE; // No real roots; no intersection } t = (-b - Sqrt(discr)) / a; - int result = RR_ISECT_RAY_CYL_PQ; // default along the PQ segment + CylinderIsectTypes result = RR_ISECT_RAY_CYL_PQ; // default along the PQ segment if (md + t * nd < 0.0f) { @@ -1294,7 +1294,7 @@ AZ::Intersect::IntersectSegmentCylinder( // IntersectSegmentCapsule // [10/21/2009] //========================================================================= -int +CapsuleIsectTypes AZ::Intersect::IntersectSegmentCapsule(const Vector3& sa, const Vector3& dir, const Vector3& p, const Vector3& q, const float r, float& t) { int result = IntersectSegmentCylinder(sa, dir, p, q, r, t); @@ -1361,13 +1361,13 @@ AZ::Intersect::IntersectSegmentCapsule(const Vector3& sa, const Vector3& dir, co // IntersectSegmentPolyhedron // [10/21/2009] //========================================================================= -int +bool AZ::Intersect::IntersectSegmentPolyhedron( - const Vector3& sa, const Vector3& sBA, const Plane p[], int numPlanes, + const Vector3& sa, const Vector3& dir, const Plane p[], int numPlanes, float& tfirst, float& tlast, int& iFirstPlane, int& iLastPlane) { // Compute direction vector for the segment - Vector3 d = /*b - a*/ sBA; + Vector3 d = /*b - a*/ dir; // Set initial interval to being the whole segment. For a ray, tlast should be // set to +RR_FLT_MAX. For a line, additionally tfirst should be set to -RR_FLT_MAX tfirst = 0.0f; @@ -1388,7 +1388,7 @@ AZ::Intersect::IntersectSegmentPolyhedron( // If so, return "no intersection" if segment lies outside plane if (dist < 0.0f) { - return 0; + return false; } } else @@ -1417,7 +1417,7 @@ AZ::Intersect::IntersectSegmentPolyhedron( // Exit with "no intersection" if intersection becomes empty if (tfirst > tlast) { - return 0; + return false; } } } @@ -1425,11 +1425,11 @@ AZ::Intersect::IntersectSegmentPolyhedron( //DBG_Assert(iFirstPlane!=-1&&iLastPlane!=-1,("We have some bad border case to have only one plane, fix this function!")); if (iFirstPlane == -1 && iLastPlane == -1) { - return 0; + return false; } // A nonzero logical intersection, so the segment intersects the polyhedron - return 1; + return true; } //========================================================================= @@ -1442,7 +1442,7 @@ AZ::Intersect::ClosestSegmentSegment( const Vector3& segment2Start, const Vector3& segment2End, float& segment1Proportion, float& segment2Proportion, Vector3& closestPointSegment1, Vector3& closestPointSegment2, - float epsilon /*= 1e-4f*/ ) + float epsilon) { const Vector3 segment1 = segment1End - segment1Start; const Vector3 segment2 = segment2End - segment2Start; diff --git a/Code/Framework/AzCore/AzCore/Math/IntersectSegment.h b/Code/Framework/AzCore/AzCore/Math/IntersectSegment.h index df3d5e10fb..523069987f 100644 --- a/Code/Framework/AzCore/AzCore/Math/IntersectSegment.h +++ b/Code/Framework/AzCore/AzCore/Math/IntersectSegment.h @@ -5,363 +5,398 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ -#ifndef AZCORE_MATH_SEGMENT_INTERSECTION_H -#define AZCORE_MATH_SEGMENT_INTERSECTION_H +#pragma once -#include #include #include #include - -/// \file isect_segment.h +#include namespace AZ { namespace Intersect { //! LineToPointDistanceTime computes the time of the shortest distance from point 'p' to segment (s1,s2). - //! To calculate the point of intersection: - //! P = s1 + u (s2 - s1) - //! @param s1 segment start point - //! @param s2 segment end point - //! @param p point to find the closest time to. - //! @return time (on the segment) for the shortest distance from 'p' to (s1,s2) [0.0f (s1),1.0f (s2)] - inline float LineToPointDistanceTime(const Vector3& s1, const Vector3& s21, const Vector3& p) - { - // so u = (p.x - s1.x)*(s2.x - s1.x) + (p.y - s1.y)*(s2.y - s1.y) + (p.z-s1.z)*(s2.z-s1.z) / |s2-s1|^2 - return s21.Dot(p - s1) / s21.Dot(s21); - } + //! To calculate the point of intersection: P = s1 + u (s2 - s1) + //! @param s1 Segment start point. + //! @param s2 Segment end point. + //! @param p Point to find the closest time to. + //! @return Time (on the segment) for the shortest distance from 'p' to (s1,s2) [0.0f (s1),1.0f (s2)] + float LineToPointDistanceTime(const Vector3& s1, const Vector3& s21, const Vector3& p); //! LineToPointDistance computes the closest point to 'p' from a segment (s1,s2). - //! @param s1 segment start point - //! @param s2 segment end point - //! @param p point to find the closest time to. - //! @param u time (on the segment) for the shortest distance from 'p' to (s1,s2) [0.0f (s1),1.0f (s2)] - //! @return the closest point - inline Vector3 LineToPointDistance(const Vector3& s1, const Vector3& s2, const Vector3& p, float& u) - { - const Vector3 s21 = s2 - s1; - // we assume seg1 and seg2 are NOT coincident - AZ_MATH_ASSERT(!s21.IsClose(Vector3(0.0f), 1e-4f), "OK we agreed that we will pass valid segments! (s1 != s2)"); - - u = LineToPointDistanceTime(s1, s21, p); - - return s1 + u * s21; - } + //! @param s1 Segment start point + //! @param s2 Segment end point + //! @param p Point to find the closest time to. + //! @param u Time (on the segment) for the shortest distance from 'p' to (s1,s2) [0.0f (s1),1.0f (s2)] + //! @return The closest point + Vector3 LineToPointDistance(const Vector3& s1, const Vector3& s2, const Vector3& p, float& u); //! Given segment pq and triangle abc (CCW), returns whether segment intersects //! triangle and if so, also returns the barycentric coordinates (u,v,w) //! of the intersection point. - //! @param p segment start point - //! @param q segment end point - //! @param a triangle point 1 - //! @param b triangle point 2 - //! @param c triangle point 3 - //! @param normal at the intersection point. - //! @param t time of intersection along the segment [0.0 (p), 1.0 (q)] - //! @return 1 if the segment intersects the triangle otherwise 0 - int IntersectSegmentTriangleCCW( - const Vector3& p, const Vector3& q, const Vector3& a, const Vector3& b, const Vector3& c, - /*float &u, float &v, float &w,*/ Vector3& normal, float& t); + //! @param p Segment start point. + //! @param q Segment end point. + //! @param a Triangle point 1. + //! @param b Triangle point 2. + //! @param c Triangle point 3. + //! @param normal At the intersection point. + //! @param t Time of intersection along the segment [0.0 (p), 1.0 (q)]. + //! @return true if the segments intersects the triangle otherwise false. + bool IntersectSegmentTriangleCCW( + const Vector3& p, const Vector3& q, const Vector3& a, const Vector3& b, const Vector3& c, Vector3& normal, float& t); //! Same as \ref IntersectSegmentTriangleCCW without respecting the triangle (a,b,c) vertex order (double sided). - int IntersectSegmentTriangle( - const Vector3& p, const Vector3& q, const Vector3& a, const Vector3& b, const Vector3& c, - /*float &u, float &v, float &w,*/ Vector3& normal, float& t); + //! @param p Segment start point. + //! @param q Segment end point. + //! @param a Triangle point 1. + //! @param b Triangle point 2. + //! @param c Triangle point 3. + //! @param normal At the intersection point. + //! @param t Time of intersection along the segment [0.0 (p), 1.0 (q)]. + //! @return True if the segments intersects the triangle otherwise false. + bool IntersectSegmentTriangle( + const Vector3& p, const Vector3& q, const Vector3& a, const Vector3& b, const Vector3& c, Vector3& normal, float& t); //! Ray aabb intersection result types. - enum RayAABBIsectTypes + enum RayAABBIsectTypes : AZ::s32 { - ISECT_RAY_AABB_NONE = 0, ///< no intersection - ISECT_RAY_AABB_SA_INSIDE, ///< the ray starts inside the aabb - ISECT_RAY_AABB_ISECT, ///< intersects along the PQ segment + ISECT_RAY_AABB_NONE = 0, ///< no intersection + ISECT_RAY_AABB_SA_INSIDE, ///< the ray starts inside the aabb + ISECT_RAY_AABB_ISECT, ///< intersects along the PQ segment }; //! Intersect ray R(t) = rayStart + t*d against AABB a. When intersecting, //! return intersection distance tmin and point q of intersection. - //! @param rayStart ray starting point - //! @param dir ray direction and length (dir = rayEnd - rayStart) - //! @param dirRCP 1/dir (reciprocal direction - we cache this result very often so we don't need to compute it multiple times, otherwise just use dir.GetReciprocal()) + //! @param rayStart Ray starting point + //! @param dir Ray direction and length (dir = rayEnd - rayStart) + //! @param dirRCP 1/dir (reciprocal direction - we cache this result very often so we don't need to compute it multiple times, + //! otherwise just use dir.GetReciprocal()) //! @param aabb Axis aligned bounding box to intersect against - //! @param tStart time on ray of the first intersection [0,1] or 0 if the ray starts inside the aabb - check the return value - //! @param tEnd time of the of the second intersection [0,1] (it can be > 1 if intersects after the rayEnd) - //! @param startNormal normal at the start point. + //! @param tStart Time on ray of the first intersection [0,1] or 0 if the ray starts inside the aabb - check the return value + //! @param tEnd Time of the of the second intersection [0,1] (it can be > 1 if intersects after the rayEnd) + //! @param startNormal Normal at the start point. //! @return \ref RayAABBIsectTypes - int IntersectRayAABB( - const Vector3& rayStart, const Vector3& dir, const Vector3& dirRCP, const Aabb& aabb, - float& tStart, float& tEnd, Vector3& startNormal /*, Vector3& inter*/); + RayAABBIsectTypes IntersectRayAABB( + const Vector3& rayStart, + const Vector3& dir, + const Vector3& dirRCP, + const Aabb& aabb, + float& tStart, + float& tEnd, + Vector3& startNormal); //! Intersect ray against AABB. - //! @param rayStart ray starting point. - //! @param dir ray reciprocal direction. + //! @param rayStart Ray starting point. + //! @param dir Ray reciprocal direction. //! @param aabb Axis aligned bounding box to intersect against. - //! @param start length on ray of the first intersection. - //! @param end length of the of the second intersection. - //! @return \ref RayAABBIsectTypes In this faster version than IntersectRayAABB we return only ISECT_RAY_AABB_NONE and ISECT_RAY_AABB_ISECT. - //! You can check yourself for that case. - int IntersectRayAABB2( - const Vector3& rayStart, const Vector3& dirRCP, const Aabb& aabb, - float& start, float& end); + //! @param start Length on ray of the first intersection. + //! @param end Length of the of the second intersection. + //! @return \ref RayAABBIsectTypes In this faster version than IntersectRayAABB we return only ISECT_RAY_AABB_NONE and + //! ISECT_RAY_AABB_ISECT. You can check yourself for that case. + RayAABBIsectTypes IntersectRayAABB2(const Vector3& rayStart, const Vector3& dirRCP, const Aabb& aabb, float& start, float& end); //! Clip a ray to an aabb. return true if ray was clipped. The ray //! can be inside so don't use the result if the ray intersect the box. - inline int ClipRayWithAabb( - const Aabb& aabb, Vector3& rayStart, Vector3& rayEnd, float& tClipStart, float& tClipEnd) - { - Vector3 startNormal; - float tStart, tEnd; - Vector3 dirLen = rayEnd - rayStart; - if (IntersectRayAABB(rayStart, dirLen, dirLen.GetReciprocal(), aabb, tStart, tEnd, startNormal) != ISECT_RAY_AABB_NONE) - { - // clip the ray with the box - if (tStart > 0.0f) - { - rayStart = rayStart + tStart * dirLen; - tClipStart = tStart; - } - if (tEnd < 1.0f) - { - rayEnd = rayStart + tEnd * dirLen; - tClipEnd = tEnd; - } - - return 1; - } - - return 0; - } + //! @param aabb Bounds to test against. + //! @param rayStart The start of the ray. + //! @param rayEnd The end of the ray. + //! @param[out] tClipStart The proportion where the ray enters the \ref Aabb. + //! @param[out] tClipEnd The proportion where the ray exits the \ref Aabb. + //! @return True if the ray was clipped, otherwise false. + bool ClipRayWithAabb(const Aabb& aabb, Vector3& rayStart, Vector3& rayEnd, float& tClipStart, float& tClipEnd); //! Test segment and aabb where the segment is defined by midpoint //! midPoint = (p1-p0) * 0.5f and half vector halfVector = p1 - midPoint. //! the aabb is at the origin and defined by half extents only. - //! @return 1 if the intersect, otherwise 0. - int TestSegmentAABBOrigin(const Vector3& midPoint, const Vector3& halfVector, const Vector3& aabbExtends); - - //! Test if segment specified by points p0 and p1 intersects AABB. \ref TestSegmentAABBOrigin - //! @return 1 if the segment and AABB intersect, otherwise 0. - inline int TestSegmentAABB(const Vector3& p0, const Vector3& p1, const Aabb& aabb) - { - Vector3 e = aabb.GetExtents(); - Vector3 d = p1 - p0; - Vector3 m = p0 + p1 - aabb.GetMin() - aabb.GetMax(); - - return TestSegmentAABBOrigin(m, d, e); - } + //! @param midPoint Midpoint of a line segment. + //! @param halfVector Half vector of an aabb. + //! @param aabbExtends The extends of a bounded box. + //! @return True if the segment and AABB intersect, otherwise false + bool TestSegmentAABBOrigin(const Vector3& midPoint, const Vector3& halfVector, const Vector3& aabbExtends); + + //! Test if segment specified by points p0 and p1 intersects AABB. \ref TestSegmentAABBOrigin. + //! @param p0 Segment start point. + //! @param p1 Segment end point. + //! @param aabb Bounded box to test against. + //! @return True if the segment and AABB intersect, otherwise false. + bool TestSegmentAABB(const Vector3& p0, const Vector3& p1, const Aabb& aabb); //! Ray sphere intersection result types. - enum SphereIsectTypes + enum SphereIsectTypes : AZ::s32 { - ISECT_RAY_SPHERE_SA_INSIDE = -1, // the ray starts inside the cylinder - ISECT_RAY_SPHERE_NONE, // no intersection - ISECT_RAY_SPHERE_ISECT, // along the PQ segment + ISECT_RAY_SPHERE_SA_INSIDE = -1, //!< The ray starts inside the cylinder + ISECT_RAY_SPHERE_NONE, //!< No intersection + ISECT_RAY_SPHERE_ISECT, //!< Along the PQ segment }; //! IntersectRaySphereOrigin //! return time t>=0 but not limited, so if you check a segment make sure - //! t <= segmentLen - //! @param rayStart ray start point + //! t <= segmentLen. + //! @param rayStart ray start point. //! @param rayDirNormalized ray direction normalized. - //! @param shereRadius sphere radius + //! @param shereRadius Radius of sphere at origin. //! @param time of closest intersection [0,+INF] in relation to the normalized direction. - //! @return \ref SphereIsectTypes - AZ_INLINE int IntersectRaySphereOrigin( - const Vector3& rayStart, const Vector3& rayDirNormalized, - const float sphereRadius, float& t) - { - Vector3 m = rayStart; - float b = m.Dot(rayDirNormalized); - float c = m.Dot(m) - sphereRadius * sphereRadius; - - // Exit if r's origin outside s (c > 0)and r pointing away from s (b > 0) - if (c > 0.0f && b > 0.0f) - { - return ISECT_RAY_SPHERE_NONE; - } - float discr = b * b - c; - // A negative discriminant corresponds to ray missing sphere - if (discr < 0.0f) - { - return ISECT_RAY_SPHERE_NONE; - } - - // Ray now found to intersect sphere, compute smallest t value of intersection - t = -b - Sqrt(discr); - - // If t is negative, ray started inside sphere so clamp t to zero - if (t < 0.0f) - { - // t = 0.0f; - return ISECT_RAY_SPHERE_SA_INSIDE; // no hit if inside - } - //q = p + t * d; - return ISECT_RAY_SPHERE_ISECT; - } + //! @return \ref SphereIsectTypes. + SphereIsectTypes IntersectRaySphereOrigin( + const Vector3& rayStart, const Vector3& rayDirNormalized, const float sphereRadius, float& t); //! Intersect ray (rayStart,rayDirNormalized) and sphere (sphereCenter,sphereRadius) \ref IntersectRaySphereOrigin - inline int IntersectRaySphere( - const Vector3& rayStart, const Vector3& rayDirNormalized, const Vector3& sphereCenter, const float sphereRadius, float& t) - { - return IntersectRaySphereOrigin(rayStart - sphereCenter, rayDirNormalized, sphereRadius, t); - } - - //! @param rayOrigin The origin of the ray to test. - //! @param rayDir The direction of the ray to test. It has to be unit length. - //! @param diskCenter Center point of the disk - //! @param diskRadius Radius of the disk - //! @param diskNormal A normal perpendicular to the disk - //! @param[out] t If returning 1 (indicating a hit), this contains distance from rayOrigin along the normalized rayDir that the hit occured at. - //! @return The number of intersecting points. - int IntersectRayDisk( - const Vector3& rayOrigin, const Vector3& rayDir, const Vector3& diskCenter, const float diskRadius, const AZ::Vector3& diskNormal, float& t); + //! @param rayStart The start of the ray. + //! @param rayDirNormalized The direction of the ray normalized. + //! @param sphereCenter The center of the sphere. + //! @param sphereRadius Radius of the sphere. + //! @param[out] t Coefficient in the ray's explicit equation from which an + //! intersecting point is calculated as "rayOrigin + t1 * rayDir". + //! @return SphereIsectTypes + SphereIsectTypes IntersectRaySphere( + const Vector3& rayStart, const Vector3& rayDirNormalized, const Vector3& sphereCenter, const float sphereRadius, float& t); + + //! Intersect ray (rayStarty, rayDirNormalized) and disk (center, radius, normal) + //! @param rayOrigin The origin of the ray to test. + //! @param rayDir The direction of the ray to test. It has to be unit length. + //! @param diskCenter Center point of the disk. + //! @param diskRadius Radius of the disk. + //! @param diskNormal A normal perpendicular to the disk. + //! @param[out] t If returning 1 (indicating a hit), this contains distance from rayOrigin along the normalized rayDir + //! that the hit occured at. + //! @return False if not interesecting and true if intersecting + bool IntersectRayDisk( + const Vector3& rayOrigin, + const Vector3& rayDir, + const Vector3& diskCenter, + const float diskRadius, + const AZ::Vector3& diskNormal, + float& t); //! If there is only one intersecting point, the coefficient is stored in \ref t1. - //! @param rayOrigin The origin of the ray to test. - //! @param rayDir The direction of the ray to test. It has to be unit length. - //! @param cylinderEnd1 The center of the circle on one end of the cylinder. - //! @param cylinderDir The direction pointing from \ref cylinderEnd1 to the other end of the cylinder. It has to be unit length. - //! @param cylinderHeight The distance between two centers of the circles on two ends of the cylinder respectively. - //! @param[out] t1 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated as "rayOrigin + t1 * rayDir". - //! @param[out] t2 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated as "rayOrigin + t2 * rayDir". - //! @return The number of intersecting points. + //! @param rayOrigin The origin of the ray to test. + //! @param rayDir The direction of the ray to test. It has to be unit length. + //! @param cylinderEnd1 The center of the circle on one end of the cylinder. + //! @param cylinderDir The direction pointing from \ref cylinderEnd1 to the other end of the cylinder. It has to be unit length. + //! @param cylinderHeight The distance between two centers of the circles on two ends of the cylinder respectively. + //! @param[out] t1 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated as "rayOrigin + t1 * rayDir". + //! @param[out] t2 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated as "rayOrigin + t2 * rayDir". + //! @return The number of intersecting points. int IntersectRayCappedCylinder( - const Vector3& rayOrigin, const Vector3& rayDir, - const Vector3& cylinderEnd1, const Vector3& cylinderDir, float cylinderHeight, float cylinderRadius, - float& t1, float& t2); + const Vector3& rayOrigin, + const Vector3& rayDir, + const Vector3& cylinderEnd1, + const Vector3& cylinderDir, + float cylinderHeight, + float cylinderRadius, + float& t1, + float& t2); //! If there is only one intersecting point, the coefficient is stored in \ref t1. - //! @param rayOrigin The origin of the ray to test. - //! @param rayDir The direction of the ray to test. It has to be unit length. - //! @param coneApex The apex of the cone. - //! @param coneDir The unit-length direction from the apex to the base. - //! @param coneHeight The height of the cone, from the apex to the base. - //! @param coneBaseRadius The radius of the cone base circle. - //! @param[out] t1 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated as "rayOrigin + t1 * rayDir". - //! @param[out] t2 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated as "rayOrigin + t2 * rayDir". - //! @return The number of intersecting points. + //! @param rayOrigin The origin of the ray to test. + //! @param rayDir The direction of the ray to test. It has to be unit length. + //! @param coneApex The apex of the cone. + //! @param coneDir The unit-length direction from the apex to the base. + //! @param coneHeight The height of the cone, from the apex to the base. + //! @param coneBaseRadius The radius of the cone base circle. + //! @param[out] t1 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated as "rayOrigin + t1 * rayDir". + //! @param[out] t2 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated as "rayOrigin + t2 * rayDir". + //! @return The number of intersecting points. int IntersectRayCone( - const Vector3& rayOrigin, const Vector3& rayDir, - const Vector3& coneApex, const Vector3& coneDir, float coneHeight, float coneBaseRadius, - float& t1, float& t2); + const Vector3& rayOrigin, + const Vector3& rayDir, + const Vector3& coneApex, + const Vector3& coneDir, + float coneHeight, + float coneBaseRadius, + float& t1, + float& t2); //! Test intersection between a ray and a plane in 3D. - //! @param rayOrigin The origin of the ray to test intersection with. - //! @param rayDir The direction of the ray to test intersection with. - //! @param planePos A point on the plane to test intersection with. - //! @param planeNormal The normal of the plane to test intersection with. - //! @param t[out] The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + t * rayDirection". - //! @return The number of intersection point. + //! @param rayOrigin The origin of the ray to test intersection with. + //! @param rayDir The direction of the ray to test intersection with. + //! @param planePos A point on the plane to test intersection with. + //! @param planeNormal The normal of the plane to test intersection with. + //! @param[out] t The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + t * rayDirection". + //! @return The number of intersection point. int IntersectRayPlane( - const Vector3& rayOrigin, const Vector3& rayDir, const Vector3& planePos, - const Vector3& planeNormal, float& t); + const Vector3& rayOrigin, const Vector3& rayDir, const Vector3& planePos, const Vector3& planeNormal, float& t); //! Test intersection between a ray and a two-sided quadrilateral defined by four points in 3D. - //! The four points that define the quadrilateral could be passed in with either counter clock-wise + //! The four points that define the quadrilateral could be passed in with either counter clock-wise //! winding or clock-wise winding. - //! @param rayOrigin The origin of the ray to test intersection with. - //! @param rayDir The direction of the ray to test intersection with. - //! @param vertexA One of the four points that define the quadrilateral. - //! @param vertexB One of the four points that define the quadrilateral. - //! @param vertexC One of the four points that define the quadrilateral. - //! @param vertexD One of the four points that define the quadrilateral. - //! @param t[out] The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + t * rayDirection". - //! @return The number of intersection point. + //! @param rayOrigin The origin of the ray to test intersection with. + //! @param rayDir The direction of the ray to test intersection with. + //! @param vertexA One of the four points that define the quadrilateral. + //! @param vertexB One of the four points that define the quadrilateral. + //! @param vertexC One of the four points that define the quadrilateral. + //! @param vertexD One of the four points that define the quadrilateral. + //! @param[out] t The coefficient in the ray's explicit equation from which the + //! intersecting point is calculated as "rayOrigin + t * rayDirection". + //! @return The number of intersection point. int IntersectRayQuad( - const Vector3& rayOrigin, const Vector3& rayDir, const Vector3& vertexA, - const Vector3& vertexB, const Vector3& vertexC, const Vector3& vertexD, float& t); + const Vector3& rayOrigin, + const Vector3& rayDir, + const Vector3& vertexA, + const Vector3& vertexB, + const Vector3& vertexC, + const Vector3& vertexD, + float& t); - //! Test intersection between a ray and an oriented box in 3D. - //! @param rayOrigin The origin of the ray to test intersection with. - //! @param rayDir The direction of the ray to test intersection with. - //! @param boxCenter The position of the center of the box. - //! @param boxAxis1 An axis along one dimension of the oriented box. - //! @param boxAxis2 An axis along one dimension of the oriented box. - //! @param boxAxis3 An axis along one dimension of the oriented box. - //! @param boxHalfExtent1 The half extent of the box on the dimension of \ref boxAxis1. - //! @param boxHalfExtent2 The half extent of the box on the dimension of \ref boxAxis2. - //! @param boxHalfExtent3 The half extent of the box on the dimension of \ref boxAxis3. - //! @param t[out] The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + t * rayDirection". - //! @return 1 if there is an intersection, 0 otherwise. - int IntersectRayBox( - const Vector3& rayOrigin, const Vector3& rayDir, const Vector3& boxCenter, const Vector3& boxAxis1, - const Vector3& boxAxis2, const Vector3& boxAxis3, float boxHalfExtent1, float boxHalfExtent2, float boxHalfExtent3, + //! Test intersection between a ray and an oriented box in 3D. + //! @param rayOrigin The origin of the ray to test intersection with. + //! @param rayDir The direction of the ray to test intersection with. + //! @param boxCenter The position of the center of the box. + //! @param boxAxis1 An axis along one dimension of the oriented box. + //! @param boxAxis2 An axis along one dimension of the oriented box. + //! @param boxAxis3 An axis along one dimension of the oriented box. + //! @param boxHalfExtent1 The half extent of the box on the dimension of \ref boxAxis1. + //! @param boxHalfExtent2 The half extent of the box on the dimension of \ref boxAxis2. + //! @param boxHalfExtent3 The half extent of the box on the dimension of \ref boxAxis3. + //! @param[out] t The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + t * rayDirection". + //! @return true if there is an intersection, false otherwise. + bool IntersectRayBox( + const Vector3& rayOrigin, + const Vector3& rayDir, + const Vector3& boxCenter, + const Vector3& boxAxis1, + const Vector3& boxAxis2, + const Vector3& boxAxis3, + float boxHalfExtent1, + float boxHalfExtent2, + float boxHalfExtent3, float& t); //! Test intersection between a ray and an OBB. //! @param rayOrigin The origin of the ray to test intersection with. //! @param rayDir The direction of the ray to test intersection with. //! @param obb The OBB to test for intersection with the ray. - //! @param t[out] The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + t * rayDirection". - //! @return 1 if there is an intersection, 0 otherwise. - int IntersectRayObb(const Vector3& rayOrigin, const Vector3& rayDir, const Obb& obb, float& t); + //! @param[out] t The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + t * rayDirection". + //! @return True if there is an intersection, false otherwise. + bool IntersectRayObb(const Vector3& rayOrigin, const Vector3& rayDir, const Obb& obb, float& t); //! Ray cylinder intersection types. - enum CylinderIsectTypes + enum CylinderIsectTypes : AZ::s32 { - RR_ISECT_RAY_CYL_SA_INSIDE = -1, // the ray starts inside the cylinder - RR_ISECT_RAY_CYL_NONE, // no intersection - RR_ISECT_RAY_CYL_PQ, // along the PQ segment - RR_ISECT_RAY_CYL_P_SIDE, // on the P side - RR_ISECT_RAY_CYL_Q_SIDE, // on the Q side + RR_ISECT_RAY_CYL_SA_INSIDE = -1, //!< the ray starts inside the cylinder + RR_ISECT_RAY_CYL_NONE, //!< no intersection + RR_ISECT_RAY_CYL_PQ, //!< along the PQ segment + RR_ISECT_RAY_CYL_P_SIDE, //!< on the P side + RR_ISECT_RAY_CYL_Q_SIDE, //!< on the Q side }; + //! Reference: Real-Time Collision Detection - 5.3.7 Intersecting Ray or Segment Against Cylinder //! Intersect segment S(t)=sa+t(dir), 0<=t<=1 against cylinder specified by p, q and r. - int IntersectSegmentCylinder( - const Vector3& sa, const Vector3& dir, const Vector3& p, const Vector3& q, - const float r, float& t); + //! @param sa The initial point. + //! @param dir Magnitude and direction for sa. + //! @param p Center point of side 1 cylinder. + //! @param q Center point of side 2 cylinder. + //! @param r Radius of cylinder. + //! @param[out] t Proporition along line segment. + //! @return CylinderIsectTypes + CylinderIsectTypes IntersectSegmentCylinder( + const Vector3& sa, const Vector3& dir, const Vector3& p, const Vector3& q, const float r, float& t); //! Capsule ray intersect types. enum CapsuleIsectTypes { - ISECT_RAY_CAPSULE_SA_INSIDE = -1, // the ray starts inside the cylinder - ISECT_RAY_CAPSULE_NONE, // no intersection - ISECT_RAY_CAPSULE_PQ, // along the PQ segment - ISECT_RAY_CAPSULE_P_SIDE, // on the P side - ISECT_RAY_CAPSULE_Q_SIDE, // on the Q side + ISECT_RAY_CAPSULE_SA_INSIDE = -1, //!< The ray starts inside the cylinder + ISECT_RAY_CAPSULE_NONE, //!< No intersection + ISECT_RAY_CAPSULE_PQ, //!< Along the PQ segment + ISECT_RAY_CAPSULE_P_SIDE, //!< On the P side + ISECT_RAY_CAPSULE_Q_SIDE, //!< On the Q side }; //! This is a quick implementation of segment capsule based on segment cylinder \ref IntersectSegmentCylinder //! segment sphere intersection. We can optimize it a lot once we fix the ray //! cylinder intersection. - int IntersectSegmentCapsule( - const Vector3& sa, const Vector3& dir, const Vector3& p, - const Vector3& q, const float r, float& t); + //! @param sa The beginning of the line segment. + //! @param dir The direction and length of the segment. + //! @param p Center point of side 1 capsule. + //! @param q Center point of side 1 capsule. + //! @param r The radius of the capsule. + //! @param[out] t Proporition along line segment. + //! @return CapsuleIsectTypes + CapsuleIsectTypes IntersectSegmentCapsule( + const Vector3& sa, const Vector3& dir, const Vector3& p, const Vector3& q, const float r, float& t); //! Intersect segment S(t)=A+t(B-A), 0<=t<=1 against convex polyhedron specified //! by the n halfspaces defined by the planes p[]. On exit tfirst and tlast //! define the intersection, if any. - int IntersectSegmentPolyhedron( - const Vector3& sa, const Vector3& sBA, const Plane p[], int numPlanes, - float& tfirst, float& tlast, int& iFirstPlane, int& iLastPlane); + //! @param sa The beggining of the line segment. + //! @param dir The direction and length of the segment. + //! @param p Planes that compose a convex ponvex polyhedron. + //! @param numPlanes number of planes. + //! @param[out] tfirst Proportion along the line segment where the line enters. + //! @param[out] tlast Proportion along the line segment where the line exits. + //! @param[out] iFirstPlane The plane where the line enters. + //! @param[out] iLastPlane The plane where the line exits. + //! @return True if intersects else false. + bool IntersectSegmentPolyhedron( + const Vector3& sa, + const Vector3& dir, + const Plane p[], + int numPlanes, + float& tfirst, + float& tlast, + int& iFirstPlane, + int& iLastPlane); //! Calculate the line segment closestPointSegment1<->closestPointSegment2 that is the shortest route between - //! two segments segment1Start<->segment1End and segment2Start<->segment2End. Also calculate the values of segment1Proportion and segment2Proportion where - //! closestPointSegment1 = segment1Start + (segment1Proportion * (segment1End - segment1Start)) + //! two segments segment1Start<->segment1End and segment2Start<->segment2End. Also calculate the values of segment1Proportion and + //! segment2Proportion where closestPointSegment1 = segment1Start + (segment1Proportion * (segment1End - segment1Start)) //! closestPointSegment2 = segment2Start + (segment2Proportion * (segment2End - segment2Start)) //! If segments are parallel returns a solution. + //! @param segment1Start Start of segment 1. + //! @param segment1End End of segment 1. + //! @param segment2Start Start of segment 2. + //! @param segment2End End of segment 2. + //! @param[out] segment1Proportion The proporition along segment 1 [0..1] + //! @param[out] segment2Proportion The proporition along segment 2 [0..1] + //! @param[out] closestPointSegment1 Closest point on segment 1. + //! @param[out] closestPointSegment2 Closest point on segment 2. + //! @param epsilon The minimum square distance where a line segment can be treated as a single point. void ClosestSegmentSegment( - const Vector3& segment1Start, const Vector3& segment1End, - const Vector3& segment2Start, const Vector3& segment2End, - float& segment1Proportion, float& segment2Proportion, - Vector3& closestPointSegment1, Vector3& closestPointSegment2, + const Vector3& segment1Start, + const Vector3& segment1End, + const Vector3& segment2Start, + const Vector3& segment2End, + float& segment1Proportion, + float& segment2Proportion, + Vector3& closestPointSegment1, + Vector3& closestPointSegment2, float epsilon = 1e-4f); //! Calculate the line segment closestPointSegment1<->closestPointSegment2 that is the shortest route between //! two segments segment1Start<->segment1End and segment2Start<->segment2End. //! If segments are parallel returns a solution. + //! @param segment1Start Start of segment 1. + //! @param segment1End End of segment 1. + //! @param segment2Start Start of segment 2. + //! @param segment2End End of segment 2. + //! @param[out] closestPointSegment1 Closest point on segment 1. + //! @param[out] closestPointSegment2 Closest point on segment 2. + //! @param epsilon The minimum square distance where a line segment can be treated as a single point. void ClosestSegmentSegment( - const Vector3& segment1Start, const Vector3& segment1End, - const Vector3& segment2Start, const Vector3& segment2End, - Vector3& closestPointSegment1, Vector3& closestPointSegment2, + const Vector3& segment1Start, + const Vector3& segment1End, + const Vector3& segment2Start, + const Vector3& segment2End, + Vector3& closestPointSegment1, + Vector3& closestPointSegment2, float epsilon = 1e-4f); //! Calculate the point (closestPointOnSegment) that is the closest point on //! segment segmentStart/segmentEnd to point. Also calculate the value of proportion where //! closestPointOnSegment = segmentStart + (proportion * (segmentEnd - segmentStart)) + //! @param point The point to test + //! @param segmentStart The start of the segment + //! @param segmentEnd The end of the segment + //! @param[out] proportion The proportion of the segment L(t) = (end - start) * t + //! @param[out] closestPointOnSegment The point along the line segment void ClosestPointSegment( - const Vector3& point, const Vector3& segmentStart, const Vector3& segmentEnd, - float& proportion, Vector3& closestPointOnSegment); - } -} - -#endif // AZCORE_MATH_SEGMENT_INTERSECTION_H -#pragma once + const Vector3& point, + const Vector3& segmentStart, + const Vector3& segmentEnd, + float& proportion, + Vector3& closestPointOnSegment); + } // namespace Intersect +} // namespace AZ + +#include diff --git a/Code/Framework/AzCore/AzCore/Math/IntersectSegment.inl b/Code/Framework/AzCore/AzCore/Math/IntersectSegment.inl new file mode 100644 index 0000000000..b570b6a182 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/Math/IntersectSegment.inl @@ -0,0 +1,101 @@ +/* + * 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 + * + */ + +namespace AZ +{ + namespace Intersect + { + AZ_MATH_INLINE bool ClipRayWithAabb(const Aabb& aabb, Vector3& rayStart, Vector3& rayEnd, float& tClipStart, float& tClipEnd) + { + Vector3 startNormal; + float tStart, tEnd; + Vector3 dirLen = rayEnd - rayStart; + if (IntersectRayAABB(rayStart, dirLen, dirLen.GetReciprocal(), aabb, tStart, tEnd, startNormal) != ISECT_RAY_AABB_NONE) + { + // clip the ray with the box + if (tStart > 0.0f) + { + rayStart = rayStart + tStart * dirLen; + tClipStart = tStart; + } + if (tEnd < 1.0f) + { + rayEnd = rayStart + tEnd * dirLen; + tClipEnd = tEnd; + } + + return true; + } + + return false; + } + + AZ_MATH_INLINE SphereIsectTypes + IntersectRaySphereOrigin(const Vector3& rayStart, const Vector3& rayDirNormalized, const float sphereRadius, float& t) + { + Vector3 m = rayStart; + float b = m.Dot(rayDirNormalized); + float c = m.Dot(m) - sphereRadius * sphereRadius; + + // Exit if r's origin outside s (c > 0)and r pointing away from s (b > 0) + if (c > 0.0f && b > 0.0f) + { + return ISECT_RAY_SPHERE_NONE; + } + float discr = b * b - c; + // A negative discriminant corresponds to ray missing sphere + if (discr < 0.0f) + { + return ISECT_RAY_SPHERE_NONE; + } + + // Ray now found to intersect sphere, compute smallest t value of intersection + t = -b - Sqrt(discr); + + // If t is negative, ray started inside sphere so clamp t to zero + if (t < 0.0f) + { + // t = 0.0f; + return ISECT_RAY_SPHERE_SA_INSIDE; // no hit if inside + } + // q = p + t * d; + return ISECT_RAY_SPHERE_ISECT; + } + + AZ_MATH_INLINE SphereIsectTypes IntersectRaySphere(const Vector3& rayStart, const Vector3& rayDirNormalized, const Vector3& sphereCenter, const float sphereRadius, float& t) + { + return IntersectRaySphereOrigin(rayStart - sphereCenter, rayDirNormalized, sphereRadius, t); + } + + AZ_MATH_INLINE Vector3 LineToPointDistance(const Vector3& s1, const Vector3& s2, const Vector3& p, float& u) + { + const Vector3 s21 = s2 - s1; + // we assume seg1 and seg2 are NOT coincident + AZ_MATH_ASSERT(!s21.IsClose(Vector3(0.0f), 1e-4f), "OK we agreed that we will pass valid segments! (s1 != s2)"); + + u = LineToPointDistanceTime(s1, s21, p); + + return s1 + u * s21; + } + + AZ_MATH_INLINE float LineToPointDistanceTime(const Vector3& s1, const Vector3& s21, const Vector3& p) + { + // so u = (p.x - s1.x)*(s2.x - s1.x) + (p.y - s1.y)*(s2.y - s1.y) + (p.z-s1.z)*(s2.z-s1.z) / |s2-s1|^2 + return s21.Dot(p - s1) / s21.Dot(s21); + } + + AZ_MATH_INLINE bool TestSegmentAABB(const Vector3& p0, const Vector3& p1, const Aabb& aabb) + { + Vector3 e = aabb.GetExtents(); + Vector3 d = p1 - p0; + Vector3 m = p0 + p1 - aabb.GetMin() - aabb.GetMax(); + + return TestSegmentAABBOrigin(m, d, e); + } + } // namespace Intersect +} // namespace AZ diff --git a/Code/Framework/AzCore/AzCore/azcore_files.cmake b/Code/Framework/AzCore/AzCore/azcore_files.cmake index 6675958247..4d95ddf098 100644 --- a/Code/Framework/AzCore/AzCore/azcore_files.cmake +++ b/Code/Framework/AzCore/AzCore/azcore_files.cmake @@ -282,6 +282,7 @@ set(FILES Math/Internal/VertexContainer.inl Math/InterpolationSample.h Math/IntersectPoint.h + Math/IntersectSegment.inl Math/IntersectSegment.cpp Math/IntersectSegment.h Math/MathIntrinsics.h 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/AzFramework/AzFramework/Spawnable/RootSpawnableInterface.h b/Code/Framework/AzFramework/AzFramework/Spawnable/RootSpawnableInterface.h index bb1f137ba2..72a3031e3e 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/RootSpawnableInterface.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/RootSpawnableInterface.h @@ -61,6 +61,10 @@ namespace AzFramework //! be deleted and the spawnable asset to be released. This call is automatically done when //! AssignRootSpawnable is called while a root spawnable is assigned. virtual void ReleaseRootSpawnable() = 0; + //! Force processing all SpawnableEntitiesManager requests immediately + //! This is useful when loading a different level while SpawnableEntitiesManager still has + //! pending requests + virtual void ProcessSpawnableQueue() = 0; }; using RootSpawnableInterface = AZ::Interface; diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.cpp index f6130c9e31..957786c6df 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.cpp @@ -45,8 +45,7 @@ namespace AzFramework void SpawnableSystemComponent::OnTick(float /*deltaTime*/, AZ::ScriptTimePoint /*time*/) { - m_entitiesManager.ProcessQueue( - SpawnableEntitiesManager::CommandQueuePriority::High | SpawnableEntitiesManager::CommandQueuePriority::Regular); + ProcessSpawnableQueue(); RootSpawnableNotificationBus::ExecuteQueuedEvents(); } @@ -121,6 +120,12 @@ namespace AzFramework m_rootSpawnableId = AZ::Data::AssetId(); } + void SpawnableSystemComponent::ProcessSpawnableQueue() + { + m_entitiesManager.ProcessQueue( + SpawnableEntitiesManager::CommandQueuePriority::High | SpawnableEntitiesManager::CommandQueuePriority::Regular); + } + void SpawnableSystemComponent::OnRootSpawnableAssigned([[maybe_unused]] AZ::Data::Asset rootSpawnable, [[maybe_unused]] uint32_t generation) { @@ -161,6 +166,8 @@ namespace AzFramework void SpawnableSystemComponent::Deactivate() { + ProcessSpawnableQueue(); + m_registryChangeHandler.Disconnect(); AZ::TickBus::Handler::BusDisconnect(); diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.h index 5b5fb1b7ee..74e255d624 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.h @@ -75,6 +75,7 @@ namespace AzFramework uint64_t AssignRootSpawnable(AZ::Data::Asset rootSpawnable) override; void ReleaseRootSpawnable() override; + void ProcessSpawnableQueue() override; // // RootSpawnbleNotificationBus diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Platform/Linux/platform_linux_files.cmake b/Code/Framework/AzQtComponents/AzQtComponents/Platform/Linux/platform_linux_files.cmake index ec71d7b508..f292c29c3c 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Platform/Linux/platform_linux_files.cmake +++ b/Code/Framework/AzQtComponents/AzQtComponents/Platform/Linux/platform_linux_files.cmake @@ -12,6 +12,7 @@ set(FILES ../../Utilities/QtWindowUtilities_linux.cpp ../../Utilities/ScreenGrabber_linux.cpp ../../../Platform/Linux/AzQtComponents/Components/StyledDockWidget_Linux.cpp + ../../../Platform/Linux/AzQtComponents/Utilities/DesktopUtilities_Linux.cpp ../../../Platform/Linux/AzQtComponents/AzQtComponents_Traits_Linux.h ../../../Platform/Linux/AzQtComponents/AzQtComponents_Traits_Platform.h ) diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Platform/Mac/platform_mac_files.cmake b/Code/Framework/AzQtComponents/AzQtComponents/Platform/Mac/platform_mac_files.cmake index 18f9479289..4addf53599 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Platform/Mac/platform_mac_files.cmake +++ b/Code/Framework/AzQtComponents/AzQtComponents/Platform/Mac/platform_mac_files.cmake @@ -12,6 +12,7 @@ set(FILES ../../Utilities/QtWindowUtilities_mac.mm ../../Utilities/ScreenGrabber_mac.mm ../../../Platform/Mac/AzQtComponents/Components/StyledDockWidget_Mac.cpp + ../../../Platform/Mac/AzQtComponents/Utilities/DesktopUtilities_Mac.cpp ../../../Platform/Mac/AzQtComponents/AzQtComponents_Traits_Mac.h ../../../Platform/Mac/AzQtComponents/AzQtComponents_Traits_Platform.h ) diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Platform/Windows/platform_windows_files.cmake b/Code/Framework/AzQtComponents/AzQtComponents/Platform/Windows/platform_windows_files.cmake index ca4145ef35..bf0d0bb785 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Platform/Windows/platform_windows_files.cmake +++ b/Code/Framework/AzQtComponents/AzQtComponents/Platform/Windows/platform_windows_files.cmake @@ -9,6 +9,7 @@ set(FILES ../../natvis/qt.natvis ../../../Platform/Windows/AzQtComponents/Utilities/HandleDpiAwareness_Windows.cpp + ../../../Platform/Windows/AzQtComponents/Utilities/DesktopUtilities_Windows.cpp ../../Utilities/MouseHider_win.cpp ../../Utilities/QtWindowUtilities_win.cpp ../../Utilities/ScreenGrabber_win.cpp diff --git a/Code/Framework/AzQtComponents/AzQtComponents/azqtcomponents_files.cmake b/Code/Framework/AzQtComponents/AzQtComponents/azqtcomponents_files.cmake index 8219ab04b2..4c343b852c 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/azqtcomponents_files.cmake +++ b/Code/Framework/AzQtComponents/AzQtComponents/azqtcomponents_files.cmake @@ -271,7 +271,6 @@ set(FILES Utilities/ColorUtilities.h Utilities/Conversions.h Utilities/Conversions.cpp - Utilities/DesktopUtilities.cpp Utilities/DesktopUtilities.h Utilities/HandleDpiAwareness.cpp Utilities/HandleDpiAwareness.h diff --git a/Code/Framework/AzQtComponents/Platform/Linux/AzQtComponents/Utilities/DesktopUtilities_Linux.cpp b/Code/Framework/AzQtComponents/Platform/Linux/AzQtComponents/Utilities/DesktopUtilities_Linux.cpp new file mode 100644 index 0000000000..e4ddaceec7 --- /dev/null +++ b/Code/Framework/AzQtComponents/Platform/Linux/AzQtComponents/Utilities/DesktopUtilities_Linux.cpp @@ -0,0 +1,49 @@ +/* + * 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 + +namespace AzQtComponents +{ + void ShowFileOnDesktop(const QString& path) + { + const char* defaultNautilusPath = "/usr/bin/nautilus"; + const char* defaultXdgOpenPath = "/usr/bin/xdg-open"; + + // Determine if Nautilus (for Gnome Desktops) is available because it supports opening the file manager + // and selecting a specific file + bool nautilusAvailable = QFileInfo(defaultNautilusPath).exists(); + + QFileInfo pathInfo(path); + if (pathInfo.isDir()) + { + QProcess::startDetached(defaultXdgOpenPath, { path }); + } + else + { + if (nautilusAvailable) + { + QProcess::startDetached(defaultNautilusPath, { "--select", path }); + } + else + { + QDir parentDir { pathInfo.dir() }; + QProcess::startDetached(defaultXdgOpenPath, { parentDir.path() }); + } + } + } + + QString fileBrowserActionName() + { + const char* exploreActionName = "Open in file browser"; + return QObject::tr(exploreActionName); + } +} diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Utilities/DesktopUtilities.cpp b/Code/Framework/AzQtComponents/Platform/Mac/AzQtComponents/Utilities/DesktopUtilities_Mac.cpp similarity index 67% rename from Code/Framework/AzQtComponents/AzQtComponents/Utilities/DesktopUtilities.cpp rename to Code/Framework/AzQtComponents/Platform/Mac/AzQtComponents/Utilities/DesktopUtilities_Mac.cpp index c210f17136..5920dc76c6 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Utilities/DesktopUtilities.cpp +++ b/Code/Framework/AzQtComponents/Platform/Mac/AzQtComponents/Utilities/DesktopUtilities_Mac.cpp @@ -15,21 +15,6 @@ namespace AzQtComponents { void ShowFileOnDesktop(const QString& path) { -#if defined(AZ_PLATFORM_WINDOWS) - - // Launch explorer at the path provided - QStringList args; - if (!QFileInfo(path).isDir()) - { - // Folders are just opened, files are selected - args << "/select,"; - } - args << QDir::toNativeSeparators(path); - - QProcess::startDetached("explorer", args); - -#else - if (QFileInfo(path).isDir()) { QProcess::startDetached("/usr/bin/osascript", { "-e", @@ -43,19 +28,11 @@ namespace AzQtComponents QProcess::startDetached("/usr/bin/osascript", { "-e", QStringLiteral("tell application \"Finder\" to activate") }); - -#endif } QString fileBrowserActionName() { -#ifdef AZ_PLATFORM_WINDOWS - const char* exploreActionName = "Open in Explorer"; -#elif defined(AZ_PLATFORM_MAC) const char* exploreActionName = "Open in Finder"; -#else - const char* exploreActionName = "Open in file browser"; -#endif return QObject::tr(exploreActionName); } } diff --git a/Code/Framework/AzQtComponents/Platform/Windows/AzQtComponents/Utilities/DesktopUtilities_Windows.cpp b/Code/Framework/AzQtComponents/Platform/Windows/AzQtComponents/Utilities/DesktopUtilities_Windows.cpp new file mode 100644 index 0000000000..796e2c0ccf --- /dev/null +++ b/Code/Framework/AzQtComponents/Platform/Windows/AzQtComponents/Utilities/DesktopUtilities_Windows.cpp @@ -0,0 +1,35 @@ +/* + * 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 + +namespace AzQtComponents +{ + void ShowFileOnDesktop(const QString& path) + { + // Launch explorer at the path provided + QStringList args; + if (!QFileInfo(path).isDir()) + { + // Folders are just opened, files are selected + args << "/select,"; + } + args << QDir::toNativeSeparators(path); + + QProcess::startDetached("explorer", args); + } + + QString fileBrowserActionName() + { + const char* exploreActionName = "Open in Explorer"; + return QObject::tr(exploreActionName); + } +} 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/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/Picking/Manipulators/ManipulatorBounds.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Picking/Manipulators/ManipulatorBounds.cpp index 3a7fffb3be..4c7afa2275 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Picking/Manipulators/ManipulatorBounds.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Picking/Manipulators/ManipulatorBounds.cpp @@ -116,7 +116,7 @@ namespace AzToolsFramework { return AZ::Intersect::IntersectRayBox( rayOrigin, rayDirection, m_center, m_axis1, m_axis2, m_axis3, m_halfExtents.GetX(), m_halfExtents.GetY(), - m_halfExtents.GetZ(), rayIntersectionDistance) > 0; + m_halfExtents.GetZ(), rayIntersectionDistance); } void ManipulatorBoundBox::SetShapeData(const BoundRequestShapeBase& shapeData) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabDomUtils.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabDomUtils.cpp index ea0fa55256..a84d6bf706 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabDomUtils.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabDomUtils.cpp @@ -262,7 +262,7 @@ namespace AzToolsFramework if (assetId.IsValid()) { - asset.Create(assetId, true); + asset.Create(assetId, false); } } }; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp index b5f61b33bf..081655a166 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp @@ -43,6 +43,12 @@ namespace AzToolsFramework m_instanceToTemplateInterface = AZ::Interface::Get(); AZ_Assert(m_instanceToTemplateInterface, "PrefabPublicHandler - Could not retrieve instance of InstanceToTemplateInterface"); + m_prefabFocusInterface = AZ::Interface::Get(); + AZ_Assert(m_prefabFocusInterface, "Could not get PrefabFocusInterface on PrefabPublicHandler construction."); + + m_prefabFocusPublicInterface = AZ::Interface::Get(); + AZ_Assert(m_prefabFocusPublicInterface, "Could not get PrefabFocusPublicInterface on PrefabPublicHandler construction."); + m_prefabLoaderInterface = AZ::Interface::Get(); AZ_Assert(m_prefabLoaderInterface, "Could not get PrefabLoaderInterface on PrefabPublicHandler construction."); @@ -552,6 +558,13 @@ namespace AzToolsFramework PrefabEntityResult PrefabPublicHandler::CreateEntity(AZ::EntityId parentId, const AZ::Vector3& position) { + // If the parent is invalid, parent to the container of the currently focused prefab. + if (!parentId.IsValid()) + { + AzFramework::EntityContextId editorEntityContextId = AzToolsFramework::GetEntityContextId(); + parentId = m_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId); + } + InstanceOptionalReference owningInstanceOfParentEntity = GetOwnerInstanceByEntityId(parentId); if (!owningInstanceOfParentEntity) { @@ -968,13 +981,13 @@ namespace AzToolsFramework return AZ::Failure(AZStd::string("No entities to duplicate.")); } - const EntityIdList entityIdsNoLevelInstance = GenerateEntityIdListWithoutLevelInstance(entityIds); - if (entityIdsNoLevelInstance.empty()) + const EntityIdList entityIdsNoFocusContainer = GenerateEntityIdListWithoutFocusedInstanceContainer(entityIds); + if (entityIdsNoFocusContainer.empty()) { - return AZ::Failure(AZStd::string("No entities to duplicate because only instance selected is the level instance.")); + return AZ::Failure(AZStd::string("No entities to duplicate because only instance selected is the container entity of the focused instance.")); } - if (!EntitiesBelongToSameInstance(entityIdsNoLevelInstance)) + if (!EntitiesBelongToSameInstance(entityIdsNoFocusContainer)) { return AZ::Failure(AZStd::string("Cannot duplicate multiple entities belonging to different instances with one operation." "Change your selection to contain entities in the same instance.")); @@ -982,7 +995,7 @@ namespace AzToolsFramework // We've already verified the entities are all owned by the same instance, // so we can just retrieve our instance from the first entity in the list. - AZ::EntityId firstEntityIdToDuplicate = entityIdsNoLevelInstance[0]; + AZ::EntityId firstEntityIdToDuplicate = entityIdsNoFocusContainer[0]; InstanceOptionalReference commonOwningInstance = GetOwnerInstanceByEntityId(firstEntityIdToDuplicate); if (!commonOwningInstance.has_value()) { @@ -1002,7 +1015,7 @@ namespace AzToolsFramework // This will cull out any entities that have ancestors in the list, since we will end up duplicating // the full nested hierarchy with what is returned from RetrieveAndSortPrefabEntitiesAndInstances - AzToolsFramework::EntityIdSet duplicationSet = AzToolsFramework::GetCulledEntityHierarchy(entityIdsNoLevelInstance); + AzToolsFramework::EntityIdSet duplicationSet = AzToolsFramework::GetCulledEntityHierarchy(entityIdsNoFocusContainer); AZ_PROFILE_FUNCTION(AzToolsFramework); @@ -1106,19 +1119,21 @@ namespace AzToolsFramework PrefabOperationResult PrefabPublicHandler::DeleteFromInstance(const EntityIdList& entityIds, bool deleteDescendants) { - const EntityIdList entityIdsNoLevelInstance = GenerateEntityIdListWithoutLevelInstance(entityIds); + // Remove the container entity of the focused prefab from the list, if it is included. + const EntityIdList entityIdsNoFocusContainer = GenerateEntityIdListWithoutFocusedInstanceContainer(entityIds); - if (entityIdsNoLevelInstance.empty()) + if (entityIdsNoFocusContainer.empty()) { return AZ::Success(); } - if (!EntitiesBelongToSameInstance(entityIdsNoLevelInstance)) + // All entities in this list need to belong to the same prefab instance for the operation to be valid. + if (!EntitiesBelongToSameInstance(entityIdsNoFocusContainer)) { return AZ::Failure(AZStd::string("Cannot delete multiple entities belonging to different instances with one operation.")); } - AZ::EntityId firstEntityIdToDelete = entityIdsNoLevelInstance[0]; + AZ::EntityId firstEntityIdToDelete = entityIdsNoFocusContainer[0]; InstanceOptionalReference commonOwningInstance = GetOwnerInstanceByEntityId(firstEntityIdToDelete); // If the first entity id is a container entity id, then we need to mark its parent as the common owning instance because you @@ -1128,8 +1143,15 @@ namespace AzToolsFramework commonOwningInstance = commonOwningInstance->get().GetParentInstance(); } + // We only allow explicit deletions for entities inside the currently focused prefab. + AzFramework::EntityContextId editorEntityContextId = AzToolsFramework::GetEntityContextId(); + if (&m_prefabFocusInterface->GetFocusedPrefabInstance(editorEntityContextId)->get() != &commonOwningInstance->get()) + { + return AZ::Failure(AZStd::string("Cannot delete entities belonging to an instance that is not being edited.")); + } + // Retrieve entityList from entityIds - EntityList inputEntityList = EntityIdListToEntityList(entityIdsNoLevelInstance); + EntityList inputEntityList = EntityIdListToEntityList(entityIdsNoFocusContainer); AZ_PROFILE_FUNCTION(AzToolsFramework); @@ -1186,7 +1208,7 @@ namespace AzToolsFramework } else { - for (AZ::EntityId entityId : entityIdsNoLevelInstance) + for (AZ::EntityId entityId : entityIdsNoFocusContainer) { InstanceOptionalReference owningInstance = m_instanceEntityMapperInterface->FindOwningInstance(entityId); // If this is the container entity, it actually represents the instance so get its owner @@ -1227,9 +1249,12 @@ namespace AzToolsFramework return AZ::Failure(AZStd::string("Cannot detach Prefab Instance with invalid container entity.")); } - if (IsLevelInstanceContainerEntity(containerEntityId)) + auto editorEntityContextId = AzFramework::EntityContextId::CreateNull(); + EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId); + + if (containerEntityId == m_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId)) { - return AZ::Failure(AZStd::string("Cannot detach level Prefab Instance.")); + return AZ::Failure(AZStd::string("Cannot detach focused Prefab Instance.")); } InstanceOptionalReference owningInstance = GetOwnerInstanceByEntityId(containerEntityId); @@ -1452,9 +1477,14 @@ namespace AzToolsFramework AZStd::queue entityQueue; + auto editorEntityContextId = AzFramework::EntityContextId::CreateNull(); + EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId); + + AZ::EntityId focusedPrefabContainerEntityId = + m_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId); for (auto inputEntity : inputEntities) { - if (inputEntity && !IsLevelInstanceContainerEntity(inputEntity->GetId())) + if (inputEntity && inputEntity->GetId() != focusedPrefabContainerEntityId) { entityQueue.push(inputEntity); } @@ -1548,19 +1578,19 @@ namespace AzToolsFramework return AZ::Success(); } - EntityIdList PrefabPublicHandler::GenerateEntityIdListWithoutLevelInstance( + EntityIdList PrefabPublicHandler::GenerateEntityIdListWithoutFocusedInstanceContainer( const EntityIdList& entityIds) const { - EntityIdList outEntityIds; - outEntityIds.reserve(entityIds.size()); // Actual size could be smaller. + EntityIdList outEntityIds(entityIds); + + AzFramework::EntityContextId editorEntityContextId = AzToolsFramework::GetEntityContextId(); + AZ::EntityId focusedInstanceContainerEntityId = m_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId); - for (const AZ::EntityId& entityId : entityIds) + if (auto iter = AZStd::find(outEntityIds.begin(), outEntityIds.end(), focusedInstanceContainerEntityId); iter != outEntityIds.end()) { - if (!IsLevelInstanceContainerEntity(entityId)) - { - outEntityIds.emplace_back(entityId); - } + outEntityIds.erase(iter); } + return outEntityIds; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h index f3c0e67d46..a9dadc3336 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h @@ -74,7 +74,7 @@ namespace AzToolsFramework Instance& commonRootEntityOwningInstance, EntityList& outEntities, AZStd::vector& outInstances) const; - EntityIdList GenerateEntityIdListWithoutLevelInstance(const EntityIdList& entityIds) const; + EntityIdList GenerateEntityIdListWithoutFocusedInstanceContainer(const EntityIdList& entityIds) const; InstanceOptionalReference GetOwnerInstanceByEntityId(AZ::EntityId entityId) const; bool EntitiesBelongToSameInstance(const EntityIdList& entityIds) const; @@ -187,6 +187,8 @@ namespace AzToolsFramework InstanceEntityMapperInterface* m_instanceEntityMapperInterface = nullptr; InstanceToTemplateInterface* m_instanceToTemplateInterface = nullptr; + PrefabFocusInterface* m_prefabFocusInterface = nullptr; + PrefabFocusPublicInterface* m_prefabFocusPublicInterface = nullptr; PrefabLoaderInterface* m_prefabLoaderInterface = nullptr; PrefabSystemComponentInterface* m_prefabSystemComponentInterface = nullptr; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp index 6ffa3aab69..16e3c047b5 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -175,12 +176,16 @@ namespace AzToolsFramework AzFramework::ApplicationRequests::Bus::BroadcastResult( prefabWipFeaturesEnabled, &AzFramework::ApplicationRequests::ArePrefabWipFeaturesEnabled); + auto editorEntityContextId = AzFramework::EntityContextId::CreateNull(); + EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId); + // Create Prefab { if (!selectedEntities.empty()) { - // Hide if the only selected entity is the Level Container - if (selectedEntities.size() > 1 || !s_prefabPublicInterface->IsLevelInstanceContainerEntity(selectedEntities[0])) + // Hide if the only selected entity is the Focused Instance Container + if (selectedEntities.size() > 1 || + selectedEntities[0] != s_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId)) { bool layerInSelection = false; @@ -247,14 +252,14 @@ namespace AzToolsFramework // Edit Prefab if (prefabWipFeaturesEnabled && !s_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(selectedEntity)) { - QAction* editAction = menu->addAction(QObject::tr("Edit Prefab")); - editAction->setToolTip(QObject::tr("Edit the prefab in focus mode.")); + QAction* editAction = menu->addAction(QObject::tr("Edit Prefab")); + editAction->setToolTip(QObject::tr("Edit the prefab in focus mode.")); - QObject::connect(editAction, &QAction::triggered, editAction, [selectedEntity] { - ContextMenu_EditPrefab(selectedEntity); - }); + QObject::connect(editAction, &QAction::triggered, editAction, [selectedEntity] { + ContextMenu_EditPrefab(selectedEntity); + }); - itemWasShown = true; + itemWasShown = true; } // Save Prefab @@ -283,8 +288,9 @@ namespace AzToolsFramework QAction* deleteAction = menu->addAction(QObject::tr("Delete")); QObject::connect(deleteAction, &QAction::triggered, deleteAction, [] { ContextMenu_DeleteSelected(); }); - if (selectedEntities.size() == 0 || - (selectedEntities.size() == 1 && s_prefabPublicInterface->IsLevelInstanceContainerEntity(selectedEntities[0]))) + + if (selectedEntities.empty() || + (selectedEntities.size() == 1 && selectedEntities[0] == s_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId))) { deleteAction->setDisabled(true); } @@ -292,17 +298,17 @@ namespace AzToolsFramework // Detach Prefab if (selectedEntities.size() == 1) { - AZ::EntityId selectedEntity = selectedEntities[0]; + AZ::EntityId selectedEntityId = selectedEntities[0]; - if (s_prefabPublicInterface->IsInstanceContainerEntity(selectedEntity) && - !s_prefabPublicInterface->IsLevelInstanceContainerEntity(selectedEntity)) + if (s_prefabPublicInterface->IsInstanceContainerEntity(selectedEntityId) && + selectedEntityId != s_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId)) { QAction* detachPrefabAction = menu->addAction(QObject::tr("Detach Prefab...")); QObject::connect( detachPrefabAction, &QAction::triggered, detachPrefabAction, - [selectedEntity] + [selectedEntityId] { - ContextMenu_DetachPrefab(selectedEntity); + ContextMenu_DetachPrefab(selectedEntityId); }); } } @@ -331,13 +337,21 @@ namespace AzToolsFramework QWidget* activeWindow = QApplication::activeWindow(); const AZStd::string prefabFilesPath = "@projectroot@/Prefabs"; - // Remove Level entity if it's part of the list - - auto levelContainerIter = - AZStd::find(selectedEntities.begin(), selectedEntities.end(), s_prefabPublicInterface->GetLevelInstanceContainerEntityId()); - if (levelContainerIter != selectedEntities.end()) + // Remove focused instance container entity if it's part of the list + auto editorEntityContextId = AzFramework::EntityContextId::CreateNull(); + EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId); + + auto focusedContainerIter = AZStd::find( + selectedEntities.begin(), selectedEntities.end(), + s_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId)); + if (focusedContainerIter != selectedEntities.end()) { - selectedEntities.erase(levelContainerIter); + selectedEntities.erase(focusedContainerIter); + } + + if (selectedEntities.empty()) + { + return; } // Set default folder for prefabs diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp index 00522b29dc..ccad85e32b 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp @@ -178,12 +178,6 @@ namespace AzToolsFramework AZ::EntityId entityId(index.data(EntityOutlinerListModel::EntityIdRole).value()); - // We hide the root instance container entity from the Outliner, so avoid drawing its full container on children - if (m_prefabPublicInterface->IsLevelInstanceContainerEntity(entityId)) - { - return; - } - const QTreeView* outlinerTreeView(qobject_cast(option.widget)); const int ancestorLeft = outlinerTreeView->visualRect(index).left() + (m_prefabBorderThickness / 2) - 1; const int curveRectSize = m_prefabCapsuleRadius * 2; diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.azsl index 05059aba6f..523353f8fa 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.azsl @@ -9,7 +9,7 @@ #include "Skin_Common.azsli" // SRGs -#include +#include #include // Pass Output diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.materialtype b/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.materialtype index 0969cc36e8..e4da9c6022 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.materialtype +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.materialtype @@ -973,15 +973,15 @@ "tag": "ForwardPass" }, { - "file": "Shaders/Shadow/Shadowmap.shader", + "file": "Shaders/Shadow/ShadowmapSkin.shader", "tag": "Shadowmap" }, { - "file": "Shaders/Depth/DepthPass.shader", + "file": "Shaders/Depth/DepthPassSkin.shader", "tag": "DepthPass" }, { - "file": "Shaders/MotionVector/MeshMotionVector.shader", + "file": "Shaders/MotionVector/MeshMotionVectorSkin.shader", "tag": "MeshMotionVector" } ], diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/DefaultObjectSrg.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/DefaultObjectSrg.azsli index 7a2d2ee428..10526597cb 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/DefaultObjectSrg.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/DefaultObjectSrg.azsli @@ -27,16 +27,6 @@ ShaderResourceGroup ObjectSrg : SRG_PerObject return SceneSrg::GetObjectToWorldInverseTransposeMatrix(m_objectId); } - //[GFX TODO][ATOM-15280] Move wrinkle mask data from the default object srg into something specific to the Skin shader - uint m_wrinkle_mask_count; - float4 m_wrinkle_mask_weights[4]; - Texture2D m_wrinkle_masks[16]; - - float GetWrinkleMaskWeight(uint index) - { - return m_wrinkle_mask_weights[index / 4][index % 4]; - } - //! Reflection Probe (smallest probe volume that overlaps the object position) struct ReflectionProbeData { diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Skin/SkinObjectSrg.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Skin/SkinObjectSrg.azsli new file mode 100644 index 0000000000..d0766c295d --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Skin/SkinObjectSrg.azsli @@ -0,0 +1,81 @@ +/* + * 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 + +ShaderResourceGroup ObjectSrg : SRG_PerObject +{ + uint m_objectId; + + //! Returns the matrix for transforming points from Object Space to World Space. + float4x4 GetWorldMatrix() + { + return SceneSrg::GetObjectToWorldMatrix(m_objectId); + } + + //! Returns the inverse-transpose of the world matrix. + //! Commonly used to transform normals while supporting non-uniform scale. + float3x3 GetWorldMatrixInverseTranspose() + { + return SceneSrg::GetObjectToWorldInverseTransposeMatrix(m_objectId); + } + + uint m_wrinkle_mask_count; + float4 m_wrinkle_mask_weights[4]; + Texture2D m_wrinkle_masks[16]; + + float GetWrinkleMaskWeight(uint index) + { + return m_wrinkle_mask_weights[index / 4][index % 4]; + } + + //! Reflection Probe (smallest probe volume that overlaps the object position) + struct ReflectionProbeData + { + row_major float3x4 m_modelToWorld; + row_major float3x4 m_modelToWorldInverse; // does not include extents + float3 m_outerObbHalfLengths; + float3 m_innerObbHalfLengths; + float m_padding; + bool m_useReflectionProbe; + bool m_useParallaxCorrection; + }; + + ReflectionProbeData m_reflectionProbeData; + TextureCube m_reflectionProbeCubeMap; + + float4x4 GetReflectionProbeWorldMatrix() + { + float4x4 modelToWorld = float4x4( + float4(1, 0, 0, 0), + float4(0, 1, 0, 0), + float4(0, 0, 1, 0), + float4(0, 0, 0, 1)); + + modelToWorld[0] = m_reflectionProbeData.m_modelToWorld[0]; + modelToWorld[1] = m_reflectionProbeData.m_modelToWorld[1]; + modelToWorld[2] = m_reflectionProbeData.m_modelToWorld[2]; + return modelToWorld; + } + + float4x4 GetReflectionProbeWorldMatrixInverse() + { + float4x4 modelToWorldInverse = float4x4( + float4(1, 0, 0, 0), + float4(0, 1, 0, 0), + float4(0, 0, 1, 0), + float4(0, 0, 0, 1)); + + modelToWorldInverse[0] = m_reflectionProbeData.m_modelToWorldInverse[0]; + modelToWorldInverse[1] = m_reflectionProbeData.m_modelToWorldInverse[1]; + modelToWorldInverse[2] = m_reflectionProbeData.m_modelToWorldInverse[2]; + return modelToWorldInverse; + } +} diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Depth/DepthPass.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/Depth/DepthPass.azsl index 8eb657f8f5..876f4b1a61 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/Depth/DepthPass.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Depth/DepthPass.azsl @@ -6,30 +6,7 @@ * */ -#include #include +#include -struct VSInput -{ - float3 m_position : POSITION; -}; - -struct VSDepthOutput -{ - float4 m_position : SV_Position; -}; - -VSDepthOutput DepthPassVS(VSInput IN) -{ - VSDepthOutput OUT; - - float4x4 objectToWorld = ObjectSrg::GetWorldMatrix(); - float4 worldPosition = mul(objectToWorld, float4(IN.m_position, 1.0)); - OUT.m_position = mul(ViewSrg::m_viewProjectionMatrix, worldPosition); - - return OUT; -} - - - - +// Use the depth pass shader with the default object srg diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Depth/DepthPassCommon.azsli b/Gems/Atom/Feature/Common/Assets/Shaders/Depth/DepthPassCommon.azsli new file mode 100644 index 0000000000..f658dd13da --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Depth/DepthPassCommon.azsli @@ -0,0 +1,36 @@ +/* + * 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 + +struct VSInput +{ + float3 m_position : POSITION; +}; + +struct VSDepthOutput +{ + float4 m_position : SV_Position; +}; + +VSDepthOutput DepthPassVS(VSInput IN) +{ + VSDepthOutput OUT; + + float4x4 objectToWorld = ObjectSrg::GetWorldMatrix(); + float4 worldPosition = mul(objectToWorld, float4(IN.m_position, 1.0)); + OUT.m_position = mul(ViewSrg::m_viewProjectionMatrix, worldPosition); + + return OUT; +} + + + + diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Depth/DepthPassSkin.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/Depth/DepthPassSkin.azsl new file mode 100644 index 0000000000..0947be6cf8 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Depth/DepthPassSkin.azsl @@ -0,0 +1,12 @@ +/* + * 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 + +// Use the depth pass shader with the skin object srg diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Depth/DepthPassSkin.shader b/Gems/Atom/Feature/Common/Assets/Shaders/Depth/DepthPassSkin.shader new file mode 100644 index 0000000000..de6c989223 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Depth/DepthPassSkin.shader @@ -0,0 +1,24 @@ +{ + "Source" : "DepthPassSkin", + + "DepthStencilState" : { + "Depth" : { "Enable" : true, "CompareFunc" : "GreaterEqual" } + }, + + "CompilerHints" : { + "DisableOptimizations" : false + }, + + "ProgramSettings" : + { + "EntryPoints": + [ + { + "name": "DepthPassVS", + "type" : "Vertex" + } + ] + }, + + "DrawList" : "depth" +} diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVector.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVector.azsl index 11cf20dcaf..a14d07194f 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVector.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVector.azsl @@ -6,77 +6,7 @@ * */ -#include -#include - #include -#include - -struct VSInput -{ - float3 m_position : POSITION; - - // This gets set automatically by the system at runtime only if it's available. - // There is a soft naming convention that associates this with o_prevPosition_isBound, which will be set to true whenever m_optional_prevPosition is available. - // (search "m_optional_" in ShaderVariantAssetBuilder for details on the naming convention). - // [GFX TODO][ATOM-14475]: Come up with a more elegant way to associate the isBound flag with the input stream. - // Vertex position of last frame to capture small scale motion due to vertex animation - float3 m_optional_prevPosition : POSITIONT; -}; - -struct VSOutput -{ - float4 m_position : SV_Position; - float3 m_worldPos : TEXCOORD0; - float3 m_worldPosPrev: TEXCOORD1; -}; - -struct PSOutput -{ - float2 m_motion : SV_Target0; -}; - -// Indicates whether the vertex input struct's "m_optional_prevPosition" is bound. If false, it is not safe to read from m_optional_prevPosition. -// This option gets set automatically by the system at runtime; there is a soft naming convention that associates it with m_optional_prevPosition. -// (search "m_optional_" in ShaderVariantAssetBuilder for details on the naming convention). -// [GFX TODO][ATOM-14475]: Come up with a more elegant way to associate the isBound flag with the input stream. -option bool o_prevPosition_isBound; - -VSOutput MainVS(VSInput IN) -{ - VSOutput OUT; - - OUT.m_worldPos = mul(SceneSrg::GetObjectToWorldMatrix(ObjectSrg::m_objectId), float4(IN.m_position, 1.0)).xyz; - OUT.m_position = mul(ViewSrg::m_viewProjectionMatrix, float4(OUT.m_worldPos, 1.0)); - - if (o_prevPosition_isBound) - { - OUT.m_worldPosPrev = mul(SceneSrg::GetObjectToWorldMatrixPrev(ObjectSrg::m_objectId), float4(IN.m_optional_prevPosition, 1.0)).xyz; - } - else - { - OUT.m_worldPosPrev = mul(SceneSrg::GetObjectToWorldMatrixPrev(ObjectSrg::m_objectId), float4(IN.m_position, 1.0)).xyz; - } - - return OUT; -} - -PSOutput MainPS(VSOutput IN) -{ - PSOutput OUT; - - // Current clip position - float4 clipPos = mul(ViewSrg::m_viewProjectionMatrix, float4(IN.m_worldPos, 1.0)); - - // Reprojected last frame's clip position, for skinned mesh it also implies last key frame - float4 clipPosPrev = mul(ViewSrg::m_viewProjectionPrevMatrix, float4(IN.m_worldPosPrev, 1.0)); - - float2 motion = (clipPos.xy / clipPos.w - clipPosPrev.xy / clipPosPrev.w) * 0.5; +#include - OUT.m_motion = motion; - - // Flip y to line up with uv coordinates - OUT.m_motion.y = -OUT.m_motion.y; - - return OUT; -} +// Use the mesh motion vector with the default object srg diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVectorCommon.azsli b/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVectorCommon.azsli new file mode 100644 index 0000000000..c934b056db --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVectorCommon.azsli @@ -0,0 +1,83 @@ +/* + * 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 + +struct VSInput +{ + float3 m_position : POSITION; + + // This gets set automatically by the system at runtime only if it's available. + // There is a soft naming convention that associates this with o_prevPosition_isBound, which will be set to true whenever m_optional_prevPosition is available. + // (search "m_optional_" in ShaderVariantAssetBuilder for details on the naming convention). + // [GFX TODO][ATOM-14475]: Come up with a more elegant way to associate the isBound flag with the input stream. + // Vertex position of last frame to capture small scale motion due to vertex animation + float3 m_optional_prevPosition : POSITIONT; +}; + +struct VSOutput +{ + float4 m_position : SV_Position; + float3 m_worldPos : TEXCOORD0; + float3 m_worldPosPrev: TEXCOORD1; +}; + +struct PSOutput +{ + float2 m_motion : SV_Target0; +}; + +// Indicates whether the vertex input struct's "m_optional_prevPosition" is bound. If false, it is not safe to read from m_optional_prevPosition. +// This option gets set automatically by the system at runtime; there is a soft naming convention that associates it with m_optional_prevPosition. +// (search "m_optional_" in ShaderVariantAssetBuilder for details on the naming convention). +// [GFX TODO][ATOM-14475]: Come up with a more elegant way to associate the isBound flag with the input stream. +option bool o_prevPosition_isBound; + +VSOutput MainVS(VSInput IN) +{ + VSOutput OUT; + + OUT.m_worldPos = mul(SceneSrg::GetObjectToWorldMatrix(ObjectSrg::m_objectId), float4(IN.m_position, 1.0)).xyz; + OUT.m_position = mul(ViewSrg::m_viewProjectionMatrix, float4(OUT.m_worldPos, 1.0)); + + if (o_prevPosition_isBound) + { + OUT.m_worldPosPrev = mul(SceneSrg::GetObjectToWorldMatrixPrev(ObjectSrg::m_objectId), float4(IN.m_optional_prevPosition, 1.0)).xyz; + } + else + { + OUT.m_worldPosPrev = mul(SceneSrg::GetObjectToWorldMatrixPrev(ObjectSrg::m_objectId), float4(IN.m_position, 1.0)).xyz; + } + + return OUT; +} + +PSOutput MainPS(VSOutput IN) +{ + PSOutput OUT; + + // Current clip position + float4 clipPos = mul(ViewSrg::m_viewProjectionMatrix, float4(IN.m_worldPos, 1.0)); + + // Reprojected last frame's clip position, for skinned mesh it also implies last key frame + float4 clipPosPrev = mul(ViewSrg::m_viewProjectionPrevMatrix, float4(IN.m_worldPosPrev, 1.0)); + + float2 motion = (clipPos.xy / clipPos.w - clipPosPrev.xy / clipPosPrev.w) * 0.5; + + OUT.m_motion = motion; + + // Flip y to line up with uv coordinates + OUT.m_motion.y = -OUT.m_motion.y; + + return OUT; +} diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVectorSkin.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVectorSkin.azsl new file mode 100644 index 0000000000..fc089ac7a3 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVectorSkin.azsl @@ -0,0 +1,12 @@ +/* + * 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 + +// Use the mesh motion vector with the skin object srg diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVectorSkin.shader b/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVectorSkin.shader new file mode 100644 index 0000000000..9a50e4e2cf --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/MotionVector/MeshMotionVectorSkin.shader @@ -0,0 +1,24 @@ +{ + "Source" : "MeshMotionVectorSkin", + + "DepthStencilState" : { + "Depth" : { "Enable" : true, "CompareFunc" : "GreaterEqual" } + }, + + "DrawList" : "motion", + + "ProgramSettings": + { + "EntryPoints": + [ + { + "name": "MainVS", + "type": "Vertex" + }, + { + "name": "MainPS", + "type": "Fragment" + } + ] + } +} diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/Shadowmap.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/Shadowmap.azsl index 713ff7c393..7a958a73ca 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/Shadowmap.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/Shadowmap.azsl @@ -6,27 +6,7 @@ * */ -#include -#include #include +#include -struct VertexInput -{ - float3 m_position : POSITION; -}; - -struct VertexOutput -{ - float4 m_position : SV_Position; -}; - -VertexOutput MainVS(VertexInput input) -{ - const float4x4 worldMatrix = ObjectSrg::GetWorldMatrix(); - VertexOutput output; - - const float3 worldPosition = mul(worldMatrix, float4(input.m_position, 1.0)).xyz; - output.m_position = mul(ViewSrg::m_viewProjectionMatrix, float4(worldPosition, 1.0)); - - return output; -} +// Use the shadowmap shader with the default object srg diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/ShadowmapCommon.azsli b/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/ShadowmapCommon.azsli new file mode 100644 index 0000000000..213fb18dc4 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/ShadowmapCommon.azsli @@ -0,0 +1,33 @@ +/* + * 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 + +struct VertexInput +{ + float3 m_position : POSITION; +}; + +struct VertexOutput +{ + float4 m_position : SV_Position; +}; + +VertexOutput MainVS(VertexInput input) +{ + const float4x4 worldMatrix = ObjectSrg::GetWorldMatrix(); + VertexOutput output; + + const float3 worldPosition = mul(worldMatrix, float4(input.m_position, 1.0)).xyz; + output.m_position = mul(ViewSrg::m_viewProjectionMatrix, float4(worldPosition, 1.0)); + + return output; +} diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/ShadowmapSkin.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/ShadowmapSkin.azsl new file mode 100644 index 0000000000..6f0d8e1a31 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/ShadowmapSkin.azsl @@ -0,0 +1,12 @@ +/* + * 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 + +// Use the shadowmap shader with the skin object srg diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/ShadowmapSkin.shader b/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/ShadowmapSkin.shader new file mode 100644 index 0000000000..14c1352c08 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Shadow/ShadowmapSkin.shader @@ -0,0 +1,26 @@ +{ + "Source" : "ShadowmapSkin", + + "DepthStencilState" : { + "Depth" : { "Enable" : true, "CompareFunc" : "LessEqual" } + }, + + "DrawList" : "shadow", + + "RasterState" : + { + "depthBias" : "10", + "depthBiasSlopeScale" : "4" + }, + + "ProgramSettings": + { + "EntryPoints": + [ + { + "name": "MainVS", + "type": "Vertex" + } + ] + } +} diff --git a/Gems/Atom/RHI/3rdParty/Findrenderdoc.cmake b/Gems/Atom/RHI/3rdParty/Findrenderdoc.cmake index f8f17392b3..b95a246afe 100644 --- a/Gems/Atom/RHI/3rdParty/Findrenderdoc.cmake +++ b/Gems/Atom/RHI/3rdParty/Findrenderdoc.cmake @@ -10,6 +10,5 @@ ly_add_external_target( NAME renderdoc 3RDPARTY_ROOT_DIRECTORY "${LY_RENDERDOC_PATH}" VERSION - INCLUDE_DIRECTORIES . COMPILE_DEFINITIONS USE_RENDERDOC ) diff --git a/Gems/Atom/RHI/3rdParty/Platform/Linux/renderdoc_linux.cmake b/Gems/Atom/RHI/3rdParty/Platform/Linux/renderdoc_linux.cmake index a74d250901..5e88fcec2f 100644 --- a/Gems/Atom/RHI/3rdParty/Platform/Linux/renderdoc_linux.cmake +++ b/Gems/Atom/RHI/3rdParty/Platform/Linux/renderdoc_linux.cmake @@ -6,4 +6,5 @@ # # -set(RENDERDOC_RUNTIME_DEPENDENCIES "${BASE_PATH}/librenderdoc.so") +set(RENDERDOC_RUNTIME_DEPENDENCIES "${BASE_PATH}/lib/librenderdoc.so") +set(RENDERDOC_INCLUDE_DIRECTORIES "include") diff --git a/Gems/Atom/RHI/3rdParty/Platform/Windows/renderdoc_windows.cmake b/Gems/Atom/RHI/3rdParty/Platform/Windows/renderdoc_windows.cmake index 559863ca07..70c8564a82 100644 --- a/Gems/Atom/RHI/3rdParty/Platform/Windows/renderdoc_windows.cmake +++ b/Gems/Atom/RHI/3rdParty/Platform/Windows/renderdoc_windows.cmake @@ -7,3 +7,4 @@ # set(RENDERDOC_RUNTIME_DEPENDENCIES "${BASE_PATH}/renderdoc.dll") +set(RENDERDOC_INCLUDE_DIRECTORIES ".") 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/LmbrCentral/Code/Source/Shape/BoxShape.cpp b/Gems/LmbrCentral/Code/Source/Shape/BoxShape.cpp index a776a01ccc..4f6089ba16 100644 --- a/Gems/LmbrCentral/Code/Source/Shape/BoxShape.cpp +++ b/Gems/LmbrCentral/Code/Source/Shape/BoxShape.cpp @@ -166,7 +166,7 @@ namespace LmbrCentral return intersection; } - const bool intersection = AZ::Intersect::IntersectRayObb(src, dir, m_intersectionDataCache.m_obb, distance) > 0; + const bool intersection = AZ::Intersect::IntersectRayObb(src, dir, m_intersectionDataCache.m_obb, distance); return intersection; } diff --git a/Gems/LmbrCentral/Code/Source/Shape/DiskShape.cpp b/Gems/LmbrCentral/Code/Source/Shape/DiskShape.cpp index 8eaf03457f..1c4a94ced3 100644 --- a/Gems/LmbrCentral/Code/Source/Shape/DiskShape.cpp +++ b/Gems/LmbrCentral/Code/Source/Shape/DiskShape.cpp @@ -153,7 +153,7 @@ namespace LmbrCentral m_intersectionDataCache.UpdateIntersectionParams(m_currentTransform, m_diskShapeConfig); return AZ::Intersect::IntersectRayDisk( - src, dir, m_intersectionDataCache.m_position, m_intersectionDataCache.m_radius, m_intersectionDataCache.m_normal, distance) > 0; + src, dir, m_intersectionDataCache.m_position, m_intersectionDataCache.m_radius, m_intersectionDataCache.m_normal, distance); } void DiskShape::DiskIntersectionDataCache::UpdateIntersectionParamsImpl( 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; - - 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; + 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); - 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); + SetCenterOfMassOffset(computeCenterOfMass ? DefaultCenterOfMass : centerOfMassOffsetOverride); + SetMass(computeMass ? DefaultMass : massOverride); + SetInertia(computeInertiaTensor ? DefaultInertiaTensor : inertiaTensorOverride); + return; } - const physx::PxVec3* massLocalPose = optionalComOverride.has_value() ? &optionalComOverride.value() : nullptr; + auto cannotComputeMassProperties = [this, includeAllShapesInMassCalculation] + { + 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); + + 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; + } - bool includeAllShapesInMassCalculation = - AzPhysics::MassComputeFlags::INCLUDE_ALL_SHAPES == (flags & AzPhysics::MassComputeFlags::INCLUDE_ALL_SHAPES); + // 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 + { + SetCenterOfMassOffset(centerOfMassOffsetOverride); + } + const physx::PxVec3 pxCenterOfMass = PxMathConvert(GetCenterOfMassLocal()); - // Handle the case when we don't compute mass - if (!computeMass) + 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.emplace_back(shape->GetMaterial()->GetDensity()); + } + + // Compute Mass + Inertia { PHYSX_SCENE_WRITE_LOCK(m_pxRigidActor->getScene()); - physx::PxRigidBodyExt::setMassAndUpdateInertia(*m_pxRigidActor, *massOverride, massLocalPose, - includeAllShapesInMassCalculation); + physx::PxRigidBodyExt::updateMassAndInertia(*m_pxRigidActor, + densities.data(), static_cast(densities.size()), + &pxCenterOfMass, includeAllShapesInMassCalculation); } + // 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); + SetInertia(inertiaTensorOverride); } - - return; - } - - // Handle the cases when mass should be computed from density - if (shapesCount == 1) - { - AZStd::shared_ptr shape = GetShape(0); - float density = shape->GetMaterial()->GetDensity(); - - PHYSX_SCENE_WRITE_LOCK(m_pxRigidActor->getScene()); - physx::PxRigidBodyExt::updateMassAndInertia(*m_pxRigidActor, density, massLocalPose, - includeAllShapesInMassCalculation); } else { - AZStd::vector densities(shapesCount); - for (AZ::u32 i = 0; i < shapesCount; ++i) + if (computeInertiaTensor) { - densities[i] = GetShape(i)->GetMaterial()->GetDensity(); + // Set Mass + Compute Inertia + PHYSX_SCENE_WRITE_LOCK(m_pxRigidActor->getScene()); + physx::PxRigidBodyExt::setMassAndUpdateInertia(*m_pxRigidActor, massOverride, + &pxCenterOfMass, includeAllShapesInMassCalculation); + } + else + { + SetMass(massOverride); + SetInertia(inertiaTensorOverride); } - - PHYSX_SCENE_WRITE_LOCK(m_pxRigidActor->getScene()); - physx::PxRigidBodyExt::updateMassAndInertia(*m_pxRigidActor, densities.data(), - shapesCount, massLocalPose, 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) - { - SetInertia(*inertiaTensorOverride); } } @@ -344,70 +383,57 @@ namespace PhysX } } - void RigidBody::UpdateComputedCenterOfMass() + void RigidBody::UpdateCenterOfMass(bool includeAllShapesInMassCalculation) { - if (m_pxRigidActor) + if (m_shapes.empty()) { - physx::PxU32 shapeCount = 0; - { - PHYSX_SCENE_READ_LOCK(m_pxRigidActor->getScene()); - shapeCount = m_pxRigidActor->getNbShapes(); - } - if (shapeCount > 0) - { - AZStd::vector shapes; - shapes.resize(shapeCount); - - { - PHYSX_SCENE_READ_LOCK(m_pxRigidActor->getScene()); - m_pxRigidActor->getShapes(&shapes[0], shapeCount); - } - - 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()); + SetCenterOfMassOffset(DefaultCenterOfMass); + return; + } - if (shapeCount == 0) - { - SetZeroCenterOfMass(); - 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) + { + const physx::PxShape& pxShape = *shape->GetPxShape(); + const bool includeShape = includeAllShapesInMassCalculation || IsSimulationShape(pxShape); - const auto properties = physx::PxRigidBodyExt::computeMassPropertiesFromShapes(&shapes[0], shapeCount); - const physx::PxTransform computedCenterOfMass(properties.centerOfMass); + if (includeShape && CanShapeComputeMassProperties(pxShape)) { - PHYSX_SCENE_WRITE_LOCK(m_pxRigidActor->getScene()); - m_pxRigidActor->setCMassLocalPose(computedCenterOfMass); + pxShapes.emplace_back(&pxShape); } } - else - { - SetZeroCenterOfMass(); - } } - } - void RigidBody::SetInertia(const AZ::Matrix3x3& inertia) - { - if (m_pxRigidActor) + if (pxShapes.empty()) { - PHYSX_SCENE_WRITE_LOCK(m_pxRigidActor->getScene()); - m_pxRigidActor->setMassSpaceInertiaTensor(PxMathConvert(inertia.RetrieveScale())); + 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::ComputeInertia() + void RigidBody::SetInertia(const AZ::Matrix3x3& inertia) { 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); + m_pxRigidActor->setMassSpaceInertiaTensor(PxMathConvert(inertia.RetrieveScale())); } } @@ -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/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/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp index d7300cdc48..8c85e21490 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp +++ b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp @@ -567,13 +567,15 @@ namespace Terrain ShaderMacroMaterialData& shaderData = macroMaterialData.at(i); const AZ::Aabb& materialBounds = materialData.m_bounds; + // Use reverse coordinates (1 - y) for the y direction so that the lower left corner of the macro material images + // map to the lower left corner in world space. This will match up with the height uv coordinate mapping. shaderData.m_uvMin = { (xPatch - materialBounds.GetMin().GetX()) / materialBounds.GetXExtent(), - (yPatch - materialBounds.GetMin().GetY()) / materialBounds.GetYExtent() + 1.0f - ((yPatch - materialBounds.GetMin().GetY()) / materialBounds.GetYExtent()) }; shaderData.m_uvMax = { ((xPatch + GridMeters) - materialBounds.GetMin().GetX()) / materialBounds.GetXExtent(), - ((yPatch + GridMeters) - materialBounds.GetMin().GetY()) / materialBounds.GetYExtent() + 1.0f - (((yPatch + GridMeters) - materialBounds.GetMin().GetY()) / materialBounds.GetYExtent()) }; shaderData.m_normalFactor = materialData.m_normalFactor; shaderData.m_flipNormalX = materialData.m_normalFlipX; 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; diff --git a/Registry/bootstrap.setreg b/Registry/bootstrap.setreg index 3a15d59450..b2bcdd7f3f 100644 --- a/Registry/bootstrap.setreg +++ b/Registry/bootstrap.setreg @@ -17,7 +17,7 @@ "remote_port": 45643, "connect_to_remote": 0, "windows_connect_to_remote": 1, - "linux_connect_to_remote": 0, + "linux_connect_to_remote": 1, "provo_connect_to_remote": 1, "salem_connect_to_remote": 0, "jasper_connect_to_remote": 0, @@ -29,7 +29,7 @@ "salem_wait_for_connect": 0, "jasper_wait_for_connect": 0, "windows_wait_for_connect": 1, - "linux_wait_for_connect": 0, + "linux_wait_for_connect": 1, "android_wait_for_connect": 0, "ios_wait_for_connect": 0, "mac_wait_for_connect": 0, diff --git a/Templates/CustomTool/Template/CMakeLists.txt b/Templates/CustomTool/Template/CMakeLists.txt new file mode 100644 index 0000000000..b19ea2edce --- /dev/null +++ b/Templates/CustomTool/Template/CMakeLists.txt @@ -0,0 +1,22 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(o3de_gem_path ${CMAKE_CURRENT_LIST_DIR}) +set(o3de_gem_json ${o3de_gem_path}/gem.json) +o3de_read_json_key(o3de_gem_name ${o3de_gem_json} "gem_name") +o3de_restricted_path(${o3de_gem_json} o3de_gem_restricted_path) + +ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME} "${o3de_gem_restricted_path}" ${o3de_gem_path} ${o3de_gem_name}) + +# Now that we have the platform abstraction layer (PAL) folder for this folder, thats where we will find the +# project cmake for this platform. +include(${pal_dir}/${PAL_PLATFORM_NAME_LOWERCASE}_gem.cmake) + +ly_add_external_target_path(${CMAKE_CURRENT_LIST_DIR}/3rdParty) + +add_subdirectory(Code) diff --git a/Templates/CustomTool/Template/Code/${NameLower}_editor_files.cmake b/Templates/CustomTool/Template/Code/${NameLower}_editor_files.cmake new file mode 100644 index 0000000000..d73efffa2e --- /dev/null +++ b/Templates/CustomTool/Template/Code/${NameLower}_editor_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(FILES + Source/${Name}EditorSystemComponent.cpp + Source/${Name}EditorSystemComponent.h + Source/${Name}Widget.cpp + Source/${Name}Widget.h + Source/${Name}.qrc +) diff --git a/Templates/CustomTool/Template/Code/${NameLower}_editor_shared_files.cmake b/Templates/CustomTool/Template/Code/${NameLower}_editor_shared_files.cmake new file mode 100644 index 0000000000..2d4ceae97d --- /dev/null +++ b/Templates/CustomTool/Template/Code/${NameLower}_editor_shared_files.cmake @@ -0,0 +1,11 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(FILES + Source/${Name}EditorModule.cpp +) diff --git a/Templates/CustomTool/Template/Code/${NameLower}_editor_tests_files.cmake b/Templates/CustomTool/Template/Code/${NameLower}_editor_tests_files.cmake new file mode 100644 index 0000000000..ff45c2fc1c --- /dev/null +++ b/Templates/CustomTool/Template/Code/${NameLower}_editor_tests_files.cmake @@ -0,0 +1,11 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(FILES + Tests/${Name}EditorTest.cpp +) diff --git a/Templates/CustomTool/Template/Code/${NameLower}_files.cmake b/Templates/CustomTool/Template/Code/${NameLower}_files.cmake new file mode 100644 index 0000000000..b7d6d37bdf --- /dev/null +++ b/Templates/CustomTool/Template/Code/${NameLower}_files.cmake @@ -0,0 +1,14 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(FILES + Include/${Name}/${Name}Bus.h + Source/${Name}ModuleInterface.h + Source/${Name}SystemComponent.cpp + Source/${Name}SystemComponent.h +) diff --git a/Templates/CustomTool/Template/Code/${NameLower}_shared_files.cmake b/Templates/CustomTool/Template/Code/${NameLower}_shared_files.cmake new file mode 100644 index 0000000000..b85916191c --- /dev/null +++ b/Templates/CustomTool/Template/Code/${NameLower}_shared_files.cmake @@ -0,0 +1,11 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(FILES + Source/${Name}Module.cpp +) diff --git a/Templates/CustomTool/Template/Code/${NameLower}_tests_files.cmake b/Templates/CustomTool/Template/Code/${NameLower}_tests_files.cmake new file mode 100644 index 0000000000..adcfe2645f --- /dev/null +++ b/Templates/CustomTool/Template/Code/${NameLower}_tests_files.cmake @@ -0,0 +1,11 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(FILES + Tests/${Name}Test.cpp +) diff --git a/Templates/CustomTool/Template/Code/CMakeLists.txt b/Templates/CustomTool/Template/Code/CMakeLists.txt new file mode 100644 index 0000000000..f5cda477fc --- /dev/null +++ b/Templates/CustomTool/Template/Code/CMakeLists.txt @@ -0,0 +1,168 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +# Currently we are in the Code folder: ${CMAKE_CURRENT_LIST_DIR} +# Get the platform specific folder ${pal_dir} for the current folder: ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME} +# Note: ly_get_list_relative_pal_filename will take care of the details for us, as this may be a restricted platform +# in which case it will see if that platform is present here or in the restricted folder. +# i.e. It could here in our gem : Gems/${Name}/Code/Platform/ or +# //Gems/${Name}/Code +ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME} ${o3de_gem_restricted_path} ${o3de_gem_path} ${o3de_gem_name}) + +# Now that we have the platform abstraction layer (PAL) folder for this folder, thats where we will find the +# traits for this platform. Traits for a platform are defines for things like whether or not something in this gem +# is supported by this platform. +include(${pal_dir}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) + +# Add the ${Name}.Static target +# Note: We include the common files and the platform specific files which are set in ${NameLower}_common_files.cmake +# and in ${pal_dir}/${NameLower}_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake +ly_add_target( + NAME ${Name}.Static STATIC + NAMESPACE Gem + FILES_CMAKE + ${NameLower}_files.cmake + ${pal_dir}/${NameLower}_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake + INCLUDE_DIRECTORIES + PUBLIC + Include + PRIVATE + Source + BUILD_DEPENDENCIES + PUBLIC + AZ::AzCore + AZ::AzFramework +) + +# Here add ${Name} target, it depends on the ${Name}.Static +ly_add_target( + NAME ${Name} ${PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE} + NAMESPACE Gem + FILES_CMAKE + ${NameLower}_shared_files.cmake + ${pal_dir}/${NameLower}_shared_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake + INCLUDE_DIRECTORIES + PUBLIC + Include + PRIVATE + Source + BUILD_DEPENDENCIES + PRIVATE + Gem::${Name}.Static +) + +# By default, we will specify that the above target ${Name} would be used by +# Client and Server type targets when this gem is enabled. If you don't want it +# active in Clients or Servers by default, delete one of both of the following lines: +ly_create_alias(NAME ${Name}.Clients NAMESPACE Gem TARGETS Gem::${Name}) +ly_create_alias(NAME ${Name}.Servers NAMESPACE Gem TARGETS Gem::${Name}) + +# If we are on a host platform, we want to add the host tools targets like the ${Name}.Editor target which +# will also depend on ${Name}.Static +if(PAL_TRAIT_BUILD_HOST_TOOLS) + ly_add_target( + NAME ${Name}.Editor.Static STATIC + NAMESPACE Gem + AUTOMOC + AUTORCC + FILES_CMAKE + ${NameLower}_editor_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Source + PUBLIC + Include + BUILD_DEPENDENCIES + PUBLIC + AZ::AzToolsFramework + Gem::${Name}.Static + ) + + ly_add_target( + NAME ${Name}.Editor GEM_MODULE + NAMESPACE Gem + AUTOMOC + FILES_CMAKE + ${NameLower}_editor_shared_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Source + PUBLIC + Include + BUILD_DEPENDENCIES + PUBLIC + Gem::${Name}.Editor.Static + ) + + # By default, we will specify that the above target ${Name} would be used by + # Tool and Builder type targets when this gem is enabled. If you don't want it + # active in Tools or Builders by default, delete one of both of the following lines: + ly_create_alias(NAME ${Name}.Tools NAMESPACE Gem TARGETS Gem::${Name}.Editor) + ly_create_alias(NAME ${Name}.Builders NAMESPACE Gem TARGETS Gem::${Name}.Editor) + + +endif() + +################################################################################ +# Tests +################################################################################ +# See if globally, tests are supported +if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) + # We globally support tests, see if we support tests on this platform for ${Name}.Static + if(PAL_TRAIT_${NameUpper}_TEST_SUPPORTED) + # We support ${Name}.Tests on this platform, add ${Name}.Tests target which depends on ${Name}.Static + ly_add_target( + NAME ${Name}.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} + NAMESPACE Gem + FILES_CMAKE + ${NameLower}_files.cmake + ${NameLower}_tests_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Tests + Source + BUILD_DEPENDENCIES + PRIVATE + AZ::AzTest + AZ::AzFramework + Gem::${Name}.Static + ) + + # Add ${Name}.Tests to googletest + ly_add_googletest( + NAME Gem::${Name}.Tests + ) + endif() + + # If we are a host platform we want to add tools test like editor tests here + if(PAL_TRAIT_BUILD_HOST_TOOLS) + # We are a host platform, see if Editor tests are supported on this platform + if(PAL_TRAIT_${NameUpper}_EDITOR_TEST_SUPPORTED) + # We support ${Name}.Editor.Tests on this platform, add ${Name}.Editor.Tests target which depends on ${Name}.Editor + ly_add_target( + NAME ${Name}.Editor.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} + NAMESPACE Gem + FILES_CMAKE + ${NameLower}_editor_tests_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Tests + Source + BUILD_DEPENDENCIES + PRIVATE + AZ::AzTest + Gem::${Name}.Editor + ) + + # Add ${Name}.Editor.Tests to googletest + ly_add_googletest( + NAME Gem::${Name}.Editor.Tests + ) + endif() + endif() +endif() diff --git a/Templates/CustomTool/Template/Code/Include/${Name}/${Name}Bus.h b/Templates/CustomTool/Template/Code/Include/${Name}/${Name}Bus.h new file mode 100644 index 0000000000..d09bb2b009 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Include/${Name}/${Name}Bus.h @@ -0,0 +1,40 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ +// {END_LICENSE} + +#pragma once + +#include +#include + +namespace ${SanitizedCppName} +{ + class ${SanitizedCppName}Requests + { + public: + AZ_RTTI(${SanitizedCppName}Requests, "{${Random_Uuid}}"); + virtual ~${SanitizedCppName}Requests() = default; + // Put your public methods here + }; + + class ${SanitizedCppName}BusTraits + : public AZ::EBusTraits + { + public: + ////////////////////////////////////////////////////////////////////////// + // EBusTraits overrides + static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; + static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + ////////////////////////////////////////////////////////////////////////// + }; + + using ${SanitizedCppName}RequestBus = AZ::EBus<${SanitizedCppName}Requests, ${SanitizedCppName}BusTraits>; + using ${SanitizedCppName}Interface = AZ::Interface<${SanitizedCppName}Requests>; + +} // namespace ${SanitizedCppName} diff --git a/Templates/CustomTool/Template/Code/Platform/Android/${NameLower}_android_files.cmake b/Templates/CustomTool/Template/Code/Platform/Android/${NameLower}_android_files.cmake new file mode 100644 index 0000000000..5b6da14a20 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Platform/Android/${NameLower}_android_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +# Platform specific files for Android +# i.e. ../Source/Android/${Name}Android.cpp +# ../Source/Android/${Name}Android.h +# ../Include/Android/${Name}Android.h + +set(FILES +) diff --git a/Templates/CustomTool/Template/Code/Platform/Android/${NameLower}_shared_android_files.cmake b/Templates/CustomTool/Template/Code/Platform/Android/${NameLower}_shared_android_files.cmake new file mode 100644 index 0000000000..5b6da14a20 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Platform/Android/${NameLower}_shared_android_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +# Platform specific files for Android +# i.e. ../Source/Android/${Name}Android.cpp +# ../Source/Android/${Name}Android.h +# ../Include/Android/${Name}Android.h + +set(FILES +) diff --git a/Templates/CustomTool/Template/Code/Platform/Android/PAL_android.cmake b/Templates/CustomTool/Template/Code/Platform/Android/PAL_android.cmake new file mode 100644 index 0000000000..90d1caccf4 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Platform/Android/PAL_android.cmake @@ -0,0 +1,11 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(PAL_TRAIT_${NameUpper}_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_TEST_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_EDITOR_TEST_SUPPORTED FALSE) diff --git a/Templates/CustomTool/Template/Code/Platform/Linux/${NameLower}_linux_files.cmake b/Templates/CustomTool/Template/Code/Platform/Linux/${NameLower}_linux_files.cmake new file mode 100644 index 0000000000..2f58a2e6f5 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Platform/Linux/${NameLower}_linux_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +# Platform specific files for Linux +# i.e. ../Source/Linux/${Name}Linux.cpp +# ../Source/Linux/${Name}Linux.h +# ../Include/Linux/${Name}Linux.h + +set(FILES +) diff --git a/Templates/CustomTool/Template/Code/Platform/Linux/${NameLower}_shared_linux_files.cmake b/Templates/CustomTool/Template/Code/Platform/Linux/${NameLower}_shared_linux_files.cmake new file mode 100644 index 0000000000..2f58a2e6f5 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Platform/Linux/${NameLower}_shared_linux_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +# Platform specific files for Linux +# i.e. ../Source/Linux/${Name}Linux.cpp +# ../Source/Linux/${Name}Linux.h +# ../Include/Linux/${Name}Linux.h + +set(FILES +) diff --git a/Templates/CustomTool/Template/Code/Platform/Linux/PAL_linux.cmake b/Templates/CustomTool/Template/Code/Platform/Linux/PAL_linux.cmake new file mode 100644 index 0000000000..0abcd887e8 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Platform/Linux/PAL_linux.cmake @@ -0,0 +1,11 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(PAL_TRAIT_${NameUpper}_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_TEST_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_EDITOR_TEST_SUPPORTED TRUE) \ No newline at end of file diff --git a/Templates/CustomTool/Template/Code/Platform/Mac/${NameLower}_mac_files.cmake b/Templates/CustomTool/Template/Code/Platform/Mac/${NameLower}_mac_files.cmake new file mode 100644 index 0000000000..1cf737a2f1 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Platform/Mac/${NameLower}_mac_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +# Platform specific files for Mac +# i.e. ../Source/Mac/${Name}Mac.cpp +# ../Source/Mac/${Name}Mac.h +# ../Include/Mac/${Name}Mac.h + +set(FILES +) diff --git a/Templates/CustomTool/Template/Code/Platform/Mac/${NameLower}_shared_mac_files.cmake b/Templates/CustomTool/Template/Code/Platform/Mac/${NameLower}_shared_mac_files.cmake new file mode 100644 index 0000000000..1cf737a2f1 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Platform/Mac/${NameLower}_shared_mac_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +# Platform specific files for Mac +# i.e. ../Source/Mac/${Name}Mac.cpp +# ../Source/Mac/${Name}Mac.h +# ../Include/Mac/${Name}Mac.h + +set(FILES +) diff --git a/Templates/CustomTool/Template/Code/Platform/Mac/PAL_mac.cmake b/Templates/CustomTool/Template/Code/Platform/Mac/PAL_mac.cmake new file mode 100644 index 0000000000..0abcd887e8 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Platform/Mac/PAL_mac.cmake @@ -0,0 +1,11 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(PAL_TRAIT_${NameUpper}_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_TEST_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_EDITOR_TEST_SUPPORTED TRUE) \ No newline at end of file diff --git a/Templates/CustomTool/Template/Code/Platform/Windows/${NameLower}_shared_windows_files.cmake b/Templates/CustomTool/Template/Code/Platform/Windows/${NameLower}_shared_windows_files.cmake new file mode 100644 index 0000000000..712aad1207 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Platform/Windows/${NameLower}_shared_windows_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +# Platform specific files for Windows +# i.e. ../Source/Windows/${Name}Windows.cpp +# ../Source/Windows/${Name}Windows.h +# ../Include/Windows/${Name}Windows.h + +set(FILES +) diff --git a/Templates/CustomTool/Template/Code/Platform/Windows/${NameLower}_windows_files.cmake b/Templates/CustomTool/Template/Code/Platform/Windows/${NameLower}_windows_files.cmake new file mode 100644 index 0000000000..712aad1207 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Platform/Windows/${NameLower}_windows_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +# Platform specific files for Windows +# i.e. ../Source/Windows/${Name}Windows.cpp +# ../Source/Windows/${Name}Windows.h +# ../Include/Windows/${Name}Windows.h + +set(FILES +) diff --git a/Templates/CustomTool/Template/Code/Platform/Windows/PAL_windows.cmake b/Templates/CustomTool/Template/Code/Platform/Windows/PAL_windows.cmake new file mode 100644 index 0000000000..0abcd887e8 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Platform/Windows/PAL_windows.cmake @@ -0,0 +1,11 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(PAL_TRAIT_${NameUpper}_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_TEST_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_EDITOR_TEST_SUPPORTED TRUE) \ No newline at end of file diff --git a/Templates/CustomTool/Template/Code/Platform/iOS/${NameLower}_ios_files.cmake b/Templates/CustomTool/Template/Code/Platform/iOS/${NameLower}_ios_files.cmake new file mode 100644 index 0000000000..61efde11c2 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Platform/iOS/${NameLower}_ios_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +# Platform specific files for iOS +# i.e. ../Source/iOS/${Name}iOS.cpp +# ../Source/iOS/${Name}iOS.h +# ../Include/iOS/${Name}iOS.h + +set(FILES +) diff --git a/Templates/CustomTool/Template/Code/Platform/iOS/${NameLower}_shared_ios_files.cmake b/Templates/CustomTool/Template/Code/Platform/iOS/${NameLower}_shared_ios_files.cmake new file mode 100644 index 0000000000..61efde11c2 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Platform/iOS/${NameLower}_shared_ios_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +# Platform specific files for iOS +# i.e. ../Source/iOS/${Name}iOS.cpp +# ../Source/iOS/${Name}iOS.h +# ../Include/iOS/${Name}iOS.h + +set(FILES +) diff --git a/Templates/CustomTool/Template/Code/Platform/iOS/PAL_ios.cmake b/Templates/CustomTool/Template/Code/Platform/iOS/PAL_ios.cmake new file mode 100644 index 0000000000..332f4469b6 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Platform/iOS/PAL_ios.cmake @@ -0,0 +1,11 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(PAL_TRAIT_${NameUpper}_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_TEST_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_EDITOR_TEST_SUPPORTED FALSE) \ No newline at end of file diff --git a/Templates/CustomTool/Template/Code/Source/${Name}.qrc b/Templates/CustomTool/Template/Code/Source/${Name}.qrc new file mode 100644 index 0000000000..90d7695b88 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Source/${Name}.qrc @@ -0,0 +1,5 @@ + + + toolbar_icon.svg + + diff --git a/Templates/CustomTool/Template/Code/Source/${Name}EditorModule.cpp b/Templates/CustomTool/Template/Code/Source/${Name}EditorModule.cpp new file mode 100644 index 0000000000..0027af011a --- /dev/null +++ b/Templates/CustomTool/Template/Code/Source/${Name}EditorModule.cpp @@ -0,0 +1,55 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ +// {END_LICENSE} + +#include <${Name}ModuleInterface.h> +#include <${Name}EditorSystemComponent.h> + +void Init${SanitizedCppName}Resources() +{ + // We must register our Qt resources (.qrc file) since this is being loaded from a separate module (gem) + Q_INIT_RESOURCE(${SanitizedCppName}); +} + +namespace ${SanitizedCppName} +{ + class ${SanitizedCppName}EditorModule + : public ${SanitizedCppName}ModuleInterface + { + public: + AZ_RTTI(${SanitizedCppName}EditorModule, "${ModuleClassId}", ${SanitizedCppName}ModuleInterface); + AZ_CLASS_ALLOCATOR(${SanitizedCppName}EditorModule, AZ::SystemAllocator, 0); + + ${SanitizedCppName}EditorModule() + { + Init${SanitizedCppName}Resources(); + + // Push results of [MyComponent]::CreateDescriptor() into m_descriptors here. + // Add ALL components descriptors associated with this gem to m_descriptors. + // This will associate the AzTypeInfo information for the components with the the SerializeContext, BehaviorContext and EditContext. + // This happens through the [MyComponent]::Reflect() function. + m_descriptors.insert(m_descriptors.end(), { + ${SanitizedCppName}EditorSystemComponent::CreateDescriptor(), + }); + } + + /** + * Add required SystemComponents to the SystemEntity. + * Non-SystemComponents should not be added here + */ + AZ::ComponentTypeList GetRequiredSystemComponents() const override + { + return AZ::ComponentTypeList { + azrtti_typeid<${SanitizedCppName}EditorSystemComponent>(), + }; + } + }; +}// namespace ${SanitizedCppName} + +AZ_DECLARE_MODULE_CLASS(Gem_${SanitizedCppName}, ${SanitizedCppName}::${SanitizedCppName}EditorModule) diff --git a/Templates/CustomTool/Template/Code/Source/${Name}EditorSystemComponent.cpp b/Templates/CustomTool/Template/Code/Source/${Name}EditorSystemComponent.cpp new file mode 100644 index 0000000000..f12fa04929 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Source/${Name}EditorSystemComponent.cpp @@ -0,0 +1,78 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ +// {END_LICENSE} + +#include + +#include + +#include <${Name}Widget.h> +#include <${Name}EditorSystemComponent.h> + +namespace ${SanitizedCppName} +{ + void ${SanitizedCppName}EditorSystemComponent::Reflect(AZ::ReflectContext* context) + { + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class<${SanitizedCppName}EditorSystemComponent, ${SanitizedCppName}SystemComponent>() + ->Version(0); + } + } + + ${SanitizedCppName}EditorSystemComponent::${SanitizedCppName}EditorSystemComponent() = default; + + ${SanitizedCppName}EditorSystemComponent::~${SanitizedCppName}EditorSystemComponent() = default; + + void ${SanitizedCppName}EditorSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + BaseSystemComponent::GetProvidedServices(provided); + provided.push_back(AZ_CRC_CE("${SanitizedCppName}EditorService")); + } + + void ${SanitizedCppName}EditorSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + { + BaseSystemComponent::GetIncompatibleServices(incompatible); + incompatible.push_back(AZ_CRC_CE("${SanitizedCppName}EditorService")); + } + + void ${SanitizedCppName}EditorSystemComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required) + { + BaseSystemComponent::GetRequiredServices(required); + } + + void ${SanitizedCppName}EditorSystemComponent::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent) + { + BaseSystemComponent::GetDependentServices(dependent); + } + + void ${SanitizedCppName}EditorSystemComponent::Activate() + { + ${SanitizedCppName}SystemComponent::Activate(); + AzToolsFramework::EditorEvents::Bus::Handler::BusConnect(); + } + + void ${SanitizedCppName}EditorSystemComponent::Deactivate() + { + AzToolsFramework::EditorEvents::Bus::Handler::BusDisconnect(); + ${SanitizedCppName}SystemComponent::Deactivate(); + } + + void ${SanitizedCppName}EditorSystemComponent::NotifyRegisterViews() + { + AzToolsFramework::ViewPaneOptions options; + options.paneRect = QRect(100, 100, 500, 400); + options.showOnToolsToolbar = true; + options.toolbarIcon = ":/${Name}/toolbar_icon.svg"; + + // Register our custom widget as a dockable tool with the Editor + AzToolsFramework::RegisterViewPane<${SanitizedCppName}Widget>("${Name}", "Tools", options); + } + +} // namespace ${SanitizedCppName} diff --git a/Templates/CustomTool/Template/Code/Source/${Name}EditorSystemComponent.h b/Templates/CustomTool/Template/Code/Source/${Name}EditorSystemComponent.h new file mode 100644 index 0000000000..bbeac97da3 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Source/${Name}EditorSystemComponent.h @@ -0,0 +1,45 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ +// {END_LICENSE} + +#pragma once + +#include <${Name}SystemComponent.h> + +#include + +namespace ${SanitizedCppName} +{ + /// System component for ${SanitizedCppName} editor + class ${SanitizedCppName}EditorSystemComponent + : public ${SanitizedCppName}SystemComponent + , private AzToolsFramework::EditorEvents::Bus::Handler + { + using BaseSystemComponent = ${SanitizedCppName}SystemComponent; + public: + AZ_COMPONENT(${SanitizedCppName}EditorSystemComponent, "${EditorSysCompClassId}", BaseSystemComponent); + static void Reflect(AZ::ReflectContext* context); + + ${SanitizedCppName}EditorSystemComponent(); + ~${SanitizedCppName}EditorSystemComponent(); + + private: + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); + static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent); + + // AZ::Component + void Activate() override; + void Deactivate() override; + + // AzToolsFramework::EditorEventsBus overrides ... + void NotifyRegisterViews() override; + }; +} // namespace ${SanitizedCppName} diff --git a/Templates/CustomTool/Template/Code/Source/${Name}Module.cpp b/Templates/CustomTool/Template/Code/Source/${Name}Module.cpp new file mode 100644 index 0000000000..0a6e8bde3c --- /dev/null +++ b/Templates/CustomTool/Template/Code/Source/${Name}Module.cpp @@ -0,0 +1,25 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ +// {END_LICENSE} + +#include <${Name}ModuleInterface.h> +#include <${Name}SystemComponent.h> + +namespace ${SanitizedCppName} +{ + class ${SanitizedCppName}Module + : public ${SanitizedCppName}ModuleInterface + { + public: + AZ_RTTI(${SanitizedCppName}Module, "${ModuleClassId}", ${SanitizedCppName}ModuleInterface); + AZ_CLASS_ALLOCATOR(${SanitizedCppName}Module, AZ::SystemAllocator, 0); + }; +}// namespace ${SanitizedCppName} + +AZ_DECLARE_MODULE_CLASS(Gem_${SanitizedCppName}, ${SanitizedCppName}::${SanitizedCppName}Module) diff --git a/Templates/CustomTool/Template/Code/Source/${Name}ModuleInterface.h b/Templates/CustomTool/Template/Code/Source/${Name}ModuleInterface.h new file mode 100644 index 0000000000..925632491a --- /dev/null +++ b/Templates/CustomTool/Template/Code/Source/${Name}ModuleInterface.h @@ -0,0 +1,45 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ +// {END_LICENSE} + +#include +#include +#include <${Name}SystemComponent.h> + +namespace ${SanitizedCppName} +{ + class ${SanitizedCppName}ModuleInterface + : public AZ::Module + { + public: + AZ_RTTI(${SanitizedCppName}ModuleInterface, "{${Random_Uuid}}", AZ::Module); + AZ_CLASS_ALLOCATOR(${SanitizedCppName}ModuleInterface, AZ::SystemAllocator, 0); + + ${SanitizedCppName}ModuleInterface() + { + // Push results of [MyComponent]::CreateDescriptor() into m_descriptors here. + // Add ALL components descriptors associated with this gem to m_descriptors. + // This will associate the AzTypeInfo information for the components with the the SerializeContext, BehaviorContext and EditContext. + // This happens through the [MyComponent]::Reflect() function. + m_descriptors.insert(m_descriptors.end(), { + ${SanitizedCppName}SystemComponent::CreateDescriptor(), + }); + } + + /** + * Add required SystemComponents to the SystemEntity. + */ + AZ::ComponentTypeList GetRequiredSystemComponents() const override + { + return AZ::ComponentTypeList{ + azrtti_typeid<${SanitizedCppName}SystemComponent>(), + }; + } + }; +}// namespace ${SanitizedCppName} diff --git a/Templates/CustomTool/Template/Code/Source/${Name}SystemComponent.cpp b/Templates/CustomTool/Template/Code/Source/${Name}SystemComponent.cpp new file mode 100644 index 0000000000..cb4d58418e --- /dev/null +++ b/Templates/CustomTool/Template/Code/Source/${Name}SystemComponent.cpp @@ -0,0 +1,92 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ +// {END_LICENSE} + +#include <${Name}SystemComponent.h> + +#include +#include +#include + +namespace ${SanitizedCppName} +{ + void ${SanitizedCppName}SystemComponent::Reflect(AZ::ReflectContext* context) + { + if (AZ::SerializeContext* serialize = azrtti_cast(context)) + { + serialize->Class<${SanitizedCppName}SystemComponent, AZ::Component>() + ->Version(0) + ; + + if (AZ::EditContext* ec = serialize->GetEditContext()) + { + ec->Class<${SanitizedCppName}SystemComponent>("${SanitizedCppName}", "[Description of functionality provided by this System Component]") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("System")) + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ; + } + } + } + + void ${SanitizedCppName}SystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(AZ_CRC_CE("${SanitizedCppName}Service")); + } + + void ${SanitizedCppName}SystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + { + incompatible.push_back(AZ_CRC_CE("${SanitizedCppName}Service")); + } + + void ${SanitizedCppName}SystemComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required) + { + } + + void ${SanitizedCppName}SystemComponent::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent) + { + } + + ${SanitizedCppName}SystemComponent::${SanitizedCppName}SystemComponent() + { + if (${SanitizedCppName}Interface::Get() == nullptr) + { + ${SanitizedCppName}Interface::Register(this); + } + } + + ${SanitizedCppName}SystemComponent::~${SanitizedCppName}SystemComponent() + { + if (${SanitizedCppName}Interface::Get() == this) + { + ${SanitizedCppName}Interface::Unregister(this); + } + } + + void ${SanitizedCppName}SystemComponent::Init() + { + } + + void ${SanitizedCppName}SystemComponent::Activate() + { + ${SanitizedCppName}RequestBus::Handler::BusConnect(); + AZ::TickBus::Handler::BusConnect(); + } + + void ${SanitizedCppName}SystemComponent::Deactivate() + { + AZ::TickBus::Handler::BusDisconnect(); + ${SanitizedCppName}RequestBus::Handler::BusDisconnect(); + } + + void ${SanitizedCppName}SystemComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time) + { + } + +} // namespace ${SanitizedCppName} diff --git a/Templates/CustomTool/Template/Code/Source/${Name}SystemComponent.h b/Templates/CustomTool/Template/Code/Source/${Name}SystemComponent.h new file mode 100644 index 0000000000..5495d18e48 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Source/${Name}SystemComponent.h @@ -0,0 +1,56 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ +// {END_LICENSE} + +#pragma once + +#include +#include +#include <${Name}/${Name}Bus.h> + +namespace ${SanitizedCppName} +{ + class ${SanitizedCppName}SystemComponent + : public AZ::Component + , protected ${SanitizedCppName}RequestBus::Handler + , public AZ::TickBus::Handler + { + public: + AZ_COMPONENT(${SanitizedCppName}SystemComponent, "${SysCompClassId}"); + + static void Reflect(AZ::ReflectContext* context); + + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); + static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent); + + ${SanitizedCppName}SystemComponent(); + ~${SanitizedCppName}SystemComponent(); + + protected: + //////////////////////////////////////////////////////////////////////// + // ${SanitizedCppName}RequestBus interface implementation + + //////////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////////// + // AZ::Component interface implementation + void Init() override; + void Activate() override; + void Deactivate() override; + //////////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////////// + // AZTickBus interface implementation + void OnTick(float deltaTime, AZ::ScriptTimePoint time) override; + //////////////////////////////////////////////////////////////////////// + }; + +} // namespace ${SanitizedCppName} diff --git a/Templates/CustomTool/Template/Code/Source/${Name}Widget.cpp b/Templates/CustomTool/Template/Code/Source/${Name}Widget.cpp new file mode 100644 index 0000000000..bd6dd6c86a --- /dev/null +++ b/Templates/CustomTool/Template/Code/Source/${Name}Widget.cpp @@ -0,0 +1,44 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ +// {END_LICENSE} + +#include + +#include +#include + +#include <${Name}Widget.h> + +namespace ${SanitizedCppName} +{ + ${SanitizedCppName}Widget::${SanitizedCppName}Widget(QWidget* parent) + : QWidget(parent) + { + setWindowTitle(QObject::tr("${Name}")); + + QVBoxLayout* mainLayout = new QVBoxLayout(this); + + QLabel* introLabel = new QLabel(QObject::tr("Put your cool stuff here!"), this); + mainLayout->addWidget(introLabel, 0, Qt::AlignCenter); + + QString helpText = QString( + "For help getting started, visit the UI Development documentation
or come ask a question in the sig-ui-ux channel on Discord"); + + QLabel* helpLabel = new QLabel(this); + helpLabel->setTextFormat(Qt::RichText); + helpLabel->setText(helpText); + helpLabel->setOpenExternalLinks(true); + + mainLayout->addWidget(helpLabel, 0, Qt::AlignCenter); + + setLayout(mainLayout); + } +} + +#include diff --git a/Templates/CustomTool/Template/Code/Source/${Name}Widget.h b/Templates/CustomTool/Template/Code/Source/${Name}Widget.h new file mode 100644 index 0000000000..4d0c86d043 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Source/${Name}Widget.h @@ -0,0 +1,28 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ +// {END_LICENSE} + +#pragma once + +#if !defined(Q_MOC_RUN) +#include + +#include +#endif + +namespace ${SanitizedCppName} +{ + class ${SanitizedCppName}Widget + : public QWidget + { + Q_OBJECT + public: + explicit ${SanitizedCppName}Widget(QWidget* parent = nullptr); + }; +} diff --git a/Templates/CustomTool/Template/Code/Source/toolbar_icon.svg b/Templates/CustomTool/Template/Code/Source/toolbar_icon.svg new file mode 100644 index 0000000000..59de66961c --- /dev/null +++ b/Templates/CustomTool/Template/Code/Source/toolbar_icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Templates/CustomTool/Template/Code/Tests/${Name}EditorTest.cpp b/Templates/CustomTool/Template/Code/Tests/${Name}EditorTest.cpp new file mode 100644 index 0000000000..9b84575fa0 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Tests/${Name}EditorTest.cpp @@ -0,0 +1,13 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ +// {END_LICENSE} + +#include + +AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV); diff --git a/Templates/CustomTool/Template/Code/Tests/${Name}Test.cpp b/Templates/CustomTool/Template/Code/Tests/${Name}Test.cpp new file mode 100644 index 0000000000..9b84575fa0 --- /dev/null +++ b/Templates/CustomTool/Template/Code/Tests/${Name}Test.cpp @@ -0,0 +1,13 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ +// {END_LICENSE} + +#include + +AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV); diff --git a/Templates/CustomTool/Template/Platform/Android/android_gem.cmake b/Templates/CustomTool/Template/Platform/Android/android_gem.cmake new file mode 100644 index 0000000000..063b3be9ac --- /dev/null +++ b/Templates/CustomTool/Template/Platform/Android/android_gem.cmake @@ -0,0 +1,8 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + diff --git a/Templates/CustomTool/Template/Platform/Android/android_gem.json b/Templates/CustomTool/Template/Platform/Android/android_gem.json new file mode 100644 index 0000000000..23bbb28e66 --- /dev/null +++ b/Templates/CustomTool/Template/Platform/Android/android_gem.json @@ -0,0 +1,3 @@ +{ + "Tags": ["Android"], +} \ No newline at end of file diff --git a/Templates/CustomTool/Template/Platform/Linux/linux_gem.cmake b/Templates/CustomTool/Template/Platform/Linux/linux_gem.cmake new file mode 100644 index 0000000000..063b3be9ac --- /dev/null +++ b/Templates/CustomTool/Template/Platform/Linux/linux_gem.cmake @@ -0,0 +1,8 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + diff --git a/Templates/CustomTool/Template/Platform/Linux/linux_gem.json b/Templates/CustomTool/Template/Platform/Linux/linux_gem.json new file mode 100644 index 0000000000..d08fbf53ba --- /dev/null +++ b/Templates/CustomTool/Template/Platform/Linux/linux_gem.json @@ -0,0 +1,3 @@ +{ + "Tags": ["Linux"] +} \ No newline at end of file diff --git a/Templates/CustomTool/Template/Platform/Mac/mac_gem.cmake b/Templates/CustomTool/Template/Platform/Mac/mac_gem.cmake new file mode 100644 index 0000000000..063b3be9ac --- /dev/null +++ b/Templates/CustomTool/Template/Platform/Mac/mac_gem.cmake @@ -0,0 +1,8 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + diff --git a/Templates/CustomTool/Template/Platform/Mac/mac_gem.json b/Templates/CustomTool/Template/Platform/Mac/mac_gem.json new file mode 100644 index 0000000000..d42b6f8186 --- /dev/null +++ b/Templates/CustomTool/Template/Platform/Mac/mac_gem.json @@ -0,0 +1,3 @@ +{ + "Tags": ["Mac"] +} \ No newline at end of file diff --git a/Templates/CustomTool/Template/Platform/Windows/windows_gem.cmake b/Templates/CustomTool/Template/Platform/Windows/windows_gem.cmake new file mode 100644 index 0000000000..063b3be9ac --- /dev/null +++ b/Templates/CustomTool/Template/Platform/Windows/windows_gem.cmake @@ -0,0 +1,8 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + diff --git a/Templates/CustomTool/Template/Platform/Windows/windows_gem.json b/Templates/CustomTool/Template/Platform/Windows/windows_gem.json new file mode 100644 index 0000000000..a052f1e05a --- /dev/null +++ b/Templates/CustomTool/Template/Platform/Windows/windows_gem.json @@ -0,0 +1,3 @@ +{ + "Tags": ["Windows"] +} \ No newline at end of file diff --git a/Templates/CustomTool/Template/Platform/iOS/ios_gem.cmake b/Templates/CustomTool/Template/Platform/iOS/ios_gem.cmake new file mode 100644 index 0000000000..063b3be9ac --- /dev/null +++ b/Templates/CustomTool/Template/Platform/iOS/ios_gem.cmake @@ -0,0 +1,8 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + diff --git a/Templates/CustomTool/Template/Platform/iOS/ios_gem.json b/Templates/CustomTool/Template/Platform/iOS/ios_gem.json new file mode 100644 index 0000000000..b2dab56d05 --- /dev/null +++ b/Templates/CustomTool/Template/Platform/iOS/ios_gem.json @@ -0,0 +1,3 @@ +{ + "Tags": ["iOS"] +} \ No newline at end of file diff --git a/Templates/CustomTool/Template/gem.json b/Templates/CustomTool/Template/gem.json new file mode 100644 index 0000000000..518d831e0f --- /dev/null +++ b/Templates/CustomTool/Template/gem.json @@ -0,0 +1,17 @@ +{ + "gem_name": "${Name}", + "display_name": "${Name}", + "license": "What license ${Name} uses goes here: i.e. https://opensource.org/licenses/MIT", + "origin": "The primary repo for ${Name} goes here: i.e. http://www.mydomain.com", + "type": "Code", + "summary": "A short description of ${Name}.", + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "${Name}" + ], + "icon_path": "preview.png", + "requirements": "", + "restricted_name": "gems" +} diff --git a/Templates/CustomTool/Template/preview.png b/Templates/CustomTool/Template/preview.png new file mode 100644 index 0000000000..0f393ac886 --- /dev/null +++ b/Templates/CustomTool/Template/preview.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7ac9dd09bde78f389e3725ac49d61eff109857e004840bc0bc3881739df9618d +size 2217 diff --git a/Templates/CustomTool/template.json b/Templates/CustomTool/template.json new file mode 100644 index 0000000000..e3221db106 --- /dev/null +++ b/Templates/CustomTool/template.json @@ -0,0 +1,382 @@ +{ + "template_name": "CustomTool", + "origin": "The primary repo for CustomTool goes here: i.e. http://www.mydomain.com", + "license": "What license CustomTool uses goes here: i.e. https://opensource.org/licenses/MIT", + "display_name": "CustomTool", + "summary": "A gem template for a custom tool in C++ that gets registered with the Editor.", + "canonical_tags": [], + "user_tags": [ + "CustomTool" + ], + "icon_path": "preview.png", + "copyFiles": [ + { + "file": "CMakeLists.txt", + "origin": "CMakeLists.txt", + "isTemplated": false, + "isOptional": false + }, + { + "file": "Code/${NameLower}_editor_files.cmake", + "origin": "Code/${NameLower}_editor_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/${NameLower}_editor_shared_files.cmake", + "origin": "Code/${NameLower}_editor_shared_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/${NameLower}_editor_tests_files.cmake", + "origin": "Code/${NameLower}_editor_tests_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/${NameLower}_files.cmake", + "origin": "Code/${NameLower}_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/${NameLower}_shared_files.cmake", + "origin": "Code/${NameLower}_shared_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/${NameLower}_tests_files.cmake", + "origin": "Code/${NameLower}_tests_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/CMakeLists.txt", + "origin": "Code/CMakeLists.txt", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Include/${Name}/${Name}Bus.h", + "origin": "Code/Include/${Name}/${Name}Bus.h", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Android/${NameLower}_android_files.cmake", + "origin": "Code/Platform/Android/${NameLower}_android_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Android/${NameLower}_shared_android_files.cmake", + "origin": "Code/Platform/Android/${NameLower}_shared_android_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Android/PAL_android.cmake", + "origin": "Code/Platform/Android/PAL_android.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Linux/${NameLower}_linux_files.cmake", + "origin": "Code/Platform/Linux/${NameLower}_linux_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Linux/${NameLower}_shared_linux_files.cmake", + "origin": "Code/Platform/Linux/${NameLower}_shared_linux_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Linux/PAL_linux.cmake", + "origin": "Code/Platform/Linux/PAL_linux.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Mac/${NameLower}_mac_files.cmake", + "origin": "Code/Platform/Mac/${NameLower}_mac_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Mac/${NameLower}_shared_mac_files.cmake", + "origin": "Code/Platform/Mac/${NameLower}_shared_mac_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Mac/PAL_mac.cmake", + "origin": "Code/Platform/Mac/PAL_mac.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Windows/${NameLower}_shared_windows_files.cmake", + "origin": "Code/Platform/Windows/${NameLower}_shared_windows_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Windows/${NameLower}_windows_files.cmake", + "origin": "Code/Platform/Windows/${NameLower}_windows_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Windows/PAL_windows.cmake", + "origin": "Code/Platform/Windows/PAL_windows.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/iOS/${NameLower}_ios_files.cmake", + "origin": "Code/Platform/iOS/${NameLower}_ios_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/iOS/${NameLower}_shared_ios_files.cmake", + "origin": "Code/Platform/iOS/${NameLower}_shared_ios_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/iOS/PAL_ios.cmake", + "origin": "Code/Platform/iOS/PAL_ios.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/${Name}.qrc", + "origin": "Code/Source/${Name}.qrc", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/${Name}EditorModule.cpp", + "origin": "Code/Source/${Name}EditorModule.cpp", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/${Name}EditorSystemComponent.cpp", + "origin": "Code/Source/${Name}EditorSystemComponent.cpp", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/${Name}EditorSystemComponent.h", + "origin": "Code/Source/${Name}EditorSystemComponent.h", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/${Name}Module.cpp", + "origin": "Code/Source/${Name}Module.cpp", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/${Name}ModuleInterface.h", + "origin": "Code/Source/${Name}ModuleInterface.h", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/${Name}SystemComponent.cpp", + "origin": "Code/Source/${Name}SystemComponent.cpp", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/${Name}SystemComponent.h", + "origin": "Code/Source/${Name}SystemComponent.h", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/${Name}Widget.cpp", + "origin": "Code/Source/${Name}Widget.cpp", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/${Name}Widget.h", + "origin": "Code/Source/${Name}Widget.h", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/toolbar_icon.svg", + "origin": "Code/Source/toolbar_icon.svg", + "isTemplated": false, + "isOptional": false + }, + { + "file": "Code/Tests/${Name}EditorTest.cpp", + "origin": "Code/Tests/${Name}EditorTest.cpp", + "isTemplated": false, + "isOptional": false + }, + { + "file": "Code/Tests/${Name}Test.cpp", + "origin": "Code/Tests/${Name}Test.cpp", + "isTemplated": false, + "isOptional": false + }, + { + "file": "Platform/Android/android_gem.cmake", + "origin": "Platform/Android/android_gem.cmake", + "isTemplated": false, + "isOptional": false + }, + { + "file": "Platform/Android/android_gem.json", + "origin": "Platform/Android/android_gem.json", + "isTemplated": false, + "isOptional": false + }, + { + "file": "Platform/Linux/linux_gem.cmake", + "origin": "Platform/Linux/linux_gem.cmake", + "isTemplated": false, + "isOptional": false + }, + { + "file": "Platform/Linux/linux_gem.json", + "origin": "Platform/Linux/linux_gem.json", + "isTemplated": false, + "isOptional": false + }, + { + "file": "Platform/Mac/mac_gem.cmake", + "origin": "Platform/Mac/mac_gem.cmake", + "isTemplated": false, + "isOptional": false + }, + { + "file": "Platform/Mac/mac_gem.json", + "origin": "Platform/Mac/mac_gem.json", + "isTemplated": false, + "isOptional": false + }, + { + "file": "Platform/Windows/windows_gem.cmake", + "origin": "Platform/Windows/windows_gem.cmake", + "isTemplated": false, + "isOptional": false + }, + { + "file": "Platform/Windows/windows_gem.json", + "origin": "Platform/Windows/windows_gem.json", + "isTemplated": false, + "isOptional": false + }, + { + "file": "Platform/iOS/ios_gem.cmake", + "origin": "Platform/iOS/ios_gem.cmake", + "isTemplated": false, + "isOptional": false + }, + { + "file": "Platform/iOS/ios_gem.json", + "origin": "Platform/iOS/ios_gem.json", + "isTemplated": false, + "isOptional": false + }, + { + "file": "gem.json", + "origin": "gem.json", + "isTemplated": true, + "isOptional": false + }, + { + "file": "preview.png", + "origin": "preview.png", + "isTemplated": false, + "isOptional": false + } + ], + "createDirectories": [ + { + "dir": "Assets", + "origin": "Assets" + }, + { + "dir": "Code", + "origin": "Code" + }, + { + "dir": "Code/Include", + "origin": "Code/Include" + }, + { + "dir": "Code/Include/${Name}", + "origin": "Code/Include/${Name}" + }, + { + "dir": "Code/Platform", + "origin": "Code/Platform" + }, + { + "dir": "Code/Platform/Android", + "origin": "Code/Platform/Android" + }, + { + "dir": "Code/Platform/Linux", + "origin": "Code/Platform/Linux" + }, + { + "dir": "Code/Platform/Mac", + "origin": "Code/Platform/Mac" + }, + { + "dir": "Code/Platform/Windows", + "origin": "Code/Platform/Windows" + }, + { + "dir": "Code/Platform/iOS", + "origin": "Code/Platform/iOS" + }, + { + "dir": "Code/Source", + "origin": "Code/Source" + }, + { + "dir": "Code/Tests", + "origin": "Code/Tests" + }, + { + "dir": "Platform", + "origin": "Platform" + }, + { + "dir": "Platform/Android", + "origin": "Platform/Android" + }, + { + "dir": "Platform/Linux", + "origin": "Platform/Linux" + }, + { + "dir": "Platform/Mac", + "origin": "Platform/Mac" + }, + { + "dir": "Platform/Windows", + "origin": "Platform/Windows" + }, + { + "dir": "Platform/iOS", + "origin": "Platform/iOS" + } + ] +} diff --git a/engine.json b/engine.json index 2b56e255fc..3d522ce175 100644 --- a/engine.json +++ b/engine.json @@ -92,6 +92,7 @@ "templates": [ "Templates/AssetGem", "Templates/DefaultGem", + "Templates/CustomTool", "Templates/DefaultProject", "Templates/MinimalProject" ]