diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/ap_fixtures/bundler_batch_setup_fixture.py b/AutomatedTesting/Gem/PythonTests/assetpipeline/ap_fixtures/bundler_batch_setup_fixture.py index ff362c732c..9281d5947e 100755 --- a/AutomatedTesting/Gem/PythonTests/assetpipeline/ap_fixtures/bundler_batch_setup_fixture.py +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/ap_fixtures/bundler_batch_setup_fixture.py @@ -158,7 +158,7 @@ def bundler_batch_setup_fixture(request, workspace, asset_processor, timeout) -> else: cmd.append(f"--{key}") if append_defaults: - cmd.append(f"--project-path={workspace.project}") + cmd.append(f"--project-path={workspace.paths.project()}") return cmd # ****** diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_bundler_batch_tests.py b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_bundler_batch_tests.py index 7f85e5e317..f5e5642573 100755 --- a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_bundler_batch_tests.py +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_bundler_batch_tests.py @@ -88,214 +88,6 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): bundler_batch_helper.call_bundles(help="") bundler_batch_helper.call_bundleSeed(help="") - @pytest.mark.BAT - @pytest.mark.assetpipeline - @pytest.mark.test_case_id("C16877175") - @pytest.mark.skip("'animations/animationeditorfiles/sample1.animgraph' missing, needs investigation") - def test_WindowsAndMac_CreateAssetList_DependenciesCorrect(self, workspace, bundler_batch_helper): - r""" - Tests that an asset list created maps dependencies correctly. - testdependencieslevel\level.pak and lists of known dependencies are used for validation - - Test Steps: - 1. Create an asset list from the level.pak - 2. Create Lists of expected assets in the level.pak - 3. Add lists of expected assets to a single list - 4. Compare list of expected assets to actual assets - """ - helper = bundler_batch_helper - - # Create the asset list file - helper.call_assetLists( - addSeed=r"levels\testdependencieslevel\level.pak", - assetListFile=helper['asset_info_file_request'] - ) - - assert os.path.isfile(helper["asset_info_file_result"]) - - # Lists of known relative locations of assets - default_level_assets = [ - "engineassets/texturemsg/defaultnouvs.dds", - "engineassets/texturemsg/defaultnouvs.dds.1", - "engineassets/texturemsg/defaultnouvs.dds.2", - "engineassets/texturemsg/defaultnouvs.dds.3", - "engineassets/texturemsg/defaultnouvs.dds.4", - "engineassets/texturemsg/defaultnouvs.dds.5", - "engineassets/texturemsg/defaultnouvs.dds.6", - "engineassets/texturemsg/defaultnouvs.dds.7", - "engineassets/texturemsg/defaultnouvs_ddn.dds", - "engineassets/texturemsg/defaultnouvs_ddn.dds.1", - "engineassets/texturemsg/defaultnouvs_ddn.dds.2", - "engineassets/texturemsg/defaultnouvs_ddn.dds.3", - "engineassets/texturemsg/defaultnouvs_ddn.dds.4", - "engineassets/texturemsg/defaultnouvs_ddn.dds.5", - "engineassets/texturemsg/defaultnouvs_spec.dds", - "engineassets/texturemsg/defaultnouvs_spec.dds.1", - "engineassets/texturemsg/defaultnouvs_spec.dds.2", - "engineassets/texturemsg/defaultnouvs_spec.dds.3", - "engineassets/texturemsg/defaultnouvs_spec.dds.4", - "engineassets/texturemsg/defaultnouvs_spec.dds.5", - "engineassets/textures/defaults/16_grey.dds", - "engineassets/textures/cubemap/default_level_cubemap.dds", - "engineassets/textures/cubemap/default_level_cubemap.dds.1", - "engineassets/textures/cubemap/default_level_cubemap.dds.2", - "engineassets/textures/cubemap/default_level_cubemap.dds.3", - "engineassets/textures/cubemap/default_level_cubemap.dds.4", - "engineassets/textures/cubemap/default_level_cubemap_diff.dds", - "engineassets/materials/water/ocean_default.mtl", - "engineassets/textures/defaults/spot_default.dds", - "engineassets/textures/defaults/spot_default.dds.1", - "engineassets/textures/defaults/spot_default.dds.2", - "engineassets/textures/defaults/spot_default.dds.3", - "engineassets/textures/defaults/spot_default.dds.4", - "engineassets/textures/defaults/spot_default.dds.5", - "materials/material_terrain_default.mtl", - "textures/skys/night/half_moon.dds", - "textures/skys/night/half_moon.dds.1", - "textures/skys/night/half_moon.dds.2", - "textures/skys/night/half_moon.dds.3", - "textures/skys/night/half_moon.dds.4", - "textures/skys/night/half_moon.dds.5", - "textures/skys/night/half_moon.dds.6", - "engineassets/materials/sky/sky.mtl", - "levels/testdependencieslevel/level.pak", - "levels/testdependencieslevel/terrain/cover.ctc", - "levels/testdependencieslevel/terraintexture.pak", - ] - - sequence_material_cube_assets = [ - "textures/test_texture_sequence/test_texture_sequence000.dds", - "textures/test_texture_sequence/test_texture_sequence001.dds", - "textures/test_texture_sequence/test_texture_sequence002.dds", - "textures/test_texture_sequence/test_texture_sequence003.dds", - "textures/test_texture_sequence/test_texture_sequence004.dds", - "textures/test_texture_sequence/test_texture_sequence005.dds", - "objects/_primitives/_box_1x1.cgf", - "materials/test_texture_sequence.mtl", - "objects/_primitives/_box_1x1.mtl", - "textures/_primitives/middle_gray_checker.dds", - "textures/_primitives/middle_gray_checker.dds.1", - "textures/_primitives/middle_gray_checker.dds.2", - "textures/_primitives/middle_gray_checker.dds.3", - "textures/_primitives/middle_gray_checker.dds.4", - "textures/_primitives/middle_gray_checker.dds.5", - "textures/_primitives/middle_gray_checker_ddn.dds", - "textures/_primitives/middle_gray_checker_ddn.dds.1", - "textures/_primitives/middle_gray_checker_ddn.dds.2", - "textures/_primitives/middle_gray_checker_ddn.dds.3", - "textures/_primitives/middle_gray_checker_ddn.dds.4", - "textures/_primitives/middle_gray_checker_ddn.dds.5", - "textures/_primitives/middle_gray_checker_spec.dds", - "textures/_primitives/middle_gray_checker_spec.dds.1", - "textures/_primitives/middle_gray_checker_spec.dds.2", - "textures/_primitives/middle_gray_checker_spec.dds.3", - "textures/_primitives/middle_gray_checker_spec.dds.4", - "textures/_primitives/middle_gray_checker_spec.dds.5", - ] - - character_with_simplified_material_assets = [ - "objects/characters/jack/jack.actor", - "objects/characters/jack/jack.mtl", - "objects/characters/jack/textures/jack_diff.dds", - "objects/characters/jack/textures/jack_diff.dds.1", - "objects/characters/jack/textures/jack_diff.dds.2", - "objects/characters/jack/textures/jack_diff.dds.3", - "objects/characters/jack/textures/jack_diff.dds.4", - "objects/characters/jack/textures/jack_diff.dds.5", - "objects/characters/jack/textures/jack_diff.dds.6", - "objects/characters/jack/textures/jack_diff.dds.7", - "objects/characters/jack/textures/jack_spec.dds", - "objects/characters/jack/textures/jack_spec.dds.1", - "objects/characters/jack/textures/jack_spec.dds.2", - "objects/characters/jack/textures/jack_spec.dds.3", - "objects/characters/jack/textures/jack_spec.dds.4", - "objects/characters/jack/textures/jack_spec.dds.5", - "objects/characters/jack/textures/jack_spec.dds.6", - "objects/characters/jack/textures/jack_spec.dds.7", - "objects/default/editorprimitive.mtl", - "engineassets/textures/grey.dds", - "animations/animationeditorfiles/sample0.animgraph", - "animations/motions/jack_death_fall_back_zup.motion", - "animations/animationeditorfiles/sample1.animgraph", - "animations/animationeditorfiles/sample0.motionset", - "animations/motions/rin_jump.motion", - "animations/animationeditorfiles/sample1.motionset", - "animations/motions/rin_idle.motion", - "animations/motions/jack_idle_aim_zup.motion", - ] - - spawner_assets = [ - "slices/sphere.dynamicslice", - "objects/default/primitive_sphere.cgf", - "test1.luac", - "test2.luac", - ] - - ui_canvas_assets = [ - "fonts/vera.ttf", - "fonts/vera.font", - "scriptcanvas/mainmenu.scriptcanvas_compiled", - "fonts/vera.fontfamily", - "ui/canvas/start.uicanvas", - "fonts/vera-italic.font", - "ui/textureatlas/sample.texatlasidx", - "fonts/vera-bold-italic.ttf", - "fonts/vera-bold.font", - "ui/textures/prefab/button_normal.dds", - "ui/textures/prefab/button_normal.sprite", - "fonts/vera-italic.ttf", - "ui/textureatlas/sample.dds", - "fonts/vera-bold-italic.font", - "fonts/vera-bold.ttf", - "ui/textures/prefab/button_disabled.dds", - "ui/textures/prefab/button_disabled.sprite", - ] - - wwise_and_atl_assets = [ - "libs/gameaudio/wwise/levels/testdependencieslevel/test_dependencies_level.xml", - "sounds/wwise/test_bank3.bnk", - "sounds/wwise/test_bank4.bnk", - "sounds/wwise/test_bank5.bnk", - "sounds/wwise/test_bank1.bnk", - "sounds/wwise/init.bnk", - "sounds/wwise/499820003.wem", - "sounds/wwise/196049145.wem", - ] - - particle_library_assets = [ - "libs/particles/milestone2particles.xml", - "textures/milestone2/particles/fx_launchermuzzlering_01.dds", - "textures/milestone2/particles/fx_launchermuzzlering_01.dds.1", - "textures/milestone2/particles/fx_launchermuzzlering_01.dds.2", - "textures/milestone2/particles/fx_launchermuzzlering_01.dds.3", - "textures/milestone2/particles/fx_launchermuzzlering_01.dds.4", - "textures/milestone2/particles/fx_launchermuzzlering_01.dds.5", - "textures/milestone2/particles/fx_sparkstreak_01.dds", - "textures/milestone2/particles/fx_launchermuzzlefront_01.dds", - "textures/milestone2/particles/fx_launchermuzzlefront_01.dds.1", - "textures/milestone2/particles/fx_launchermuzzlefront_01.dds.2", - "textures/milestone2/particles/fx_launchermuzzlefront_01.dds.3", - "textures/milestone2/particles/fx_launchermuzzlefront_01.dds.4", - "textures/milestone2/particles/fx_launchermuzzlefront_01.dds.5", - ] - - lens_flares_library_assets = ["libs/flares/flares.xml", "textures/lights/flare01.dds"] - - expected_assets_list = default_level_assets - expected_assets_list.extend(sequence_material_cube_assets) - expected_assets_list.extend(character_with_simplified_material_assets) - expected_assets_list.extend(spawner_assets) - expected_assets_list.extend(ui_canvas_assets) - expected_assets_list.extend(wwise_and_atl_assets) - expected_assets_list.extend(particle_library_assets) - expected_assets_list.extend(lens_flares_library_assets) # All expected assets - - # Get actual calculated dependencies from the asset list created - actual_assets_list = [] - for rel_path in helper.get_asset_relative_paths(helper["asset_info_file_result"]): - actual_assets_list.append(rel_path) - - assert sorted(actual_assets_list) == sorted(expected_assets_list) @pytest.mark.BAT @pytest.mark.assetpipeline @@ -310,9 +102,9 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): 3. Read and store contents of asset list into memory 4. Attempt to create a new asset list in without using --allowOverwrites 5. Verify that Asset Bundler returns false - 6. Verify that file contents of the orignally created asset list did not change from what was stored in memory + 6. Verify that file contents of the originally created asset list did not change from what was stored in memory 7. Attempt to create a new asset list without debug while allowing overwrites - 8. Verify that file contents of the orignally created asset list changed from what was stored in memory + 8. Verify that file contents of the originally created asset list changed from what was stored in memory """ helper = bundler_batch_helper seed_list = os.path.join(workspace.paths.engine_root(), "Assets", "Engine", "SeedAssetList.seed") # Engine seed list @@ -919,7 +711,7 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): # Extra arguments for pattern comparison cmd.extend([f"--filePatternType={pattern_type}", f"--filePattern={pattern}"]) if workspace.project: - cmd.append(f'--project-path={project_name}') + cmd.append(f'--project-path={workspace.paths.project()}') return cmd # End generate_compare_command() @@ -960,7 +752,7 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): output_mac_asset_list = helper.platform_file_name(last_output_arg, platform) # Build execution command - cmd = generate_compare_command(platform_arg, workspace.project) + cmd = generate_compare_command(platform_arg, workspace.paths.project()) # Execute command subprocess.check_call(cmd) @@ -995,7 +787,7 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): f"--comparisonRulesFile={rule_file}", f"--comparisonType={args[1]}", r"--addComparison", - f"--project-path={workspace.project}", + f"--project-path={workspace.paths.project()}", ] if args[1] == "4": # If pattern comparison, append a few extra arguments @@ -1117,7 +909,7 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): "--addDefaultSeedListFiles", "--platform=pc", "--print", - f"--project-path={workspace.project}" + f"--project-path={workspace.paths.project()}" ], universal_newlines=True, ) @@ -1189,7 +981,7 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): # Make sure file gets deleted on teardown request.addfinalizer(lambda: fs.delete([bundle_result_path], True, False)) - bundles_folder = os.path.join(workspace.paths.engine_root(), workspace.project, "Bundles") + bundles_folder = os.path.join(workspace.paths.project(), "Bundles") level_pak = r"levels\testdependencieslevel\level.pak" bundle_request_path = os.path.join(bundles_folder, "bundle.pak") bundle_result_path = os.path.join(bundles_folder, @@ -1243,23 +1035,64 @@ class TestsAssetBundlerBatch_WindowsAndMac(object): 2. Verify file was created 3. Verify that only the expected assets are present in the created asset list """ - expected_assets = [ + expected_assets = sorted([ "ui/canvases/lyshineexamples/animation/multiplesequences.uicanvas", - "ui/textures/prefab/button_normal.sprite" - ] + "ui/textures/prefab/button_disabled.tif.streamingimage", + "ui/textures/prefab/tooltip_sliced.tif.streamingimage", + "ui/textures/prefab/button_normal.tif.streamingimage" + ]) + # Printing these lists out can save a step in debugging if this test fails on Jenkins. + logger.info(f"expected_assets: {expected_assets}") + + skip_assets = sorted([ + "ui/scripts/lyshineexamples/animation/multiplesequences.luac", + "ui/scripts/lyshineexamples/unloadthiscanvasbutton.luac", + "fonts/vera.fontfamily", + "fonts/vera-italic.font", + "fonts/vera.font", + "fonts/vera-bold.font", + "fonts/vera-bold-italic.font", + "fonts/vera-italic.ttf", + "fonts/vera.ttf", + "fonts/vera-bold.ttf", + "fonts/vera-bold-italic.ttf" + ]) + logger.info(f"skip_assets: {skip_assets}") + + expected_and_skip_assets = sorted(expected_assets + skip_assets) + # Printing both together to make it quick to compare the results in the logs for a test failure on Jenkins + logger.info(f"expected_and_skip_assets: {expected_and_skip_assets}") + + # First, generate an asset info file without skipping, to get a list that can be used as a baseline to verify + # the files were actually skipped, and not just missing. + bundler_batch_helper.call_assetLists( + assetListFile=bundler_batch_helper['asset_info_file_request'], + addSeed="ui/canvases/lyshineexamples/animation/multiplesequences.uicanvas" + ) + assert os.path.isfile(bundler_batch_helper["asset_info_file_result"]) + assets_in_no_skip_list = [] + for rel_path in bundler_batch_helper.get_asset_relative_paths(bundler_batch_helper["asset_info_file_result"]): + assets_in_no_skip_list.append(rel_path) + assets_in_no_skip_list = sorted(assets_in_no_skip_list) + logger.info(f"assets_in_no_skip_list: {assets_in_no_skip_list}") + assert assets_in_no_skip_list == expected_and_skip_assets + + # Now generate an asset info file using the skip command, and verify the skip files are not in the list. bundler_batch_helper.call_assetLists( assetListFile=bundler_batch_helper['asset_info_file_request'], addSeed="ui/canvases/lyshineexamples/animation/multiplesequences.uicanvas", - skip="ui/textures/prefab/button_disabled.sprite,ui/scripts/lyshineexamples/animation/multiplesequences.luac," - "ui/textures/prefab/tooltip_sliced.sprite,ui/scripts/lyshineexamples/unloadthiscanvasbutton.luac,fonts/vera.fontfamily,fonts/vera-italic.font," - "fonts/vera.font,fonts/vera-bold.font,fonts/vera-bold-italic.font,fonts/vera-italic.ttf,fonts/vera.ttf,fonts/vera-bold.ttf,fonts/vera-bold-italic.ttf" + allowOverwrites="", + skip=','.join(skip_assets) ) + assert os.path.isfile(bundler_batch_helper["asset_info_file_result"]) assets_in_list = [] for rel_path in bundler_batch_helper.get_asset_relative_paths(bundler_batch_helper["asset_info_file_result"]): assets_in_list.append(rel_path) + assets_in_list = sorted(assets_in_list) + logger.info(f"assets_in_list: {assets_in_list}") + assert assets_in_list == expected_assets - assert sorted(assets_in_list) == sorted(expected_assets) @pytest.mark.BAT @pytest.mark.assetpipeline diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp index 920e99665d..c1fc9138d1 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp @@ -64,6 +64,9 @@ namespace AzToolsFramework::Prefab ); m_backButton->setToolTip("Up one level (-)"); + + // Currently hide this button until we can correctly disable/enable it based on context. + m_backButton->hide(); } void PrefabViewportFocusPathHandler::OnPrefabFocusChanged() diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp index e303e1148e..14b428a726 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp @@ -140,7 +140,8 @@ namespace O3DE::ProjectManager // Select the first entry after everything got correctly sized QTimer::singleShot(200, [=]{ QModelIndex firstModelIndex = m_gemModel->index(0, 0); - m_gemModel->GetSelectionModel()->select(firstModelIndex, QItemSelectionModel::ClearAndSelect); + QModelIndex proxyIndex = m_proxyModel->mapFromSource(firstModelIndex); + m_proxyModel->GetSelectionModel()->setCurrentIndex(proxyIndex, QItemSelectionModel::ClearAndSelect); }); } @@ -223,7 +224,7 @@ namespace O3DE::ProjectManager const bool gemFound = gemInfoHash.contains(gemName); if (!gemFound && !m_gemModel->IsAdded(index) && !m_gemModel->IsAddedDependency(index)) { - m_gemModel->removeRow(i); + m_gemModel->RemoveGem(index); } else { @@ -253,7 +254,7 @@ namespace O3DE::ProjectManager m_filterWidget->ResetAllFilters(); // Reselect the same selection to proc UI updates - m_proxyModel->GetSelectionModel()->select(m_proxyModel->GetSelectionModel()->selection(), QItemSelectionModel::Select); + m_proxyModel->GetSelectionModel()->setCurrentIndex(m_proxyModel->GetSelectionModel()->currentIndex(), QItemSelectionModel::Select); } void GemCatalogScreen::OnGemStatusChanged(const QString& gemName, uint32_t numChangedDependencies) @@ -282,7 +283,7 @@ namespace O3DE::ProjectManager if (added && GemModel::GetDownloadStatus(modelIndex) == GemInfo::DownloadStatus::NotDownloaded) { m_downloadController->AddGemDownload(GemModel::GetName(modelIndex)); - GemModel::SetDownloadStatus(*m_proxyModel, m_proxyModel->mapFromSource(modelIndex), GemInfo::DownloadStatus::Downloading); + GemModel::SetDownloadStatus(*m_gemModel, modelIndex, GemInfo::DownloadStatus::Downloading); } } @@ -314,7 +315,7 @@ namespace O3DE::ProjectManager } QModelIndex proxyIndex = m_proxyModel->mapFromSource(modelIndex); - m_proxyModel->GetSelectionModel()->select(proxyIndex, QItemSelectionModel::ClearAndSelect); + m_proxyModel->GetSelectionModel()->setCurrentIndex(proxyIndex, QItemSelectionModel::ClearAndSelect); m_gemListView->scrollTo(proxyIndex); m_rightPanelStack->setCurrentIndex(RightPanelWidgetOrder::Inspector); @@ -378,6 +379,9 @@ namespace O3DE::ProjectManager { const QString selectedGemPath = m_gemModel->GetPath(modelIndex); + // Remove gem from gems to be added + GemModel::SetIsAdded(*m_gemModel, modelIndex, false); + // Unregister the gem auto unregisterResult = PythonBindingsInterface::Get()->UnregisterGem(selectedGemPath); if (!unregisterResult) @@ -386,8 +390,10 @@ namespace O3DE::ProjectManager } else { + const QString selectedGemName = m_gemModel->GetName(modelIndex); + // Remove gem from model - m_gemModel->removeRow(modelIndex.row()); + m_gemModel->RemoveGem(modelIndex); // Delete uninstalled gem directory if (!ProjectUtils::DeleteProjectFiles(selectedGemPath, /*force*/true)) @@ -398,6 +404,11 @@ namespace O3DE::ProjectManager // Show undownloaded remote gem again Refresh(); + + // Select remote gem + QModelIndex remoteGemIndex = m_gemModel->FindIndexByNameString(selectedGemName); + QModelIndex proxyIndex = m_proxyModel->mapFromSource(remoteGemIndex); + m_proxyModel->GetSelectionModel()->setCurrentIndex(proxyIndex, QItemSelectionModel::ClearAndSelect); } } } @@ -592,7 +603,8 @@ namespace O3DE::ProjectManager if (succeeded) { // refresh the information for downloaded gems - const AZ::Outcome, AZStd::string>& allGemInfosResult = PythonBindingsInterface::Get()->GetAllGemInfos(m_projectPath); + const AZ::Outcome, AZStd::string>& allGemInfosResult = + PythonBindingsInterface::Get()->GetAllGemInfos(m_projectPath); if (allGemInfosResult.IsSuccess()) { // we should find the gem name now in all gem infos @@ -600,15 +612,33 @@ namespace O3DE::ProjectManager { if (gemInfo.m_name == gemName) { - QModelIndex index = m_gemModel->FindIndexByNameString(gemName); - if (index.isValid()) + QModelIndex oldIndex = m_gemModel->FindIndexByNameString(gemName); + if (oldIndex.isValid()) { - m_proxyModel->setData(m_proxyModel->mapFromSource(index), GemInfo::DownloadSuccessful, GemModel::RoleDownloadStatus); - m_gemModel->setData(index, gemInfo.m_path, GemModel::RolePath); - m_gemModel->setData(index, gemInfo.m_path, GemModel::RoleDirectoryLink); + // Check if old gem is selected + bool oldGemSelected = false; + if (m_gemModel->GetSelectionModel()->currentIndex() == oldIndex) + { + oldGemSelected = true; + } + + // Remove old remote gem + m_gemModel->RemoveGem(oldIndex); + + // Add new downloaded version of gem + QModelIndex newIndex = m_gemModel->AddGem(gemInfo); + GemModel::SetDownloadStatus(*m_gemModel, newIndex, GemInfo::DownloadSuccessful); + GemModel::SetIsAdded(*m_gemModel, newIndex, true); + + // Select new version of gem if it was previously selected + if (oldGemSelected) + { + QModelIndex proxyIndex = m_proxyModel->mapFromSource(newIndex); + m_proxyModel->GetSelectionModel()->setCurrentIndex(proxyIndex, QItemSelectionModel::ClearAndSelect); + } } - return; + break; } } } @@ -618,7 +648,7 @@ namespace O3DE::ProjectManager QModelIndex index = m_gemModel->FindIndexByNameString(gemName); if (index.isValid()) { - m_proxyModel->setData(m_proxyModel->mapFromSource(index), GemInfo::DownloadFailed, GemModel::RoleDownloadStatus); + GemModel::SetDownloadStatus(*m_gemModel, index, GemInfo::DownloadFailed); } } } diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp index 886b9e57c7..95fdc8e1e2 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp @@ -26,14 +26,14 @@ namespace O3DE::ProjectManager return m_selectionModel; } - void GemModel::AddGem(const GemInfo& gemInfo) + QModelIndex GemModel::AddGem(const GemInfo& gemInfo) { if (FindIndexByNameString(gemInfo.m_name).isValid()) { // do not add gems with duplicate names // this can happen by mistake or when a gem repo has a gem with the same name as a local gem AZ_TracePrintf("GemModel", "Ignoring duplicate gem: %s", gemInfo.m_name.toUtf8().constData()); - return; + return QModelIndex(); } QStandardItem* item = new QStandardItem(); @@ -67,6 +67,22 @@ namespace O3DE::ProjectManager const QModelIndex modelIndex = index(rowCount()-1, 0); m_nameToIndexMap[gemInfo.m_name] = modelIndex; + + return modelIndex; + } + + void GemModel::RemoveGem(const QModelIndex& modelIndex) + { + removeRow(modelIndex.row()); + } + + void GemModel::RemoveGem(const QString& gemName) + { + auto nameFind = m_nameToIndexMap.find(gemName); + if (nameFind != m_nameToIndexMap.end()) + { + removeRow(nameFind->row()); + } } void GemModel::Clear() @@ -391,11 +407,11 @@ namespace O3DE::ProjectManager // Select a valid row if currently selected row was removed if (selectedRowRemoved) { - for (const QModelIndex& index : m_nameToIndexMap) + for (const QModelIndex& index : m_nameToIndexMap) { if (index.isValid()) { - GetSelectionModel()->select(index, QItemSelectionModel::ClearAndSelect); + GetSelectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect); break; } } diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h index 87a718d8c7..bb89d46861 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h @@ -55,7 +55,9 @@ namespace O3DE::ProjectManager RoleRepoUri }; - void AddGem(const GemInfo& gemInfo); + QModelIndex AddGem(const GemInfo& gemInfo); + void RemoveGem(const QModelIndex& modelIndex); + void RemoveGem(const QString& gemName); void Clear(); void UpdateGemDependencies(); diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.cpp b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.cpp index 794635a3e3..91432c2346 100644 --- a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.cpp +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.cpp @@ -75,7 +75,7 @@ namespace O3DE::ProjectManager // Select the first entry after everything got correctly sized QTimer::singleShot(200, [=]{ QModelIndex firstModelIndex = m_gemRepoListView->model()->index(0,0); - m_gemRepoListView->selectionModel()->select(firstModelIndex, QItemSelectionModel::ClearAndSelect); + m_gemRepoListView->selectionModel()->setCurrentIndex(firstModelIndex, QItemSelectionModel::ClearAndSelect); }); } diff --git a/Gems/Atom/RPI/Code/CMakeLists.txt b/Gems/Atom/RPI/Code/CMakeLists.txt index 521178be20..f0459a23c2 100644 --- a/Gems/Atom/RPI/Code/CMakeLists.txt +++ b/Gems/Atom/RPI/Code/CMakeLists.txt @@ -160,6 +160,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS) Gem::Atom_RPI.Public Gem::Atom_RHI.Public Gem::Atom_RPI.Edit + Gem::Atom_Utils.TestUtils.Static ) ly_add_googletest( NAME Gem::Atom_RPI.Tests diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialTypeSourceData.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialTypeSourceData.h index 1234b15f95..f5336807c7 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialTypeSourceData.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialTypeSourceData.h @@ -209,10 +209,6 @@ namespace AZ //! Traversal will stop once all properties have been enumerated or the callback function returns false void EnumeratePropertiesInDisplayOrder(const EnumeratePropertiesCallback& callback) const; - //! Convert the property value into the format that will be stored in the source data - //! This is primarily needed to support conversions of special types like enums and images - bool ConvertPropertyValueToSourceDataFormat(const PropertyDefinition& propertyDefinition, MaterialPropertyValue& propertyValue) const; - Outcome> CreateMaterialTypeAsset(Data::AssetId assetId, AZStd::string_view materialTypeSourceFilePath = "", bool elevateWarnings = true) const; //! Possibly renames @propertyId based on the material version update steps. diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp index 396ba71e14..d5551e89ae 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp @@ -300,48 +300,6 @@ namespace AZ } } - bool MaterialTypeSourceData::ConvertPropertyValueToSourceDataFormat(const PropertyDefinition& propertyDefinition, MaterialPropertyValue& propertyValue) const - { - if (propertyDefinition.m_dataType == AZ::RPI::MaterialPropertyDataType::Enum && propertyValue.Is()) - { - const uint32_t index = propertyValue.GetValue(); - if (index >= propertyDefinition.m_enumValues.size()) - { - AZ_Error("Material source data", false, "Invalid value for material enum property: '%s'.", propertyDefinition.m_name.c_str()); - return false; - } - - propertyValue = propertyDefinition.m_enumValues[index]; - return true; - } - - // Image asset references must be converted from asset IDs to a relative source file path - if (propertyDefinition.m_dataType == AZ::RPI::MaterialPropertyDataType::Image && propertyValue.Is>()) - { - const Data::Asset& imageAsset = propertyValue.GetValue>(); - - Data::AssetInfo imageAssetInfo; - if (imageAsset.GetId().IsValid()) - { - bool result = false; - AZStd::string rootFilePath; - const AZStd::string platformName = ""; // Empty for default - AzToolsFramework::AssetSystemRequestBus::BroadcastResult(result, &AzToolsFramework::AssetSystem::AssetSystemRequest::GetAssetInfoById, - imageAsset.GetId(), imageAsset.GetType(), platformName, imageAssetInfo, rootFilePath); - if (!result) - { - AZ_Error("Material source data", false, "Image asset could not be found for property: '%s'.", propertyDefinition.m_name.c_str()); - return false; - } - } - - propertyValue = imageAssetInfo.m_relativePath; - return true; - } - - return true; - } - Outcome> MaterialTypeSourceData::CreateMaterialTypeAsset(Data::AssetId assetId, AZStd::string_view materialTypeSourceFilePath, bool elevateWarnings) const { MaterialTypeAssetCreator materialTypeAssetCreator; diff --git a/Gems/Atom/RPI/Code/Tests/Common/RPITestFixture.h b/Gems/Atom/RPI/Code/Tests/Common/RPITestFixture.h index 48d404d268..0707528d7f 100644 --- a/Gems/Atom/RPI/Code/Tests/Common/RPITestFixture.h +++ b/Gems/Atom/RPI/Code/Tests/Common/RPITestFixture.h @@ -21,7 +21,7 @@ #include #include #include -#include +#include namespace UnitTest { diff --git a/Gems/Atom/RPI/Code/atom_rpi_tests_files.cmake b/Gems/Atom/RPI/Code/atom_rpi_tests_files.cmake index e99e4e456b..5d67948ac8 100644 --- a/Gems/Atom/RPI/Code/atom_rpi_tests_files.cmake +++ b/Gems/Atom/RPI/Code/atom_rpi_tests_files.cmake @@ -10,8 +10,6 @@ set(FILES Tests/Buffer/BufferTests.cpp Tests/Common/AssetManagerTestFixture.cpp Tests/Common/AssetManagerTestFixture.h - Tests/Common/AssetSystemStub.cpp - Tests/Common/AssetSystemStub.h Tests/Common/ErrorMessageFinder.cpp Tests/Common/ErrorMessageFinder.h Tests/Common/ErrorMessageFinderTests.cpp diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/CMakeLists.txt b/Gems/Atom/Tools/AtomToolsFramework/Code/CMakeLists.txt index e64c9b80e7..40c8d7956e 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/CMakeLists.txt +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/CMakeLists.txt @@ -61,3 +61,31 @@ ly_add_target( PRIVATE Gem::AtomToolsFramework.Static ) + +################################################################################ +# Tests +################################################################################ +if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) + + ly_add_target( + NAME AtomToolsFramework.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} + NAMESPACE Gem + FILES_CMAKE + atomtoolsframework_tests_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + . + Tests + BUILD_DEPENDENCIES + PRIVATE + AZ::AzTest + AZ::AzTestShared + Gem::AtomToolsFramework.Static + Gem::Atom_Utils.TestUtils.Static + ) + + ly_add_googletest( + NAME Gem::AtomToolsFramework.Tests + ) + +endif() \ No newline at end of file diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Util/MaterialPropertyUtil.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Util/MaterialPropertyUtil.h index 333796493d..08f59f9e0b 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Util/MaterialPropertyUtil.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Util/MaterialPropertyUtil.h @@ -7,11 +7,12 @@ */ #pragma once -#include -#include #include #include #include +#include +#include +#include namespace AzToolsFramework { @@ -35,12 +36,31 @@ namespace AtomToolsFramework //! Convert and assign material property meta data fields to editor dynamic property configuration void ConvertToPropertyConfig(AtomToolsFramework::DynamicPropertyConfig& propertyConfig, const AZ::RPI::MaterialPropertyDynamicMetadata& propertyMetaData); - //! Convert and assign editor dynamic property configuration fields to material property meta data + //! Convert and assign editor dynamic property configuration fields to material property meta data void ConvertToPropertyMetaData(AZ::RPI::MaterialPropertyDynamicMetadata& propertyMetaData, const AtomToolsFramework::DynamicPropertyConfig& propertyConfig); //! Compare equality of data types and values of editor property stored in AZStd::any bool ArePropertyValuesEqual(const AZStd::any& valueA, const AZStd::any& valueB); + //! Convert the property value into the format that will be stored in the source data + //! This is primarily needed to support conversions of special types like enums and images + //! @param exportPath absolute path of the file being saved + //! @param propertyDefinition describes type information and other details about propertyValue + //! @param propertyValue the value being converted before saving + bool ConvertToExportFormat( + const AZStd::string& exportPath, + const AZ::RPI::MaterialTypeSourceData::PropertyDefinition& propertyDefinition, + AZ::RPI::MaterialPropertyValue& propertyValue); + + //! Generate a file path from the exported file to the external reference. + //! This function returns a relative path from the export file to the reference file. + //! If the relative path is too different or distant from the export path then we return the asset folder relative path. + //! @param exportPath absolute path of the file being saved + //! @param referencePath absolute path of a file that will be treated as an external reference + //! @param maxPathDepth the maximum relative depth or number of parent or child folders between the export path and the reference path + AZStd::string GetExteralReferencePath( + const AZStd::string& exportPath, const AZStd::string& referencePath, const uint32_t maxPathDepth = 2); + //! Traverse up the instance data node hierarchy to find the containing dynamic property object const AtomToolsFramework::DynamicProperty* FindDynamicPropertyForInstanceDataNode(const AzToolsFramework::InstanceDataNode* pNode); } // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Util/MaterialPropertyUtil.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Util/MaterialPropertyUtil.cpp index 3f8a173918..c49cd3fafb 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Util/MaterialPropertyUtil.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Util/MaterialPropertyUtil.cpp @@ -6,9 +6,10 @@ * */ -#include #include +#include +#include #include #include #include @@ -18,6 +19,7 @@ #include #include #include +#include #include namespace AtomToolsFramework @@ -163,6 +165,88 @@ namespace AtomToolsFramework return false; } + bool ConvertToExportFormat( + const AZStd::string& exportPath, + const AZ::RPI::MaterialTypeSourceData::PropertyDefinition& propertyDefinition, + AZ::RPI::MaterialPropertyValue& propertyValue) + { + if (propertyDefinition.m_dataType == AZ::RPI::MaterialPropertyDataType::Enum && propertyValue.Is()) + { + const uint32_t index = propertyValue.GetValue(); + if (index >= propertyDefinition.m_enumValues.size()) + { + AZ_Error("AtomToolsFramework", false, "Invalid value for material enum property: '%s'.", propertyDefinition.m_name.c_str()); + return false; + } + + propertyValue = propertyDefinition.m_enumValues[index]; + return true; + } + + // Image asset references must be converted from asset IDs to a relative source file path + if (propertyDefinition.m_dataType == AZ::RPI::MaterialPropertyDataType::Image) + { + if (propertyValue.Is>()) + { + const auto& imageAsset = propertyValue.GetValue>(); + const auto& imagePath = AZ::RPI::AssetUtils::GetSourcePathByAssetId(imageAsset.GetId()); + propertyValue = GetExteralReferencePath(exportPath, imagePath); + return true; + } + + if (propertyValue.Is>()) + { + const auto& image = propertyValue.GetValue>(); + const auto& imagePath = image ? AZ::RPI::AssetUtils::GetSourcePathByAssetId(image->GetAssetId()) : ""; + propertyValue = GetExteralReferencePath(exportPath, imagePath); + return true; + } + } + + return true; + } + + AZStd::string GetExteralReferencePath(const AZStd::string& exportPath, const AZStd::string& referencePath, const uint32_t maxPathDepth) + { + if (referencePath.empty()) + { + return {}; + } + + AZ::IO::BasicPath exportFolder(exportPath); + exportFolder.RemoveFilename(); + + const AZStd::string relativePath = AZ::IO::PathView(referencePath).LexicallyRelative(exportFolder).StringAsPosix(); + + // Count the difference in depth between the export file path and the referenced file path. + uint32_t parentFolderCount = 0; + AZStd::string::size_type pos = 0; + const AZStd::string parentFolderToken = ".."; + while ((pos = relativePath.find(parentFolderToken, pos)) != AZStd::string::npos) + { + parentFolderCount++; + pos += parentFolderToken.length(); + } + + // If the difference in depth is too great then revert to using the asset folder relative path. + // We could change this to only use relative paths for references in subfolders. + if (parentFolderCount > maxPathDepth) + { + AZStd::string watchFolder; + AZ::Data::AssetInfo assetInfo; + bool sourceInfoFound = false; + AzToolsFramework::AssetSystemRequestBus::BroadcastResult( + sourceInfoFound, &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath, referencePath.c_str(), + assetInfo, watchFolder); + if (sourceInfoFound) + { + return assetInfo.m_relativePath; + } + } + + return relativePath; + } + const AtomToolsFramework::DynamicProperty* FindDynamicPropertyForInstanceDataNode(const AzToolsFramework::InstanceDataNode* pNode) { // Traverse up the hierarchy from the input node to search for an instance corresponding to material inspector property @@ -172,7 +256,8 @@ namespace AtomToolsFramework const AZ::SerializeContext::ClassData* classData = currentNode->GetClassMetadata(); if (context && classData) { - if (context->CanDowncast(classData->m_typeId, azrtti_typeid(), classData->m_azRtti, nullptr)) + if (context->CanDowncast( + classData->m_typeId, azrtti_typeid(), classData->m_azRtti, nullptr)) { return static_cast(currentNode->FirstInstance()); } diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Tests/AtomToolsFrameworkTest.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Tests/AtomToolsFrameworkTest.cpp index aee8e2a775..df16cfbc43 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Tests/AtomToolsFrameworkTest.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Tests/AtomToolsFrameworkTest.cpp @@ -7,25 +7,71 @@ */ #include +#include +#include -class AtomToolsFrameworkTest - : public ::testing::Test +namespace UnitTest { -protected: - void SetUp() override + class AtomToolsFrameworkTest : public ::testing::Test { + protected: + void SetUp() override + { + if (!AZ::AllocatorInstance::IsReady()) + { + AZ::AllocatorInstance::Create(AZ::SystemAllocator::Descriptor()); + } + m_assetSystemStub.Activate(); + + RegisterSourceAsset("objects/upgrades/materials/supercondor.material"); + RegisterSourceAsset("materials/condor.material"); + RegisterSourceAsset("materials/talisman.material"); + RegisterSourceAsset("materials/city.material"); + RegisterSourceAsset("materials/totem.material"); + RegisterSourceAsset("textures/orange.png"); + RegisterSourceAsset("textures/red.png"); + RegisterSourceAsset("textures/gold.png"); + RegisterSourceAsset("textures/fuzz.png"); + } + + void TearDown() override + { + m_assetSystemStub.Deactivate(); + + if (AZ::AllocatorInstance::IsReady()) + { + AZ::AllocatorInstance::Destroy(); + } + } + + void RegisterSourceAsset(const AZStd::string& path) + { + const AZ::IO::BasicPath assetRootPath = AZ::IO::PathView(m_assetRoot).LexicallyNormal(); + const AZ::IO::BasicPath normalizedPath = AZ::IO::BasicPath(assetRootPath).Append(path).LexicallyNormal(); + + AZ::Data::AssetInfo assetInfo = {}; + assetInfo.m_assetId = AZ::Uuid::CreateRandom(); + assetInfo.m_relativePath = normalizedPath.LexicallyRelative(assetRootPath).StringAsPosix(); + m_assetSystemStub.RegisterSourceInfo(normalizedPath.StringAsPosix().c_str(), assetInfo, assetRootPath.StringAsPosix().c_str()); + } + + static constexpr const char* m_assetRoot = "d:/project/assets/"; + AssetSystemStub m_assetSystemStub; + }; + + TEST_F(AtomToolsFrameworkTest, GetExteralReferencePath_Succeeds) + { + ASSERT_EQ(AtomToolsFramework::GetExteralReferencePath("", "", 2), ""); + ASSERT_EQ(AtomToolsFramework::GetExteralReferencePath("d:/project/assets/materials/condor.material", "", 2), ""); + ASSERT_EQ(AtomToolsFramework::GetExteralReferencePath("d:/project/assets/materials/talisman.material", "", 2), ""); + ASSERT_EQ(AtomToolsFramework::GetExteralReferencePath("d:/project/assets/materials/talisman.material", "d:/project/assets/textures/gold.png", 2), "../textures/gold.png"); + ASSERT_EQ(AtomToolsFramework::GetExteralReferencePath("d:/project/assets/materials/talisman.material", "d:/project/assets/textures/gold.png", 0), "textures/gold.png"); + ASSERT_EQ(AtomToolsFramework::GetExteralReferencePath("d:/project/assets/objects/upgrades/materials/supercondor.material", "d:/project/assets/materials/condor.material", 3), "../../../materials/condor.material"); + ASSERT_EQ(AtomToolsFramework::GetExteralReferencePath("d:/project/assets/objects/upgrades/materials/supercondor.material", "d:/project/assets/materials/condor.material", 2), "materials/condor.material"); + ASSERT_EQ(AtomToolsFramework::GetExteralReferencePath("d:/project/assets/objects/upgrades/materials/supercondor.material", "d:/project/assets/materials/condor.material", 1), "materials/condor.material"); + ASSERT_EQ(AtomToolsFramework::GetExteralReferencePath("d:/project/assets/objects/upgrades/materials/supercondor.material", "d:/project/assets/materials/condor.material", 0), "materials/condor.material"); } - void TearDown() override - { - - } -}; - -TEST_F(AtomToolsFrameworkTest, SanityTest) -{ - ASSERT_TRUE(true); -} - -AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV); + AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV); +} // namespace UnitTest diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/atomtoolsframework_tests_files.cmake b/Gems/Atom/Tools/AtomToolsFramework/Code/atomtoolsframework_tests_files.cmake new file mode 100644 index 0000000000..bd9ad9b3d8 --- /dev/null +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/atomtoolsframework_tests_files.cmake @@ -0,0 +1,11 @@ +# +# 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 +# +# + +set(FILES + Tests/AtomToolsFrameworkTest.cpp +) \ No newline at end of file diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp index ee58cbea3e..8635d1eb3e 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp @@ -230,18 +230,13 @@ namespace MaterialEditor // create source data from properties MaterialSourceData sourceData; - sourceData.m_materialType = m_materialSourceData.m_materialType; - sourceData.m_parentMaterial = m_materialSourceData.m_parentMaterial; - - AZ_Assert(m_materialAsset && m_materialAsset->GetMaterialTypeAsset(), "When IsOpen() is true, these assets should not be null."); sourceData.m_materialTypeVersion = m_materialAsset->GetMaterialTypeAsset()->GetVersion(); - - // Force save data to store forward slashes - AzFramework::StringFunc::Replace(sourceData.m_materialType, "\\", "/"); - AzFramework::StringFunc::Replace(sourceData.m_parentMaterial, "\\", "/"); + sourceData.m_materialType = AtomToolsFramework::GetExteralReferencePath(m_absolutePath, m_materialSourceData.m_materialType); + sourceData.m_parentMaterial = AtomToolsFramework::GetExteralReferencePath(m_absolutePath, m_materialSourceData.m_parentMaterial); // populate sourceData with modified or overwritten properties - const bool savedProperties = SavePropertiesToSourceData(sourceData, [](const AtomToolsFramework::DynamicProperty& property) { + const bool savedProperties = SavePropertiesToSourceData(m_absolutePath, sourceData, [](const AtomToolsFramework::DynamicProperty& property) + { return !AtomToolsFramework::ArePropertyValuesEqual(property.GetValue(), property.GetConfig().m_parentValue); }); @@ -304,18 +299,13 @@ namespace MaterialEditor // create source data from properties MaterialSourceData sourceData; - sourceData.m_materialType = m_materialSourceData.m_materialType; - sourceData.m_parentMaterial = m_materialSourceData.m_parentMaterial; - - AZ_Assert(m_materialAsset && m_materialAsset->GetMaterialTypeAsset(), "When IsOpen() is true, these assets should not be null."); sourceData.m_materialTypeVersion = m_materialAsset->GetMaterialTypeAsset()->GetVersion(); - - // Force save data to store forward slashes - AzFramework::StringFunc::Replace(sourceData.m_materialType, "\\", "/"); - AzFramework::StringFunc::Replace(sourceData.m_parentMaterial, "\\", "/"); + sourceData.m_materialType = AtomToolsFramework::GetExteralReferencePath(normalizedSavePath, m_materialSourceData.m_materialType); + sourceData.m_parentMaterial = AtomToolsFramework::GetExteralReferencePath(normalizedSavePath, m_materialSourceData.m_parentMaterial); // populate sourceData with modified or overwritten properties - const bool savedProperties = SavePropertiesToSourceData(sourceData, [](const AtomToolsFramework::DynamicProperty& property) { + const bool savedProperties = SavePropertiesToSourceData(normalizedSavePath, sourceData, [](const AtomToolsFramework::DynamicProperty& property) + { return !AtomToolsFramework::ArePropertyValuesEqual(property.GetValue(), property.GetConfig().m_parentValue); }); @@ -377,23 +367,18 @@ namespace MaterialEditor // create source data from properties MaterialSourceData sourceData; - sourceData.m_materialType = m_materialSourceData.m_materialType; - - AZ_Assert(m_materialAsset && m_materialAsset->GetMaterialTypeAsset(), "When IsOpen() is true, these assets should not be null."); sourceData.m_materialTypeVersion = m_materialAsset->GetMaterialTypeAsset()->GetVersion(); + sourceData.m_materialType = AtomToolsFramework::GetExteralReferencePath(normalizedSavePath, m_materialSourceData.m_materialType); // Only assign a parent path if the source was a .material if (AzFramework::StringFunc::Path::IsExtension(m_relativePath.c_str(), MaterialSourceData::Extension)) { - sourceData.m_parentMaterial = m_relativePath; + sourceData.m_parentMaterial = AtomToolsFramework::GetExteralReferencePath(normalizedSavePath, m_absolutePath); } - // Force save data to store forward slashes - AzFramework::StringFunc::Replace(sourceData.m_materialType, "\\", "/"); - AzFramework::StringFunc::Replace(sourceData.m_parentMaterial, "\\", "/"); - // populate sourceData with modified properties - const bool savedProperties = SavePropertiesToSourceData(sourceData, [](const AtomToolsFramework::DynamicProperty& property) { + const bool savedProperties = SavePropertiesToSourceData(normalizedSavePath, sourceData, [](const AtomToolsFramework::DynamicProperty& property) + { return !AtomToolsFramework::ArePropertyValuesEqual(property.GetValue(), property.GetConfig().m_originalValue); }); @@ -590,7 +575,8 @@ namespace MaterialEditor } } - bool MaterialDocument::SavePropertiesToSourceData(AZ::RPI::MaterialSourceData& sourceData, PropertyFilterFunction propertyFilter) const + bool MaterialDocument::SavePropertiesToSourceData( + const AZStd::string& exportPath, AZ::RPI::MaterialSourceData& sourceData, PropertyFilterFunction propertyFilter) const { using namespace AZ; using namespace RPI; @@ -598,7 +584,7 @@ namespace MaterialEditor bool result = true; // populate sourceData with properties that meet the filter - m_materialTypeSourceData.EnumerateProperties([this, &sourceData, &propertyFilter, &result](const AZStd::string& groupName, const AZStd::string& propertyName, const auto& propertyDefinition) { + m_materialTypeSourceData.EnumerateProperties([&](const AZStd::string& groupName, const AZStd::string& propertyName, const auto& propertyDefinition) { const MaterialPropertyId propertyId(groupName, propertyName); @@ -608,7 +594,7 @@ namespace MaterialEditor MaterialPropertyValue propertyValue = AtomToolsFramework::ConvertToRuntimeType(it->second.GetValue()); if (propertyValue.IsValid()) { - if (!m_materialTypeSourceData.ConvertPropertyValueToSourceDataFormat(propertyDefinition, propertyValue)) + if (!AtomToolsFramework::ConvertToExportFormat(exportPath, propertyDefinition, propertyValue)) { AZ_Error("MaterialDocument", false, "Material document property could not be converted: '%s' in '%s'.", propertyId.GetFullName().GetCStr(), m_absolutePath.c_str()); result = false; @@ -662,8 +648,6 @@ namespace MaterialEditor return false; } - AZStd::string materialTypeSourceFilePath; - // The material document and inspector are constructed from source data if (AzFramework::StringFunc::Path::IsExtension(m_absolutePath.c_str(), MaterialSourceData::Extension)) { @@ -674,13 +658,24 @@ namespace MaterialEditor return false; } - // We must also always load the material type data for a complete, ordered set of the - // groups and properties that will be needed for comparison and building the inspector - materialTypeSourceFilePath = AssetUtils::ResolvePathReference(m_absolutePath, m_materialSourceData.m_materialType); - auto materialTypeOutcome = MaterialUtils::LoadMaterialTypeSourceData(materialTypeSourceFilePath); + // We always need the absolute path for the material type and parent material to load source data and resolving + // relative paths when saving. This will convert and store them as absolute paths for use within the document. + if (!m_materialSourceData.m_parentMaterial.empty()) + { + m_materialSourceData.m_parentMaterial = + AssetUtils::ResolvePathReference(m_absolutePath, m_materialSourceData.m_parentMaterial); + } + + if (!m_materialSourceData.m_materialType.empty()) + { + m_materialSourceData.m_materialType = AssetUtils::ResolvePathReference(m_absolutePath, m_materialSourceData.m_materialType); + } + + // Load the material type source data which provides the layout and default values of all of the properties + auto materialTypeOutcome = MaterialUtils::LoadMaterialTypeSourceData(m_materialSourceData.m_materialType); if (!materialTypeOutcome.IsSuccess()) { - AZ_Error("MaterialDocument", false, "Material type source data could not be loaded: '%s'.", materialTypeSourceFilePath.c_str()); + AZ_Error("MaterialDocument", false, "Material type source data could not be loaded: '%s'.", m_materialSourceData.m_materialType.c_str()); return false; } m_materialTypeSourceData = materialTypeOutcome.GetValue(); @@ -693,10 +688,10 @@ namespace MaterialEditor } else if (AzFramework::StringFunc::Path::IsExtension(m_absolutePath.c_str(), MaterialTypeSourceData::Extension)) { - materialTypeSourceFilePath = m_absolutePath; - - // Load the material type source data, which will be used for enumerating properties and building material source data - auto materialTypeOutcome = MaterialUtils::LoadMaterialTypeSourceData(materialTypeSourceFilePath); + // A material document can be created or loaded from material or material type source data. If we are attempting to load + // material type source data then the material source data object can be created just by referencing the document path as the + // material type path. + auto materialTypeOutcome = MaterialUtils::LoadMaterialTypeSourceData(m_absolutePath); if (!materialTypeOutcome.IsSuccess()) { AZ_Error("MaterialDocument", false, "Material type source data could not be loaded: '%s'.", m_absolutePath.c_str()); @@ -704,9 +699,8 @@ namespace MaterialEditor } m_materialTypeSourceData = materialTypeOutcome.GetValue(); - // The document represents a material, not a material type. - // If the input data is a material type file we have to generate the material source data by referencing it. - m_materialSourceData.m_materialType = m_relativePath; + // We are storing absolute paths in the loaded version of the source data so that the files can be resolved at all times. + m_materialSourceData.m_materialType = m_absolutePath; m_materialSourceData.m_parentMaterial.clear(); } else @@ -748,25 +742,24 @@ namespace MaterialEditor if (!m_materialSourceData.m_parentMaterial.empty()) { AZ::RPI::MaterialSourceData parentMaterialSourceData; - const auto parentMaterialFilePath = AssetUtils::ResolvePathReference(m_absolutePath, m_materialSourceData.m_parentMaterial); - if (!AZ::RPI::JsonUtils::LoadObjectFromFile(parentMaterialFilePath, parentMaterialSourceData)) + if (!AZ::RPI::JsonUtils::LoadObjectFromFile(m_materialSourceData.m_parentMaterial, parentMaterialSourceData)) { - AZ_Error("MaterialDocument", false, "Material parent source data could not be loaded for: '%s'.", parentMaterialFilePath.c_str()); + AZ_Error("MaterialDocument", false, "Material parent source data could not be loaded for: '%s'.", m_materialSourceData.m_parentMaterial.c_str()); return false; } - const auto parentMaterialAssetIdResult = AssetUtils::MakeAssetId(parentMaterialFilePath, 0); + const auto parentMaterialAssetIdResult = AssetUtils::MakeAssetId(m_materialSourceData.m_parentMaterial, 0); if (!parentMaterialAssetIdResult) { - AZ_Error("MaterialDocument", false, "Material parent asset ID could not be created: '%s'.", parentMaterialFilePath.c_str()); + AZ_Error("MaterialDocument", false, "Material parent asset ID could not be created: '%s'.", m_materialSourceData.m_parentMaterial.c_str()); return false; } auto parentMaterialAssetResult = parentMaterialSourceData.CreateMaterialAssetFromSourceData( - parentMaterialAssetIdResult.GetValue(), parentMaterialFilePath, true, true); + parentMaterialAssetIdResult.GetValue(), m_materialSourceData.m_parentMaterial, true, true); if (!parentMaterialAssetResult) { - AZ_Error("MaterialDocument", false, "Material parent asset could not be created from source data: '%s'.", parentMaterialFilePath.c_str()); + AZ_Error("MaterialDocument", false, "Material parent asset could not be created from source data: '%s'.", m_materialSourceData.m_parentMaterial.c_str()); return false; } @@ -877,7 +870,8 @@ namespace MaterialEditor m_properties[propertyConfig.m_id] = AtomToolsFramework::DynamicProperty(propertyConfig); } - const MaterialFunctorSourceData::EditorContext editorContext = MaterialFunctorSourceData::EditorContext(materialTypeSourceFilePath, m_materialAsset->GetMaterialPropertiesLayout()); + const MaterialFunctorSourceData::EditorContext editorContext = + MaterialFunctorSourceData::EditorContext(m_materialSourceData.m_materialType, m_materialAsset->GetMaterialPropertiesLayout()); for (Ptr functorData : m_materialTypeSourceData.m_materialFunctorSourceData) { MaterialFunctorSourceData::FunctorResult result2 = functorData->CreateFunctor(editorContext); diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.h b/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.h index 452111f99a..ceb3190f26 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.h +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.h @@ -104,7 +104,8 @@ namespace MaterialEditor void SourceFileChanged(AZStd::string relativePath, AZStd::string scanFolder, AZ::Uuid sourceUUID) override; ////////////////////////////////////////////////////////////////////////// - bool SavePropertiesToSourceData(AZ::RPI::MaterialSourceData& sourceData, PropertyFilterFunction propertyFilter) const; + bool SavePropertiesToSourceData( + const AZStd::string& exportPath, AZ::RPI::MaterialSourceData& sourceData, PropertyFilterFunction propertyFilter) const; bool OpenInternal(AZStd::string_view loadPath); diff --git a/Gems/Atom/Utils/Code/CMakeLists.txt b/Gems/Atom/Utils/Code/CMakeLists.txt index 89bc8dd0a5..90d7ce501b 100644 --- a/Gems/Atom/Utils/Code/CMakeLists.txt +++ b/Gems/Atom/Utils/Code/CMakeLists.txt @@ -27,6 +27,27 @@ ly_add_target( 3rdParty::libpng ) +if(PAL_TRAIT_BUILD_HOST_TOOLS) + + ly_add_target( + NAME Atom_Utils.TestUtils.Static STATIC + NAMESPACE Gem + FILES_CMAKE + atom_utils_editor_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Source + PUBLIC + Include + BUILD_DEPENDENCIES + PRIVATE + AZ::AtomCore + AZ::AzCore + AZ::AzFramework + AZ::AzToolsFramework + ) +endif() + ################################################################################ # Tests ################################################################################ diff --git a/Gems/Atom/RPI/Code/Tests/Common/AssetSystemStub.h b/Gems/Atom/Utils/Code/Include/Atom/Utils/TestUtils/AssetSystemStub.h similarity index 100% rename from Gems/Atom/RPI/Code/Tests/Common/AssetSystemStub.h rename to Gems/Atom/Utils/Code/Include/Atom/Utils/TestUtils/AssetSystemStub.h diff --git a/Gems/Atom/RPI/Code/Tests/Common/AssetSystemStub.cpp b/Gems/Atom/Utils/Code/Source/TestUtils/AssetSystemStub.cpp similarity index 98% rename from Gems/Atom/RPI/Code/Tests/Common/AssetSystemStub.cpp rename to Gems/Atom/Utils/Code/Source/TestUtils/AssetSystemStub.cpp index 31a46e9a6f..d57969ea9c 100644 --- a/Gems/Atom/RPI/Code/Tests/Common/AssetSystemStub.cpp +++ b/Gems/Atom/Utils/Code/Source/TestUtils/AssetSystemStub.cpp @@ -6,7 +6,7 @@ * */ -#include +#include #include namespace UnitTest diff --git a/Gems/Atom/Utils/Code/atom_utils_editor_files.cmake b/Gems/Atom/Utils/Code/atom_utils_editor_files.cmake new file mode 100644 index 0000000000..6c4202fe09 --- /dev/null +++ b/Gems/Atom/Utils/Code/atom_utils_editor_files.cmake @@ -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 +# +# + +set(FILES + Include/Atom/Utils/TestUtils/AssetSystemStub.h + Source/TestUtils/AssetSystemStub.cpp +) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentUtil.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentUtil.cpp index d15db886d6..0fd7b7164d 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentUtil.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentUtil.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -97,51 +98,28 @@ namespace AZ bool SaveSourceMaterialFromEditData(const AZStd::string& path, const MaterialEditData& editData) { - // Construct the material source data object that will be exported - AZ::RPI::MaterialSourceData exportData; - - // Converting absolute material paths to relative paths - bool result = false; - AZ::Data::AssetInfo info; - AZStd::string watchFolder; - AzToolsFramework::AssetSystemRequestBus::BroadcastResult( - result, &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath, - editData.m_materialTypeSourcePath.c_str(), info, watchFolder); - if (!result) + if (path.empty() || !editData.m_materialAsset.IsReady() || !editData.m_materialTypeAsset.IsReady() || + editData.m_materialTypeSourcePath.empty()) { - AZ_Error( - "AZ::Render::EditorMaterialComponentUtil", false, - "Failed to get material type source file info while attempting to export: %s", path.c_str()); + AZ_Error("AZ::Render::EditorMaterialComponentUtil", false, "Can not export: %s", path.c_str()); return false; } - exportData.m_materialType = info.m_relativePath; - - if (!editData.m_materialParentSourcePath.empty()) - { - result = false; - AzToolsFramework::AssetSystemRequestBus::BroadcastResult( - result, &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath, - editData.m_materialParentSourcePath.c_str(), info, watchFolder); - if (!result) - { - AZ_Error( - "AZ::Render::EditorMaterialComponentUtil", false, - "Failed to get parent material source file info while attempting to export: %s", path.c_str()); - return false; - } - - exportData.m_parentMaterial = info.m_relativePath; - } + // Construct the material source data object that will be exported + AZ::RPI::MaterialSourceData exportData; + exportData.m_materialTypeVersion = editData.m_materialTypeAsset->GetVersion(); + exportData.m_materialType = AtomToolsFramework::GetExteralReferencePath(path, editData.m_materialTypeSourcePath); + exportData.m_parentMaterial = AtomToolsFramework::GetExteralReferencePath(path, editData.m_materialParentSourcePath); // Copy all of the properties from the material asset to the source data that will be exported - result = true; - editData.m_materialTypeSourceData.EnumerateProperties([&](const AZStd::string& groupName, const AZStd::string& propertyName, const auto& propertyDefinition) { + bool result = true; + editData.m_materialTypeSourceData.EnumerateProperties([&](const AZStd::string& groupName, const AZStd::string& propertyName, const auto& propertyDefinition){ const AZ::RPI::MaterialPropertyId propertyId(groupName, propertyName); const AZ::RPI::MaterialPropertyIndex propertyIndex = editData.m_materialAsset->GetMaterialPropertiesLayout()->FindPropertyIndex(propertyId.GetFullName()); - AZ::RPI::MaterialPropertyValue propertyValue = editData.m_materialAsset->GetPropertyValues()[propertyIndex.GetIndex()]; + AZ::RPI::MaterialPropertyValue propertyValue = + editData.m_materialAsset->GetPropertyValues()[propertyIndex.GetIndex()]; AZ::RPI::MaterialPropertyValue propertyValueDefault = propertyDefinition.m_value; if (editData.m_materialParentAsset.IsReady()) @@ -151,12 +129,12 @@ namespace AZ // Check for and apply any property overrides before saving property values auto propertyOverrideItr = editData.m_materialPropertyOverrideMap.find(propertyId.GetFullName()); - if(propertyOverrideItr != editData.m_materialPropertyOverrideMap.end()) + if (propertyOverrideItr != editData.m_materialPropertyOverrideMap.end()) { propertyValue = AZ::RPI::MaterialPropertyValue::FromAny(propertyOverrideItr->second); } - if (!editData.m_materialTypeSourceData.ConvertPropertyValueToSourceDataFormat(propertyDefinition, propertyValue)) + if (!AtomToolsFramework::ConvertToExportFormat(path, propertyDefinition, propertyValue)) { AZ_Error("AZ::Render::EditorMaterialComponentUtil", false, "Failed to export: %s", path.c_str()); result = false; diff --git a/Gems/StartingPointCamera/Code/Source/CameraLookAtBehaviors/SlideAlongAxisBasedOnAngle.cpp b/Gems/StartingPointCamera/Code/Source/CameraLookAtBehaviors/SlideAlongAxisBasedOnAngle.cpp index 5031f149b8..1d8d6adf6c 100644 --- a/Gems/StartingPointCamera/Code/Source/CameraLookAtBehaviors/SlideAlongAxisBasedOnAngle.cpp +++ b/Gems/StartingPointCamera/Code/Source/CameraLookAtBehaviors/SlideAlongAxisBasedOnAngle.cpp @@ -9,8 +9,8 @@ #include "SlideAlongAxisBasedOnAngle.h" #include "StartingPointCamera/StartingPointCameraUtilities.h" #include -#include #include +#include namespace Camera { @@ -32,31 +32,43 @@ namespace Camera AZ::EditContext* editContext = serializeContext->GetEditContext(); if (editContext) { - editContext->Class("SlideAlongAxisBasedOnAngle", "Slide 0..SlideDistance along Axis based on Angle Type. Maps from 90..-90 degrees") - ->ClassElement(AZ::Edit::ClassElements::EditorData, "") - ->DataElement(AZ::Edit::UIHandlers::ComboBox, &SlideAlongAxisBasedOnAngle::m_axisToSlideAlong, "Axis to slide along", "The Axis to slide along") - ->EnumAttribute(RelativeAxisType::ForwardBackward, "Forwards and Backwards") - ->EnumAttribute(RelativeAxisType::LeftRight, "Right and Left") - ->EnumAttribute(RelativeAxisType::UpDown, "Up and Down") - ->DataElement(AZ::Edit::UIHandlers::ComboBox, &SlideAlongAxisBasedOnAngle::m_angleTypeToChangeFor, "Angle Type", "The angle type to base the slide off of") - ->EnumAttribute(EulerAngleType::Pitch, "Pitch") - ->EnumAttribute(EulerAngleType::Roll, "Roll") - ->EnumAttribute(EulerAngleType::Yaw, "Yaw") - ->DataElement(0, &SlideAlongAxisBasedOnAngle::m_maximumPositiveSlideDistance, "Max Positive Slide Distance", "The maximum distance to slide in the positive") - ->Attribute(AZ::Edit::Attributes::Suffix, "m") - ->DataElement(0, &SlideAlongAxisBasedOnAngle::m_maximumNegativeSlideDistance, "Max Negative Slide Distance", "The maximum distance to slide in the negative") - ->Attribute(AZ::Edit::Attributes::Suffix, "m") - ->ClassElement(AZ::Edit::ClassElements::Group, "Vector Components To Ignore") - ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->DataElement(0, &SlideAlongAxisBasedOnAngle::m_ignoreX, "X", "When active, the X Component will be ignored.") - ->DataElement(0, &SlideAlongAxisBasedOnAngle::m_ignoreY, "Y", "When active, the Y Component will be ignored.") - ->DataElement(0, &SlideAlongAxisBasedOnAngle::m_ignoreZ, "Z", "When active, the Z Component will be ignored.") + editContext->Class("SlideAlongAxisBasedOnAngle", + "Slide 0..SlideDistance along Axis based on Angle Type. Maps from 90..-90 degrees") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->DataElement(AZ::Edit::UIHandlers::ComboBox, &SlideAlongAxisBasedOnAngle::m_axisToSlideAlong, "Axis to slide along", + "The Axis to slide along") + ->EnumAttribute(RelativeAxisType::ForwardBackward, "Forwards and Backwards") + ->EnumAttribute(RelativeAxisType::LeftRight, "Right and Left") + ->EnumAttribute(RelativeAxisType::UpDown, "Up and Down") + ->DataElement(AZ::Edit::UIHandlers::ComboBox, &SlideAlongAxisBasedOnAngle::m_angleTypeToChangeFor, "Angle Type", + "The angle type to base the slide off of") + ->EnumAttribute(EulerAngleType::Pitch, "Pitch") + ->EnumAttribute(EulerAngleType::Roll, "Roll") + ->EnumAttribute(EulerAngleType::Yaw, "Yaw") + ->DataElement(0, &SlideAlongAxisBasedOnAngle::m_maximumPositiveSlideDistance, "Max Positive Slide Distance", + "The maximum distance to slide in the positive") + ->Attribute(AZ::Edit::Attributes::Suffix, "m") + ->DataElement(0, &SlideAlongAxisBasedOnAngle::m_maximumNegativeSlideDistance, "Max Negative Slide Distance", + "The maximum distance to slide in the negative") + ->Attribute(AZ::Edit::Attributes::Suffix, "m") + ->ClassElement(AZ::Edit::ClassElements::Group, "Vector Components To Ignore") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(0, &SlideAlongAxisBasedOnAngle::m_ignoreX, "X", "When active, the X Component will be ignored.") + ->Attribute(AZ::Edit::Attributes::ReadOnly, &SlideAlongAxisBasedOnAngle::YAndZIgnored) + ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::AttributesAndValues) + ->DataElement(0, &SlideAlongAxisBasedOnAngle::m_ignoreY, "Y", "When active, the Y Component will be ignored.") + ->Attribute(AZ::Edit::Attributes::ReadOnly, &SlideAlongAxisBasedOnAngle::XAndZIgnored) + ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::AttributesAndValues) + ->DataElement(0, &SlideAlongAxisBasedOnAngle::m_ignoreZ, "Z", "When active, the Z Component will be ignored.") + ->Attribute(AZ::Edit::Attributes::ReadOnly, &SlideAlongAxisBasedOnAngle::XAndYIgnored) + ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::AttributesAndValues) ; } } } - void SlideAlongAxisBasedOnAngle::AdjustLookAtTarget([[maybe_unused]] float deltaTime, [[maybe_unused]] const AZ::Transform& targetTransform, AZ::Transform& outLookAtTargetTransform) + void SlideAlongAxisBasedOnAngle::AdjustLookAtTarget( + [[maybe_unused]] float deltaTime, [[maybe_unused]] const AZ::Transform& targetTransform, AZ::Transform& outLookAtTargetTransform) { float angle = GetEulerAngleFromTransform(outLookAtTargetTransform, m_angleTypeToChangeFor); float currentPositionOnRange = -angle / AZ::Constants::HalfPi; @@ -67,4 +79,20 @@ namespace Camera outLookAtTargetTransform.SetTranslation(outLookAtTargetTransform.GetTranslation() + basis * currentPositionOnRange * slideScale); } -} + + bool SlideAlongAxisBasedOnAngle::XAndYIgnored() const + { + return m_ignoreX && m_ignoreY; + } + + bool SlideAlongAxisBasedOnAngle::XAndZIgnored() const + { + return m_ignoreX && m_ignoreZ; + } + + bool SlideAlongAxisBasedOnAngle::YAndZIgnored() const + { + return m_ignoreY && m_ignoreZ; + } + +} // namespace Camera diff --git a/Gems/StartingPointCamera/Code/Source/CameraLookAtBehaviors/SlideAlongAxisBasedOnAngle.h b/Gems/StartingPointCamera/Code/Source/CameraLookAtBehaviors/SlideAlongAxisBasedOnAngle.h index 2c756d1ac7..e517b6ffbd 100644 --- a/Gems/StartingPointCamera/Code/Source/CameraLookAtBehaviors/SlideAlongAxisBasedOnAngle.h +++ b/Gems/StartingPointCamera/Code/Source/CameraLookAtBehaviors/SlideAlongAxisBasedOnAngle.h @@ -6,11 +6,11 @@ * */ #pragma once -#include -#include -#include #include "StartingPointCamera/StartingPointCameraConstants.h" +#include #include +#include +#include namespace Camera { @@ -38,6 +38,10 @@ namespace Camera void Activate(AZ::EntityId) override {} void Deactivate() override {} + bool XAndYIgnored() const; + bool XAndZIgnored() const; + bool YAndZIgnored() const; + private: ////////////////////////////////////////////////////////////////////////// // Reflected data