Browse Source

Auto generated mesh groups are now marked read only (#13701)

* [WIP] Mesh groups for default prefabs should be read-only in the Scene Settings UI

Signed-off-by: AMZN-stankowi <[email protected]>

* Adding missing header file

Signed-off-by: AMZN-stankowi <[email protected]>

* First pass at disabling read only groups

Signed-off-by: AMZN-stankowi <[email protected]>

* Fixed delete buttons not working when disabled

Signed-off-by: AMZN-stankowi <[email protected]>

* Read only status mostly works now

Signed-off-by: AMZN-stankowi <[email protected]>

* Tooltips for groups

Signed-off-by: AMZN-stankowi <[email protected]>

* Fixed mesh SVG to not have a built in background, so it will enter the disabled state correctly.
Removed unused printf.
Fixed prefab group divider.

Signed-off-by: AMZN-stankowi <[email protected]>

* Fixed typo in comment

Signed-off-by: AMZN-stankowi <[email protected]>

* Reverting changes not needed

Signed-off-by: AMZN-stankowi <[email protected]>

* Removed include no longer used

Signed-off-by: AMZN-stankowi <[email protected]>

* Fixed linking and reflection issue with new read only rule

Signed-off-by: AMZN-stankowi <[email protected]>

* Automated test for new read only rule

Signed-off-by: AMZN-stankowi <[email protected]>

* PR feedback: Renamed ReadOnly to Unmodifiable for the rule. Moved pragma to after legal header. Updated tooltip to describe how to remove the groups (doesn't actually work yet)

Signed-off-by: AMZN-stankowi <[email protected]>

* Removing a prefab group removes the mesh groups it added

Signed-off-by: AMZN-stankowi <[email protected]>

* Prefab group removal automated test

Signed-off-by: AMZN-stankowi <[email protected]>

Signed-off-by: AMZN-stankowi <[email protected]>
AMZN-stankowi 2 years ago
parent
commit
1c9eef8cf8
42 changed files with 525 additions and 67 deletions
  1. 0 3
      Assets/Editor/Icons/AssetImporter/Mesh.svg
  2. 26 0
      AutomatedTesting/Gem/PythonTests/assetpipeline/scene_settings_tests/scene_settings_tests.py
  3. 1 1
      AutomatedTesting/Gem/PythonTests/assetpipeline/scene_settings_tests/tests/scene_settings_manifest_vector_widget_tests_in_editor.py
  4. 45 0
      AutomatedTesting/Gem/PythonTests/assetpipeline/scene_settings_tests/tests/scene_settings_readonly_rule_test.py
  5. 101 0
      AutomatedTesting/Gem/PythonTests/assetpipeline/scene_settings_tests/tests/scene_settings_remove_prefab_removes_mesh_groups.py
  6. 17 16
      AutomatedTesting/Gem/PythonTests/assetpipeline/scene_settings_tests/tests/scene_settings_test_helpers.py
  7. 44 5
      AutomatedTesting/Gem/PythonTests/assetpipeline/scene_settings_tests/tests/scene_settings_test_messages.py
  8. 3 4
      AutomatedTesting/Gem/PythonTests/assetpipeline/scene_settings_tests/tests/scene_settings_tests_in_editor.py
  9. 9 0
      Code/Tools/SceneAPI/SceneCore/DataTypes/IManifestObject.h
  10. 1 2
      Code/Tools/SceneAPI/SceneCore/DataTypes/Rules/IBlendShapeRule.h
  11. 1 2
      Code/Tools/SceneAPI/SceneCore/DataTypes/Rules/ICommentRule.h
  12. 1 2
      Code/Tools/SceneAPI/SceneCore/DataTypes/Rules/ILodRule.h
  13. 1 2
      Code/Tools/SceneAPI/SceneCore/DataTypes/Rules/IMaterialRule.h
  14. 1 2
      Code/Tools/SceneAPI/SceneCore/DataTypes/Rules/IMeshAdvancedRule.h
  15. 3 2
      Code/Tools/SceneAPI/SceneCore/DataTypes/Rules/IRule.h
  16. 1 2
      Code/Tools/SceneAPI/SceneCore/DataTypes/Rules/ISkeletonProxyRule.h
  17. 1 2
      Code/Tools/SceneAPI/SceneCore/DataTypes/Rules/ISkinRule.h
  18. 28 0
      Code/Tools/SceneAPI/SceneCore/DataTypes/Rules/IUnmodifiableRule.h
  19. 2 0
      Code/Tools/SceneAPI/SceneCore/DllMain.cpp
  20. 1 0
      Code/Tools/SceneAPI/SceneCore/scenecore_files.cmake
  21. 2 0
      Code/Tools/SceneAPI/SceneData/ManifestMetaInfoHandler.cpp
  22. 2 0
      Code/Tools/SceneAPI/SceneData/ReflectionRegistrar.cpp
  23. 1 2
      Code/Tools/SceneAPI/SceneData/Rules/BlendShapeRule.h
  24. 1 2
      Code/Tools/SceneAPI/SceneData/Rules/CommentRule.h
  25. 1 2
      Code/Tools/SceneAPI/SceneData/Rules/LodRule.h
  26. 1 2
      Code/Tools/SceneAPI/SceneData/Rules/MaterialRule.h
  27. 1 2
      Code/Tools/SceneAPI/SceneData/Rules/SkeletonProxyRule.h
  28. 1 2
      Code/Tools/SceneAPI/SceneData/Rules/SkinMeshAdvancedRule.h
  29. 1 2
      Code/Tools/SceneAPI/SceneData/Rules/SkinRule.h
  30. 1 2
      Code/Tools/SceneAPI/SceneData/Rules/StaticMeshAdvancedRule.h
  31. 47 0
      Code/Tools/SceneAPI/SceneData/Rules/UnmodifiableRule.cpp
  32. 37 0
      Code/Tools/SceneAPI/SceneData/Rules/UnmodifiableRule.h
  33. 2 0
      Code/Tools/SceneAPI/SceneData/SceneData_files.cmake
  34. 12 0
      Code/Tools/SceneAPI/SceneUI/RowWidgets/HeaderHandler.cpp
  35. 2 0
      Code/Tools/SceneAPI/SceneUI/RowWidgets/HeaderHandler.h
  36. 59 5
      Code/Tools/SceneAPI/SceneUI/RowWidgets/HeaderWidget.cpp
  37. 2 1
      Code/Tools/SceneAPI/SceneUI/RowWidgets/HeaderWidget.h
  38. 24 0
      Code/Tools/SceneAPI/SceneUI/SceneWidgets/ManifestWidgetPage.cpp
  39. 2 0
      Code/Tools/SceneAPI/SceneUI/SceneWidgets/ManifestWidgetPage.h
  40. 4 0
      Gems/Prefab/PrefabBuilder/PrefabGroup/DefaultProceduralPrefab.cpp
  41. 29 0
      Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroup.cpp
  42. 6 0
      Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroup.h

File diff suppressed because it is too large
+ 0 - 3
Assets/Editor/Icons/AssetImporter/Mesh.svg


+ 26 - 0
AutomatedTesting/Gem/PythonTests/assetpipeline/scene_settings_tests/scene_settings_tests.py

@@ -48,6 +48,32 @@ class TestAutomation(EditorTestSuite):
 
 
         from .tests import scene_settings_tests_in_editor as test_module
         from .tests import scene_settings_tests_in_editor as test_module
 
 
+        
+    class scene_settings_tests_readonly_rule(EditorSingleTest):
+        @classmethod
+        def setup(self, instance, request, workspace):
+            self.test_file_names = ["auto_test_fbx.fbx"]
+            setup_test_files(workspace, self.test_file_names)
+
+        @classmethod
+        def teardown(self, instance, request, workspace, editor_test_results):
+            cleanup_test_files(workspace, self.test_file_names)
+
+        from .tests import scene_settings_readonly_rule_test as test_module
+        
+
+    class scene_settings_remove_prefab_removes_mesh_groups(EditorSingleTest):
+        @classmethod
+        def setup(self, instance, request, workspace):
+            self.test_file_names = ["auto_test_fbx.fbx"]
+            setup_test_files(workspace, self.test_file_names)
+
+        @classmethod
+        def teardown(self, instance, request, workspace, editor_test_results):
+            cleanup_test_files(workspace, self.test_file_names)
+
+        from .tests import scene_settings_remove_prefab_removes_mesh_groups as test_module
+
 
 
     class scene_settings_manifest_vector_widget_tests_in_editor(EditorSingleTest):
     class scene_settings_manifest_vector_widget_tests_in_editor(EditorSingleTest):
         @classmethod
         @classmethod

+ 1 - 1
AutomatedTesting/Gem/PythonTests/assetpipeline/scene_settings_tests/tests/scene_settings_manifest_vector_widget_tests_in_editor.py

@@ -22,7 +22,7 @@ def Scene_Settings_Tests_In_Editor_Change_Manifest_Vector_Widget_Child_Marks_Fil
         path_to_manifest, widget_main_window, reflected_property_root = \
         path_to_manifest, widget_main_window, reflected_property_root = \
             scene_test_helpers.prepare_scene_ui_for_test( \
             scene_test_helpers.prepare_scene_ui_for_test( \
                 test_file_name="Jack_Death_Fall_Back_ZUp.fbx", \
                 test_file_name="Jack_Death_Fall_Back_ZUp.fbx", \
-                manifest_should_exist=True)
+                manifest_should_exist=True, should_create_manifest=True)
         
         
         # Find a toggle that used to not properly mark the UI as dirty because it was parented to a manifest vector widget.
         # Find a toggle that used to not properly mark the UI as dirty because it was parented to a manifest vector widget.
         y_axis = reflected_property_root.findChild(QtWidgets.QWidget,"Ignore Y-Axis transition")
         y_axis = reflected_property_root.findChild(QtWidgets.QWidget,"Ignore Y-Axis transition")

+ 45 - 0
AutomatedTesting/Gem/PythonTests/assetpipeline/scene_settings_tests/tests/scene_settings_readonly_rule_test.py

@@ -0,0 +1,45 @@
+"""
+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
+"""
+    
+def Scene_Settings_Tests_In_Editor_ReadOnly_Rule_Works():
+    import pyside_utils
+
+    @pyside_utils.wrap_async
+    async def run_test():
+        import asyncio
+        from editor_python_test_tools.utils import Report
+        import PySide2
+        from PySide2 import QtWidgets
+        import azlmbr.bus as bus
+        import azlmbr.legacy.general as general
+        import scene_settings_test_messages as tm
+        import scene_settings_test_helpers as scene_test_helpers
+        
+        path_to_manifest, widget_main_window, reflected_property_root = \
+            scene_test_helpers.prepare_scene_ui_for_test(test_file_name="auto_test_fbx.fbx", manifest_should_exist=False, should_create_manifest=False)
+
+        # The default prefab adds mesh groups with coordinate rules, that have advanced settings.
+        # Find that toggle.
+        use_advanced_settings_row = reflected_property_root.findChild(QtWidgets.QWidget,"Use Advanced Settings")
+        Report.critical_result(tm.Test_Messages.scene_settings_found_advanced_settings_row, use_advanced_settings_row is not None)
+
+        check_boxes = use_advanced_settings_row.findChildren(QtWidgets.QCheckBox,"")
+        Report.critical_result(tm.Test_Messages.scene_settings_found_expected_interface_checkbox, check_boxes is not None)
+        Report.critical_result(tm.Test_Messages.scene_settings_found_only_one_checkbox, len(check_boxes) == 1)
+
+        use_advanced_settings_checkbox = check_boxes[0]
+        
+        # Verify that the toggle is already checked, and is disabled, meaning it is read only.
+        Report.critical_result(tm.Test_Messages.scene_settings_read_only_checked, use_advanced_settings_checkbox.isChecked())
+        Report.critical_result(tm.Test_Messages.scene_settings_read_only_disabled, not use_advanced_settings_checkbox.isEnabled())
+        widget_main_window.close()
+
+    run_test()
+
+if __name__ == "__main__":
+    from editor_python_test_tools.utils import Report
+    Report.start_test(Scene_Settings_Tests_In_Editor_ReadOnly_Rule_Works)

+ 101 - 0
AutomatedTesting/Gem/PythonTests/assetpipeline/scene_settings_tests/tests/scene_settings_remove_prefab_removes_mesh_groups.py

@@ -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
+"""
+    
+def Scene_Settings_Tests_Remove_Prefab_Removes_Mesh_Groups():
+    import pyside_utils
+
+    @pyside_utils.wrap_async
+    async def run_test():
+        import asyncio
+        from editor_python_test_tools.utils import Report
+        import PySide2
+        from PySide2 import QtWidgets
+        import azlmbr.bus as bus
+        import azlmbr.legacy.general as general
+        import ly_test_tools.o3de.pipeline_utils as utils
+        import scene_settings_test_messages as tm
+        import scene_settings_test_helpers as scene_test_helpers
+        general.idle_enable(True)
+        
+        path_to_manifest, widget_main_window, reflected_property_root = \
+            scene_test_helpers.prepare_scene_ui_for_test(test_file_name="auto_test_fbx.fbx", manifest_should_exist=False, should_create_manifest=False)
+
+        # First, make sure the mesh groups added by the procedural prefab exists
+        name_mesh_labels = widget_main_window.findChildren(QtWidgets.QFrame, "Name mesh")
+
+        editable_mesh_group_name = "auto_test_fbx"
+        # The mesh groups generated should be the same every time, otherwise references would break
+        # when a scene manifest file didn't exist yet.
+        expected_mesh_names = [
+            editable_mesh_group_name,
+            "default_auto_test_fbx_F0FEDC3F_1BDC_546F_93A9_8DD78321B7B0_",
+            "default_auto_test_fbx_2076A10D_F9B1_5F4F_8100_DCEF0AFFB901_",
+            "default_auto_test_fbx_C93B7FD9_93B8_5FBD_864E_B63FCBE1803A_"
+        ]
+
+        found_mesh_names = []
+
+        for name_mesh_label in name_mesh_labels:
+            mesh_name_line_edit = name_mesh_label.findChild(QtWidgets.QLineEdit, "")
+            mesh_name = mesh_name_line_edit.text()
+            found_mesh_names.append(mesh_name)
+            if mesh_name == editable_mesh_group_name:
+                Report.critical_result(tm.Test_Messages.scene_settings_base_mesh_group_enabled, mesh_name_line_edit.isEnabled())
+            else:
+                # All other mesh groups should be read-only
+                Report.critical_result(tm.Test_Messages.scene_settings_prefab_mesh_group_disabled, not mesh_name_line_edit.isEnabled())
+
+        expected_diff, found_diff = utils.get_differences_between_lists(expected_mesh_names, found_mesh_names)
+
+        Report.critical_result(tm.Test_Messages.scene_settings_expected_mesh_groups_found, len(expected_diff) == 0 and len(found_diff) == 0)
+        
+        # Next, go to the prefab tab
+        tab_bar = widget_main_window.findChild(QtWidgets.QTabBar,"")
+        PREFAB_TAB_INDEX = 3
+        tab_bar.setCurrentIndex(PREFAB_TAB_INDEX)
+
+        # Find the default prefab group and delete it
+        ui_headers = widget_main_window.findChildren(QtWidgets.QWidget, "AZ__SceneAPI__UI__HeaderWidget")
+        for ui_header in ui_headers:
+            name_label = ui_header.findChild(QtWidgets.QLabel, "m_nameLabel")
+            if name_label.text() == "Prefab group":
+                delete_button = ui_header.findChild(QtWidgets.QToolButton, "m_deleteButton")
+                delete_button.click()
+                break
+                
+        # Go back to the first tab
+        MESH_TAB_INDEX = 0
+        tab_bar.setCurrentIndex(MESH_TAB_INDEX)
+
+        # Briefly pause so all events get posted
+        general.idle_wait_frames(30)
+        
+        # Verify the groups are gone now
+        
+        # Verify the prefab created groups are gone now and the remaining mesh group is the expected non-prefab one.
+        # Qt keeps objects around and can throw off searches. To work around that, examine the contents of the labels directly.        
+        new_found_mesh_names = []
+        name_mesh_labels = widget_main_window.findChildren(QtWidgets.QFrame, "Name mesh")
+        for name_mesh_label in name_mesh_labels:
+            mesh_name_line_edit = name_mesh_label.findChild(QtWidgets.QLineEdit, "")
+            if mesh_name_line_edit != None:
+                new_found_mesh_names.append(mesh_name_line_edit.text())
+                Report.critical_result(tm.Test_Messages.scene_settings_base_mesh_group_enabled, mesh_name_line_edit.isEnabled())
+
+        Report.critical_result(("", f"new: {new_found_mesh_names}"), len(new_found_mesh_names) == 1)
+        Report.critical_result(tm.Test_Messages.scene_settings_expected_mesh_groups_removed, len(new_found_mesh_names) == 1)
+
+        Report.critical_result(tm.Test_Messages.scene_settings_expected_mesh_group_found, editable_mesh_group_name == new_found_mesh_names[0])
+        
+        scene_test_helpers.save_and_verify_manifest(path_to_manifest, widget_main_window)
+        widget_main_window.close()
+
+    run_test()
+
+if __name__ == "__main__":
+    from editor_python_test_tools.utils import Report
+    Report.start_test(Scene_Settings_Tests_Remove_Prefab_Removes_Mesh_Groups)

+ 17 - 16
AutomatedTesting/Gem/PythonTests/assetpipeline/scene_settings_tests/tests/scene_settings_test_helpers.py

@@ -5,7 +5,7 @@ For complete copyright and license terms please see the LICENSE at the root of t
 SPDX-License-Identifier: Apache-2.0 OR MIT
 SPDX-License-Identifier: Apache-2.0 OR MIT
 """
 """
 
 
-def prepare_scene_ui_for_test(test_file_name, manifest_should_exist):
+def prepare_scene_ui_for_test(test_file_name, manifest_should_exist, should_create_manifest):
     import asyncio
     import asyncio
     from editor_python_test_tools.utils import Report
     from editor_python_test_tools.utils import Report
     import os
     import os
@@ -39,25 +39,26 @@ def prepare_scene_ui_for_test(test_file_name, manifest_should_exist):
     widget_main_window = QtWidgets.QWidget.find(window_id)
     widget_main_window = QtWidgets.QWidget.find(window_id)
 
 
     # When the window is first opened, even if it's to a new scene settings file not yet saved to disk, it shouldn't be marked with unsaved changes.
     # When the window is first opened, even if it's to a new scene settings file not yet saved to disk, it shouldn't be marked with unsaved changes.
-    has_unsaved_changes = azlmbr.qt.SceneSettingsRootDisplayScriptRequestBus(azlmbr.bus.Broadcast, "HasUnsavedChanges")
-    Report.critical_result(tm.Test_Messages.scene_settings_update_disabled_on_launch, not has_unsaved_changes)
+    if should_create_manifest:
+        has_unsaved_changes = azlmbr.qt.SceneSettingsRootDisplayScriptRequestBus(azlmbr.bus.Broadcast, "HasUnsavedChanges")
+        Report.critical_result(tm.Test_Messages.scene_settings_update_disabled_on_launch, not has_unsaved_changes)
 
 
-    card_layout_area = widget_main_window.findChild(QtWidgets.QWidget, "m_cardAreaLayoutWidget")
+        card_layout_area = widget_main_window.findChild(QtWidgets.QWidget, "m_cardAreaLayoutWidget")
     
     
-    # On initial launch of the Scene Settings UI, it will generate one processing event to load the requested file.
-    # Find the card for this processing event and close it.
-    first_card_push_buttons = card_layout_area.findChildren(QtWidgets.QPushButton,"")
-    Report.critical_result(tm.Test_Messages.scene_settings_only_one_card_in_layout, len(first_card_push_buttons) == 1)
-    Report.critical_result(tm.Test_Messages.scene_settings_status_card_can_be_clicked, first_card_push_buttons[0].isEnabled())
+        # On initial launch of the Scene Settings UI, it will generate one processing event to load the requested file.
+        # Find the card for this processing event and close it.
+        first_card_push_buttons = card_layout_area.findChildren(QtWidgets.QPushButton,"")
+        Report.critical_result(tm.Test_Messages.scene_settings_only_one_card_in_layout, len(first_card_push_buttons) == 1)
+        Report.critical_result(tm.Test_Messages.scene_settings_status_card_can_be_clicked, first_card_push_buttons[0].isEnabled())
 
 
-    # Click the button
-    first_card_push_buttons[0].click()
-    # Wait a brief period of time after clicking to make sure the status card is removed.
-    general.idle_wait_frames(30)
+        # Click the button
+        first_card_push_buttons[0].click()
+        # Wait a brief period of time after clicking to make sure the status card is removed.
+        general.idle_wait_frames(30)
 
 
-    # Verify the button no longer exists
-    first_card_push_buttons = card_layout_area.findChildren(QtWidgets.QPushButton,"")
-    Report.critical_result(tm.Test_Messages.scene_setting_card_dismissed_on_click, len(first_card_push_buttons) == 0)
+        # Verify the button no longer exists
+        first_card_push_buttons = card_layout_area.findChildren(QtWidgets.QPushButton,"")
+        Report.critical_result(tm.Test_Messages.scene_setting_card_dismissed_on_click, len(first_card_push_buttons) == 0)
     
     
     reflected_property_root = widget_main_window.findChild(QtWidgets.QWidget, "m_rootWidget")
     reflected_property_root = widget_main_window.findChild(QtWidgets.QWidget, "m_rootWidget")
 
 

+ 44 - 5
AutomatedTesting/Gem/PythonTests/assetpipeline/scene_settings_tests/tests/scene_settings_test_messages.py

@@ -53,15 +53,30 @@ class Test_Messages:
         "Found Update materials root object.",
         "Found Update materials root object.",
         "Failed to find Update materials root object."
         "Failed to find Update materials root object."
     )
     )
+    
+    scene_settings_found_advanced_settings_row = (
+        "Found 'use advanced settings' root object.",
+        "Failed to find 'use advanced settings' root object."
+    )
 
 
-    scene_settings_found_update_materials_checkbox = (
+    scene_settings_found_expected_interface_checkbox = (
         "Found the expected interface element in the scene settings UI.",
         "Found the expected interface element in the scene settings UI.",
         "Unable to find the expected interface element in the scene settings UI."
         "Unable to find the expected interface element in the scene settings UI."
     )
     )
+            
+    scene_settings_found_only_one_checkbox = (
+        "Found single checkbox object.",
+        "Checkbox count is incorrect."
+    )
+    
+    scene_settings_read_only_checked = (
+        "Verified checkbox was checked.",
+        "Checkbox was not checked."
+    )
     
     
-    scene_settings_found_only_one_update_materials_checkbox = (
-        "Found single Update materials checkbox object.",
-        "Update materials checkbox count is incorrect."
+    scene_settings_read_only_disabled = (
+        "Verified checkbox was not enabled.",
+        "Checkbox was enabled when it was expected to be read-only."
     )
     )
 
 
     # Make sure the UI became responsive after the save finished.
     # Make sure the UI became responsive after the save finished.
@@ -96,4 +111,28 @@ class Test_Messages:
         "Found single Ignore Y-Axis Transition checkbox object.",
         "Found single Ignore Y-Axis Transition checkbox object.",
         "Ignore Y-Axis Transition checkbox count is incorrect."
         "Ignore Y-Axis Transition checkbox count is incorrect."
     )
     )
-    
+
+    scene_settings_base_mesh_group_enabled = (
+        "The base mesh group is enabled.",
+        "The base mesh group is disabled, but should be enabled."
+    )
+
+    scene_settings_prefab_mesh_group_disabled = (
+        "The prefab generated mesh group is disabled.",
+        "The prefab generated mesh group is enabled, but should be disabled."
+    )
+    
+    scene_settings_expected_mesh_groups_found = (
+        "The expected mesh groups were found.",
+        "The expected mesh groups were not found."
+    )
+    
+    scene_settings_expected_mesh_groups_removed = (
+        "The expected mesh groups were removed.",
+        "The expected mesh groups were not removed."
+    )
+
+    scene_settings_expected_mesh_group_found = (
+        "The expected mesh group was found.",
+        "The expected mesh group was not found."
+    )

+ 3 - 4
AutomatedTesting/Gem/PythonTests/assetpipeline/scene_settings_tests/tests/scene_settings_tests_in_editor.py

@@ -19,14 +19,14 @@ def Scene_Settings_Tests_In_Editor_Create_And_Verify_Scene_Settings_File():
         import scene_settings_test_helpers as scene_test_helpers
         import scene_settings_test_helpers as scene_test_helpers
         
         
         path_to_manifest, widget_main_window, reflected_property_root = \
         path_to_manifest, widget_main_window, reflected_property_root = \
-            scene_test_helpers.prepare_scene_ui_for_test(test_file_name="auto_test_fbx.fbx", manifest_should_exist=False)
+            scene_test_helpers.prepare_scene_ui_for_test(test_file_name="auto_test_fbx.fbx", manifest_should_exist=False, should_create_manifest=True)
 
 
         update_materials_row = reflected_property_root.findChild(QtWidgets.QWidget,"Update materials")
         update_materials_row = reflected_property_root.findChild(QtWidgets.QWidget,"Update materials")
         Report.critical_result(tm.Test_Messages.scene_settings_found_update_materials_row, update_materials_row is not None)
         Report.critical_result(tm.Test_Messages.scene_settings_found_update_materials_row, update_materials_row is not None)
 
 
         check_boxes = update_materials_row.findChildren(QtWidgets.QCheckBox,"")
         check_boxes = update_materials_row.findChildren(QtWidgets.QCheckBox,"")
-        Report.critical_result(tm.Test_Messages.scene_settings_found_update_materials_checkbox, check_boxes is not None)
-        Report.critical_result(tm.Test_Messages.scene_settings_found_only_one_update_materials_checkbox, len(check_boxes) == 1)
+        Report.critical_result(tm.Test_Messages.scene_settings_found_expected_interface_checkbox, check_boxes is not None)
+        Report.critical_result(tm.Test_Messages.scene_settings_found_only_one_checkbox, len(check_boxes) == 1)
 
 
         update_material_checkbox = check_boxes[0]
         update_material_checkbox = check_boxes[0]
 
 
@@ -44,7 +44,6 @@ def Scene_Settings_Tests_In_Editor_Create_And_Verify_Scene_Settings_File():
         widget_main_window.close()
         widget_main_window.close()
 
 
     run_test()
     run_test()
-    
 
 
 if __name__ == "__main__":
 if __name__ == "__main__":
     from editor_python_test_tools.utils import Report
     from editor_python_test_tools.utils import Report

+ 9 - 0
Code/Tools/SceneAPI/SceneCore/DataTypes/IManifestObject.h

@@ -10,11 +10,16 @@
 
 
 #include <AzCore/RTTI/RTTI.h>
 #include <AzCore/RTTI/RTTI.h>
 #include <AzCore/Serialization/SerializeContext.h>
 #include <AzCore/Serialization/SerializeContext.h>
+#include <AzCore/std/containers/vector.h>
 
 
 namespace AZ
 namespace AZ
 {
 {
     namespace SceneAPI
     namespace SceneAPI
     {
     {
+        namespace Containers
+        {
+            class SceneManifest;
+        }
         namespace DataTypes
         namespace DataTypes
         {
         {
             class IManifestObject
             class IManifestObject
@@ -26,6 +31,10 @@ namespace AZ
                 virtual ~IManifestObject() = 0;
                 virtual ~IManifestObject() = 0;
                 virtual void OnUserAdded() {};
                 virtual void OnUserAdded() {};
                 virtual void OnUserRemoved() const {};
                 virtual void OnUserRemoved() const {};
+                // Some manifest objects cause other manifest objects to be created.
+                // When those manifest objects are removed, the added manifest objects should be removed, too.
+                virtual void GetManifestObjectsToRemoveOnRemoved(
+                    AZStd::vector<const IManifestObject*>& /*toRemove*/, const Containers::SceneManifest& /*manifest*/) const {}
             };
             };
 
 
             inline void IManifestObject::Reflect(AZ::ReflectContext* context)
             inline void IManifestObject::Reflect(AZ::ReflectContext* context)

+ 1 - 2
Code/Tools/SceneAPI/SceneCore/DataTypes/Rules/IBlendShapeRule.h

@@ -1,5 +1,3 @@
-#pragma once
-
 /*
 /*
  * Copyright (c) Contributors to the Open 3D Engine Project.
  * 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.
  * For complete copyright and license terms please see the LICENSE at the root of this distribution.
@@ -7,6 +5,7 @@
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  *
  */
  */
+#pragma once
 
 
 #include <AzCore/RTTI/RTTI.h>
 #include <AzCore/RTTI/RTTI.h>
 #include <SceneAPI/SceneCore/DataTypes/Rules/IRule.h>
 #include <SceneAPI/SceneCore/DataTypes/Rules/IRule.h>

+ 1 - 2
Code/Tools/SceneAPI/SceneCore/DataTypes/Rules/ICommentRule.h

@@ -1,5 +1,3 @@
-#pragma once
-
 /*
 /*
  * Copyright (c) Contributors to the Open 3D Engine Project.
  * 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.
  * For complete copyright and license terms please see the LICENSE at the root of this distribution.
@@ -7,6 +5,7 @@
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  *
  */
  */
+#pragma once
 
 
 #include <AzCore/std/string/string.h>
 #include <AzCore/std/string/string.h>
 #include <AzCore/RTTI/RTTI.h>
 #include <AzCore/RTTI/RTTI.h>

+ 1 - 2
Code/Tools/SceneAPI/SceneCore/DataTypes/Rules/ILodRule.h

@@ -1,5 +1,3 @@
-#pragma once
-
 /*
 /*
  * Copyright (c) Contributors to the Open 3D Engine Project.
  * 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.
  * For complete copyright and license terms please see the LICENSE at the root of this distribution.
@@ -7,6 +5,7 @@
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  *
  */
  */
+#pragma once
 
 
 #include <AzCore/RTTI/RTTI.h>
 #include <AzCore/RTTI/RTTI.h>
 #include <SceneAPI/SceneCore/DataTypes/Rules/IRule.h>
 #include <SceneAPI/SceneCore/DataTypes/Rules/IRule.h>

+ 1 - 2
Code/Tools/SceneAPI/SceneCore/DataTypes/Rules/IMaterialRule.h

@@ -1,5 +1,3 @@
-#pragma once
-
 /*
 /*
  * Copyright (c) Contributors to the Open 3D Engine Project.
  * 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.
  * For complete copyright and license terms please see the LICENSE at the root of this distribution.
@@ -7,6 +5,7 @@
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  *
  */
  */
+#pragma once
 
 
 #include <AzCore/RTTI/RTTI.h>
 #include <AzCore/RTTI/RTTI.h>
 #include <SceneAPI/SceneCore/DataTypes/Rules/IRule.h>
 #include <SceneAPI/SceneCore/DataTypes/Rules/IRule.h>

+ 1 - 2
Code/Tools/SceneAPI/SceneCore/DataTypes/Rules/IMeshAdvancedRule.h

@@ -1,5 +1,3 @@
-#pragma once
-
 /*
 /*
  * Copyright (c) Contributors to the Open 3D Engine Project.
  * 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.
  * For complete copyright and license terms please see the LICENSE at the root of this distribution.
@@ -7,6 +5,7 @@
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  *
  */
  */
+#pragma once
 
 
 #include <AzCore/std/string/string.h>
 #include <AzCore/std/string/string.h>
 #include <AzCore/RTTI/RTTI.h>
 #include <AzCore/RTTI/RTTI.h>

+ 3 - 2
Code/Tools/SceneAPI/SceneCore/DataTypes/Rules/IRule.h

@@ -1,5 +1,3 @@
-#pragma once
-
 /*
 /*
  * Copyright (c) Contributors to the Open 3D Engine Project.
  * 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.
  * For complete copyright and license terms please see the LICENSE at the root of this distribution.
@@ -7,6 +5,7 @@
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  *
  */
  */
+#pragma once
 
 
 #include <memory>
 #include <memory>
 #include <AzCore/std/string/string.h>
 #include <AzCore/std/string/string.h>
@@ -26,6 +25,8 @@ namespace AZ
                 AZ_RTTI(IRule, "{81267F8B-3963-423B-9FF7-D276D82CD110}", IManifestObject);
                 AZ_RTTI(IRule, "{81267F8B-3963-423B-9FF7-D276D82CD110}", IManifestObject);
 
 
                 virtual ~IRule() override = default;
                 virtual ~IRule() override = default;
+
+                virtual bool ModifyTooltip(AZStd::string& /*tooltip*/) {return false; }
             };
             };
         }  // DataTypes
         }  // DataTypes
     }  // SceneAPI
     }  // SceneAPI

+ 1 - 2
Code/Tools/SceneAPI/SceneCore/DataTypes/Rules/ISkeletonProxyRule.h

@@ -1,5 +1,3 @@
-#pragma once
-
 /*
 /*
  * Copyright (c) Contributors to the Open 3D Engine Project.
  * 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.
  * For complete copyright and license terms please see the LICENSE at the root of this distribution.
@@ -7,6 +5,7 @@
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  *
  */
  */
+#pragma once
 
 
 #include <AzCore/RTTI/RTTI.h>
 #include <AzCore/RTTI/RTTI.h>
 #include <SceneAPI/SceneCore/DataTypes/Rules/IRule.h>
 #include <SceneAPI/SceneCore/DataTypes/Rules/IRule.h>

+ 1 - 2
Code/Tools/SceneAPI/SceneCore/DataTypes/Rules/ISkinRule.h

@@ -1,5 +1,3 @@
-#pragma once
-
 /*
 /*
  * Copyright (c) Contributors to the Open 3D Engine Project.
  * 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.
  * For complete copyright and license terms please see the LICENSE at the root of this distribution.
@@ -7,6 +5,7 @@
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  *
  */
  */
+#pragma once
 
 
 #include <AzCore/RTTI/RTTI.h>
 #include <AzCore/RTTI/RTTI.h>
 #include <AzCore/Settings/SettingsRegistry.h>
 #include <AzCore/Settings/SettingsRegistry.h>

+ 28 - 0
Code/Tools/SceneAPI/SceneCore/DataTypes/Rules/IUnmodifiableRule.h

@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+#include <AzCore/RTTI/RTTI.h>
+#include <SceneAPI/SceneCore/DataTypes/Rules/IRule.h>
+
+namespace AZ
+{
+    namespace SceneAPI
+    {
+        namespace DataTypes
+        {
+            // Disables UI interaction for the node and children of the node with this rule.
+            class IUnmodifiableRule : public IRule
+            {
+            public:
+                AZ_RTTI(IUnmodifiableRule, "{071A7F4A-70A5-4D32-AC9C-1D49AFCB1480}", IRule);
+
+                virtual ~IUnmodifiableRule() override = default;
+            };
+        } // namespace DataTypes
+    } // namespace SceneAPI
+} // namespace AZ

+ 2 - 0
Code/Tools/SceneAPI/SceneCore/DllMain.cpp

@@ -41,6 +41,7 @@
 #include <SceneAPI/SceneCore/DataTypes/Rules/ILodRule.h>
 #include <SceneAPI/SceneCore/DataTypes/Rules/ILodRule.h>
 #include <SceneAPI/SceneCore/DataTypes/Rules/ISkeletonProxyRule.h>
 #include <SceneAPI/SceneCore/DataTypes/Rules/ISkeletonProxyRule.h>
 #include <SceneAPI/SceneCore/DataTypes/Rules/IScriptProcessorRule.h>
 #include <SceneAPI/SceneCore/DataTypes/Rules/IScriptProcessorRule.h>
+#include <SceneAPI/SceneCore/DataTypes/Rules/IUnmodifiableRule.h>
 #include <SceneAPI/SceneCore/DataTypes/GraphData/IAnimationData.h>
 #include <SceneAPI/SceneCore/DataTypes/GraphData/IAnimationData.h>
 #include <SceneAPI/SceneCore/DataTypes/GraphData/IBlendShapeData.h>
 #include <SceneAPI/SceneCore/DataTypes/GraphData/IBlendShapeData.h>
 #include <SceneAPI/SceneCore/DataTypes/GraphData/IBoneData.h>
 #include <SceneAPI/SceneCore/DataTypes/GraphData/IBoneData.h>
@@ -166,6 +167,7 @@ namespace AZ
                     context->Class<AZ::SceneAPI::DataTypes::ILodRule, AZ::SceneAPI::DataTypes::IRule>()->Version(1);
                     context->Class<AZ::SceneAPI::DataTypes::ILodRule, AZ::SceneAPI::DataTypes::IRule>()->Version(1);
                     context->Class<AZ::SceneAPI::DataTypes::ISkeletonProxyRule, AZ::SceneAPI::DataTypes::IRule>()->Version(1);
                     context->Class<AZ::SceneAPI::DataTypes::ISkeletonProxyRule, AZ::SceneAPI::DataTypes::IRule>()->Version(1);
                     context->Class<AZ::SceneAPI::DataTypes::IScriptProcessorRule, AZ::SceneAPI::DataTypes::IRule>()->Version(1);
                     context->Class<AZ::SceneAPI::DataTypes::IScriptProcessorRule, AZ::SceneAPI::DataTypes::IRule>()->Version(1);
+                    context->Class<AZ::SceneAPI::DataTypes::IUnmodifiableRule, AZ::SceneAPI::DataTypes::IRule>()->Version(1);
                     // Register graph data interfaces
                     // Register graph data interfaces
                     context->Class<AZ::SceneAPI::DataTypes::IAnimationData, AZ::SceneAPI::DataTypes::IGraphObject>()->Version(1);
                     context->Class<AZ::SceneAPI::DataTypes::IAnimationData, AZ::SceneAPI::DataTypes::IGraphObject>()->Version(1);
                     context->Class<AZ::SceneAPI::DataTypes::IBlendShapeData, AZ::SceneAPI::DataTypes::IGraphObject>()->Version(1);
                     context->Class<AZ::SceneAPI::DataTypes::IBlendShapeData, AZ::SceneAPI::DataTypes::IGraphObject>()->Version(1);

+ 1 - 0
Code/Tools/SceneAPI/SceneCore/scenecore_files.cmake

@@ -46,6 +46,7 @@ set(FILES
     DataTypes/Rules/ISkeletonProxyRule.h
     DataTypes/Rules/ISkeletonProxyRule.h
     DataTypes/Rules/ICoordinateSystemRule.h
     DataTypes/Rules/ICoordinateSystemRule.h
     DataTypes/Rules/IClothRule.h
     DataTypes/Rules/IClothRule.h
+    DataTypes/Rules/IUnmodifiableRule.h
     DataTypes/Rules/ISkinRule.h
     DataTypes/Rules/ISkinRule.h
     Components/BehaviorComponent.h
     Components/BehaviorComponent.h
     Components/BehaviorComponent.cpp
     Components/BehaviorComponent.cpp

+ 2 - 0
Code/Tools/SceneAPI/SceneData/ManifestMetaInfoHandler.cpp

@@ -21,6 +21,7 @@
 #include <SceneAPI/SceneData/Rules/CommentRule.h>
 #include <SceneAPI/SceneData/Rules/CommentRule.h>
 #include <SceneAPI/SceneData/Rules/LodRule.h>
 #include <SceneAPI/SceneData/Rules/LodRule.h>
 #include <SceneAPI/SceneData/Rules/MaterialRule.h>
 #include <SceneAPI/SceneData/Rules/MaterialRule.h>
+#include <SceneAPI/SceneData/Rules/UnmodifiableRule.h>
 #include <SceneAPI/SceneData/Rules/StaticMeshAdvancedRule.h>
 #include <SceneAPI/SceneData/Rules/StaticMeshAdvancedRule.h>
 #include <SceneAPI/SceneData/Rules/SkeletonProxyRule.h>
 #include <SceneAPI/SceneData/Rules/SkeletonProxyRule.h>
 #include <SceneAPI/SceneData/Rules/TangentsRule.h>
 #include <SceneAPI/SceneData/Rules/TangentsRule.h>
@@ -51,6 +52,7 @@ namespace AZ
             {
             {
                 AZ_TraceContext("Object Type", target.RTTI_GetTypeName());
                 AZ_TraceContext("Object Type", target.RTTI_GetTypeName());
                 modifiers.push_back(SceneData::CommentRule::TYPEINFO_Uuid());
                 modifiers.push_back(SceneData::CommentRule::TYPEINFO_Uuid());
+                modifiers.push_back(SceneData::UnmodifiableRule::TYPEINFO_Uuid());
 
 
                 if (target.RTTI_IsTypeOf(DataTypes::IMeshGroup::TYPEINFO_Uuid()))
                 if (target.RTTI_IsTypeOf(DataTypes::IMeshGroup::TYPEINFO_Uuid()))
                 {
                 {

+ 2 - 0
Code/Tools/SceneAPI/SceneData/ReflectionRegistrar.cpp

@@ -17,6 +17,7 @@
 #include <SceneAPI/SceneData/Rules/StaticMeshAdvancedRule.h>
 #include <SceneAPI/SceneData/Rules/StaticMeshAdvancedRule.h>
 #include <SceneAPI/SceneData/Rules/SkinMeshAdvancedRule.h>
 #include <SceneAPI/SceneData/Rules/SkinMeshAdvancedRule.h>
 #include <SceneAPI/SceneData/Rules/MaterialRule.h>
 #include <SceneAPI/SceneData/Rules/MaterialRule.h>
+#include <SceneAPI/SceneData/Rules/UnmodifiableRule.h>
 #include <SceneAPI/SceneData/Rules/ScriptProcessorRule.h>
 #include <SceneAPI/SceneData/Rules/ScriptProcessorRule.h>
 #include <SceneAPI/SceneData/Rules/SkeletonProxyRule.h>
 #include <SceneAPI/SceneData/Rules/SkeletonProxyRule.h>
 #include <SceneAPI/SceneData/Rules/TangentsRule.h>
 #include <SceneAPI/SceneData/Rules/TangentsRule.h>
@@ -68,6 +69,7 @@ namespace AZ
             SceneData::LodRule::Reflect(context);
             SceneData::LodRule::Reflect(context);
             SceneData::StaticMeshAdvancedRule::Reflect(context);
             SceneData::StaticMeshAdvancedRule::Reflect(context);
             SceneData::MaterialRule::Reflect(context);
             SceneData::MaterialRule::Reflect(context);
+            SceneData::UnmodifiableRule::Reflect(context);
             SceneData::ScriptProcessorRule::Reflect(context);
             SceneData::ScriptProcessorRule::Reflect(context);
             SceneData::SkeletonProxyRule::Reflect(context);
             SceneData::SkeletonProxyRule::Reflect(context);
             SceneData::SkinMeshAdvancedRule::Reflect(context);
             SceneData::SkinMeshAdvancedRule::Reflect(context);

+ 1 - 2
Code/Tools/SceneAPI/SceneData/Rules/BlendShapeRule.h

@@ -1,5 +1,3 @@
-#pragma once
-
 /*
 /*
  * Copyright (c) Contributors to the Open 3D Engine Project.
  * 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.
  * For complete copyright and license terms please see the LICENSE at the root of this distribution.
@@ -7,6 +5,7 @@
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  *
  */
  */
+#pragma once
 
 
 #include <AzCore/Memory/Memory.h>
 #include <AzCore/Memory/Memory.h>
 #include <AzCore/std/containers/vector.h>
 #include <AzCore/std/containers/vector.h>

+ 1 - 2
Code/Tools/SceneAPI/SceneData/Rules/CommentRule.h

@@ -1,5 +1,3 @@
-#pragma once
-
 /*
 /*
  * Copyright (c) Contributors to the Open 3D Engine Project.
  * 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.
  * For complete copyright and license terms please see the LICENSE at the root of this distribution.
@@ -7,6 +5,7 @@
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  *
  */
  */
+#pragma once
 
 
 #include <AzCore/Memory/Memory.h>
 #include <AzCore/Memory/Memory.h>
 #include <AzCore/std/string/string.h>
 #include <AzCore/std/string/string.h>

+ 1 - 2
Code/Tools/SceneAPI/SceneData/Rules/LodRule.h

@@ -1,5 +1,3 @@
-#pragma once
-
 /*
 /*
  * Copyright (c) Contributors to the Open 3D Engine Project.
  * 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.
  * For complete copyright and license terms please see the LICENSE at the root of this distribution.
@@ -7,6 +5,7 @@
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  *
  */
  */
+#pragma once
 
 
 #include <AzCore/Memory/Memory.h>
 #include <AzCore/Memory/Memory.h>
 #include <AzCore/std/containers/fixed_vector.h>
 #include <AzCore/std/containers/fixed_vector.h>

+ 1 - 2
Code/Tools/SceneAPI/SceneData/Rules/MaterialRule.h

@@ -1,5 +1,3 @@
-#pragma once
-
 /*
 /*
  * Copyright (c) Contributors to the Open 3D Engine Project.
  * 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.
  * For complete copyright and license terms please see the LICENSE at the root of this distribution.
@@ -7,6 +5,7 @@
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  *
  */
  */
+#pragma once
 
 
 #include <AzCore/Memory/Memory.h>
 #include <AzCore/Memory/Memory.h>
 #include <SceneAPI/SceneCore/DataTypes/Rules/IMaterialRule.h>
 #include <SceneAPI/SceneCore/DataTypes/Rules/IMaterialRule.h>

+ 1 - 2
Code/Tools/SceneAPI/SceneData/Rules/SkeletonProxyRule.h

@@ -1,5 +1,3 @@
-#pragma once
-
 /*
 /*
  * Copyright (c) Contributors to the Open 3D Engine Project.
  * 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.
  * For complete copyright and license terms please see the LICENSE at the root of this distribution.
@@ -7,6 +5,7 @@
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  *
  */
  */
+#pragma once
 
 
 #include <AzCore/Memory/Memory.h>
 #include <AzCore/Memory/Memory.h>
 #include <AzCore/std/containers/fixed_vector.h>
 #include <AzCore/std/containers/fixed_vector.h>

+ 1 - 2
Code/Tools/SceneAPI/SceneData/Rules/SkinMeshAdvancedRule.h

@@ -1,5 +1,3 @@
-#pragma once
-
 /*
 /*
  * Copyright (c) Contributors to the Open 3D Engine Project.
  * 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.
  * For complete copyright and license terms please see the LICENSE at the root of this distribution.
@@ -7,6 +5,7 @@
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  *
  */
  */
+#pragma once
 
 
 #include <AzCore/Memory/Memory.h>
 #include <AzCore/Memory/Memory.h>
 #include <AzCore/std/containers/fixed_vector.h>
 #include <AzCore/std/containers/fixed_vector.h>

+ 1 - 2
Code/Tools/SceneAPI/SceneData/Rules/SkinRule.h

@@ -1,5 +1,3 @@
-#pragma once
-
 /*
 /*
  * Copyright (c) Contributors to the Open 3D Engine Project.
  * 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.
  * For complete copyright and license terms please see the LICENSE at the root of this distribution.
@@ -7,6 +5,7 @@
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  *
  */
  */
+#pragma once
 
 
 #include <AzCore/Memory/Memory.h>
 #include <AzCore/Memory/Memory.h>
 #include <SceneAPI/SceneCore/DataTypes/Rules/ISkinRule.h>
 #include <SceneAPI/SceneCore/DataTypes/Rules/ISkinRule.h>

+ 1 - 2
Code/Tools/SceneAPI/SceneData/Rules/StaticMeshAdvancedRule.h

@@ -1,5 +1,3 @@
-#pragma once
-
 /*
 /*
  * Copyright (c) Contributors to the Open 3D Engine Project.
  * 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.
  * For complete copyright and license terms please see the LICENSE at the root of this distribution.
@@ -7,6 +5,7 @@
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  *
  */
  */
+#pragma once
 
 
 #include <AzCore/Memory/Memory.h>
 #include <AzCore/Memory/Memory.h>
 #include <AzCore/std/containers/fixed_vector.h>
 #include <AzCore/std/containers/fixed_vector.h>

+ 47 - 0
Code/Tools/SceneAPI/SceneData/Rules/UnmodifiableRule.cpp

@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <AzCore/Memory/SystemAllocator.h>
+#include <AzCore/RTTI/ReflectContext.h>
+#include <AzCore/Serialization/EditContext.h>
+#include <AzCore/Serialization/SerializeContext.h>
+#include <SceneAPI/SceneData/Rules/UnmodifiableRule.h>
+
+namespace AZ
+{
+    namespace SceneAPI
+    {
+        namespace SceneData
+        {
+            void UnmodifiableRule::Reflect(AZ::ReflectContext* context)
+            {
+                AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
+                if (!serializeContext)
+                {
+                    return;
+                }
+
+                serializeContext->Class<UnmodifiableRule, DataTypes::IUnmodifiableRule>()->Version(1);
+
+                AZ::EditContext* editContext = serializeContext->GetEditContext();
+                if (editContext)
+                {
+                    editContext->Class<UnmodifiableRule>("Unmodifiable", "This rule marks the container as unable to be modified.")
+                        ->ClassElement(Edit::ClassElements::EditorData, "")
+                        ->Attribute(AZ::Edit::Attributes::NameLabelOverride, "");
+                }
+            }
+
+            bool UnmodifiableRule::ModifyTooltip(AZStd::string& tooltip)
+            {
+                tooltip = AZStd::string::format("This group is not modifiable. %.*s", AZ_STRING_ARG(tooltip));
+                return true;
+            }
+        } // namespace SceneData
+    } // namespace SceneAPI
+} // namespace AZ

+ 37 - 0
Code/Tools/SceneAPI/SceneData/Rules/UnmodifiableRule.h

@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include <AzCore/Memory/Memory.h>
+#include <AzCore/std/string/string.h>
+#include <SceneAPI/SceneCore/DataTypes/Rules/IUnmodifiableRule.h>
+#include <SceneAPI/SceneData/SceneDataConfiguration.h>
+
+namespace AZ
+{
+    class ReflectContext;
+    namespace SceneAPI
+    {
+        namespace SceneData
+        {
+            // Disables UI interaction for the node and children of the node with this rule.
+            class SCENE_DATA_CLASS UnmodifiableRule : public DataTypes::IUnmodifiableRule
+            {
+            public:
+                AZ_RTTI(UnmodifiableRule, "{6527EBC2-60DF-4E5A-98B4-106F050A186C}", DataTypes::IUnmodifiableRule);
+                AZ_CLASS_ALLOCATOR(UnmodifiableRule, AZ::SystemAllocator, 0)
+
+                SCENE_DATA_API ~UnmodifiableRule() override = default;
+
+                SCENE_DATA_API bool ModifyTooltip(AZStd::string& tooltip) override;
+
+                static void Reflect(ReflectContext* context);
+            };
+        } // namespace DataTypes
+    } // namespace SceneAPI
+} // namespace AZ

+ 2 - 0
Code/Tools/SceneAPI/SceneData/SceneData_files.cmake

@@ -69,6 +69,8 @@ set(FILES
     Rules/SkinRule.cpp
     Rules/SkinRule.cpp
     Rules/TangentsRule.h
     Rules/TangentsRule.h
     Rules/TangentsRule.cpp
     Rules/TangentsRule.cpp
+    Rules/UnmodifiableRule.h
+    Rules/UnmodifiableRule.cpp
     GraphData/CustomPropertyData.h
     GraphData/CustomPropertyData.h
     GraphData/CustomPropertyData.cpp
     GraphData/CustomPropertyData.cpp
     GraphData/MeshData.h
     GraphData/MeshData.h

+ 12 - 0
Code/Tools/SceneAPI/SceneUI/RowWidgets/HeaderHandler.cpp

@@ -76,6 +76,18 @@ namespace AZ
                     s_instance = nullptr;
                     s_instance = nullptr;
                 }
                 }
             }
             }
+
+            bool HeaderHandler::ModifyTooltip(QWidget* widget, QString& toolTipString)
+            {
+                HeaderWidget* headerWidget = qobject_cast<HeaderWidget*>(widget);
+                if (!headerWidget)
+                {
+                    return false;
+                }
+
+                return headerWidget->ModifyTooltip(toolTipString);
+                
+            }
         } // UI
         } // UI
     } // SceneAPI
     } // SceneAPI
 } // AZ
 } // AZ

+ 2 - 0
Code/Tools/SceneAPI/SceneUI/RowWidgets/HeaderHandler.h

@@ -54,6 +54,8 @@ namespace AZ
                 bool ReadValuesIntoGUI(size_t index, HeaderWidget* GUI, const property_t& instance,
                 bool ReadValuesIntoGUI(size_t index, HeaderWidget* GUI, const property_t& instance,
                     AzToolsFramework::InstanceDataNode* node) override;
                     AzToolsFramework::InstanceDataNode* node) override;
 
 
+                bool ModifyTooltip(QWidget* widget, QString& toolTipString) override;
+
                 static void Register();
                 static void Register();
                 static void Unregister();
                 static void Unregister();
 
 

+ 59 - 5
Code/Tools/SceneAPI/SceneUI/RowWidgets/HeaderWidget.cpp

@@ -17,6 +17,7 @@
 #include <SceneAPI/SceneCore/Containers/Scene.h>
 #include <SceneAPI/SceneCore/Containers/Scene.h>
 #include <SceneAPI/SceneCore/Containers/SceneManifest.h>
 #include <SceneAPI/SceneCore/Containers/SceneManifest.h>
 #include <SceneAPI/SceneCore/DataTypes/Groups/ISceneNodeGroup.h>
 #include <SceneAPI/SceneCore/DataTypes/Groups/ISceneNodeGroup.h>
+#include <SceneAPI/SceneCore/DataTypes/Rules/IUnmodifiableRule.h>
 #include <SceneAPI/SceneCore/Utilities/Reporting.h>
 #include <SceneAPI/SceneCore/Utilities/Reporting.h>
 #include <SceneAPI/SceneCore/Events/ManifestMetaInfoBus.h>
 #include <SceneAPI/SceneCore/Events/ManifestMetaInfoBus.h>
 #include <SceneAPI/SceneUI/RowWidgets/HeaderWidget.h>
 #include <SceneAPI/SceneUI/RowWidgets/HeaderWidget.h>
@@ -43,7 +44,6 @@ namespace AZ
                 : QWidget(parent)
                 : QWidget(parent)
                 , ui(new Ui::HeaderWidget())
                 , ui(new Ui::HeaderWidget())
                 , m_target(nullptr)
                 , m_target(nullptr)
-                , m_nameIsEditable(false)
                 , m_sceneManifest(nullptr)
                 , m_sceneManifest(nullptr)
             {
             {
                 InitSceneUIHeaderWidgetResources();
                 InitSceneUIHeaderWidgetResources();
@@ -54,7 +54,7 @@ namespace AZ
                 ui->m_headerLine->hide();
                 ui->m_headerLine->hide();
                 ui->m_headerLineSpacer->hide();
                 ui->m_headerLineSpacer->hide();
                 
                 
-                ui->m_deleteButton->setIcon(QIcon(":/PropertyEditor/Resources/cross-small.png"));
+                ui->m_deleteButton->setIcon(QIcon(":/stylesheet/img/close_small.svg"));
                 connect(ui->m_deleteButton, &QToolButton::clicked, this, &HeaderWidget::DeleteObject);
                 connect(ui->m_deleteButton, &QToolButton::clicked, this, &HeaderWidget::DeleteObject);
                 ui->m_deleteButton->hide();
                 ui->m_deleteButton->hide();
 
 
@@ -82,6 +82,39 @@ namespace AZ
                 return m_target;
                 return m_target;
             }
             }
 
 
+            bool HeaderWidget::ModifyTooltip(QString& toolTipString)
+            {
+                if (!m_target)
+                {
+                    return false;
+                }
+                if (m_target->RTTI_IsTypeOf(DataTypes::IGroup::TYPEINFO_Uuid()))
+                {
+                    const DataTypes::IGroup* group = azrtti_cast<const DataTypes::IGroup*>(m_target);
+
+                    const Containers::RuleContainer& rules = group->GetRuleContainerConst();
+                    // Multiple rules might change the tooltip, so loop through all rules.
+                    bool ruleChangedTooltip = false;
+                    // Rules don't all have access to Qt
+                    AZStd::string ruleTooltip;
+                    for (size_t ruleIndex = 0; ruleIndex < rules.GetRuleCount(); ++ruleIndex)
+                    {
+                        if (rules.GetRule(ruleIndex)->ModifyTooltip(ruleTooltip))
+                        {
+                            ruleChangedTooltip = true;
+                        }
+                    }
+                    if (ruleChangedTooltip)
+                    {
+                        toolTipString = QString("%1%2").arg(ruleTooltip.c_str()).arg(toolTipString);
+                    }
+
+                    return ruleChangedTooltip;
+                }
+
+                return false;
+            }
+
             void HeaderWidget::DeleteObject()
             void HeaderWidget::DeleteObject()
             {
             {
                 AZ_TraceContext("Delete target", GetSerializedName(m_target));
                 AZ_TraceContext("Delete target", GetSerializedName(m_target));
@@ -91,8 +124,10 @@ namespace AZ
                     Containers::SceneManifest::Index index = m_sceneManifest->FindIndex(m_target);
                     Containers::SceneManifest::Index index = m_sceneManifest->FindIndex(m_target);
                     if (index != Containers::SceneManifest::s_invalidIndex)
                     if (index != Containers::SceneManifest::s_invalidIndex)
                     {
                     {
-                        AZ_TraceContext("Manifest index", static_cast<int>(index));
                         ManifestWidget* root = ManifestWidget::FindRoot(this);
                         ManifestWidget* root = ManifestWidget::FindRoot(this);
+
+                        AZStd::vector<const AZ::SceneAPI::DataTypes::IManifestObject*> otherObjectsToRemove;
+                        m_target->GetManifestObjectsToRemoveOnRemoved(otherObjectsToRemove, *m_sceneManifest);
                         // The manifest object could be a root element at the manifest page level so it needs to be
                         // The manifest object could be a root element at the manifest page level so it needs to be
                         //      removed from there as well in that case.
                         //      removed from there as well in that case.
                         if (root->RemoveObject(m_sceneManifest->GetValue(index)) && m_sceneManifest->RemoveEntry(m_target))
                         if (root->RemoveObject(m_sceneManifest->GetValue(index)) && m_sceneManifest->RemoveEntry(m_target))
@@ -101,6 +136,13 @@ namespace AZ
                             // Hide and disable the button so when users spam the delete button only a single click is recorded.
                             // Hide and disable the button so when users spam the delete button only a single click is recorded.
                             ui->m_deleteButton->hide();
                             ui->m_deleteButton->hide();
                             ui->m_deleteButton->setEnabled(false);
                             ui->m_deleteButton->setEnabled(false);
+
+                            for (auto* toRemove : otherObjectsToRemove)
+                            {
+                                index = m_sceneManifest->FindIndex(toRemove);
+                                root->RemoveObject(m_sceneManifest->GetValue(index));
+                                m_sceneManifest->RemoveEntry(toRemove);
+                            }
                             return;
                             return;
                         }
                         }
                         else
                         else
@@ -140,6 +182,19 @@ namespace AZ
             {
             {
                 ui->m_deleteButton->hide();
                 ui->m_deleteButton->hide();
 
 
+                // If this widget has the unmodifiable rule, then this can't be deleted.
+                // Even though the delete button would be disabled, it's even more clear it can't be deleted if it's not visible.
+                if (m_target->RTTI_IsTypeOf(DataTypes::IGroup::TYPEINFO_Uuid()))
+                {
+                    const DataTypes::IGroup* sceneNodeGroup = azrtti_cast<const DataTypes::IGroup*>(m_target);
+                    const Containers::RuleContainer& rules = sceneNodeGroup->GetRuleContainerConst();
+                    if (rules.FindFirstByType<AZ::SceneAPI::DataTypes::IUnmodifiableRule>())
+                    {
+                        // This header is unmodifiable, so leave the delete button hidden.
+                        return;
+                    }
+                }
+
                 if (m_sceneManifest)
                 if (m_sceneManifest)
                 {
                 {
                     Containers::SceneManifest::Index index = m_sceneManifest->FindIndex(m_target);
                     Containers::SceneManifest::Index index = m_sceneManifest->FindIndex(m_target);
@@ -196,8 +251,7 @@ namespace AZ
                 // show have a visual divider, and potentially the icon.
                 // show have a visual divider, and potentially the icon.
                 if (target->RTTI_IsTypeOf(DataTypes::IGroup::TYPEINFO_Uuid()))
                 if (target->RTTI_IsTypeOf(DataTypes::IGroup::TYPEINFO_Uuid()))
                 {
                 {
-                    sceneNodeGroup = azrtti_cast<const DataTypes::IGroup*>(&target);
-                    
+                    sceneNodeGroup = azrtti_cast<const DataTypes::IGroup*>(target);
                     AZ::SerializeContext* serializeContext = nullptr;
                     AZ::SerializeContext* serializeContext = nullptr;
                     AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
                     AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
                     AZ_Assert(serializeContext, "No serialize context");
                     AZ_Assert(serializeContext, "No serialize context");

+ 2 - 1
Code/Tools/SceneAPI/SceneUI/RowWidgets/HeaderWidget.h

@@ -50,6 +50,8 @@ namespace AZ
                 void SetManifestObject(const DataTypes::IManifestObject* target);
                 void SetManifestObject(const DataTypes::IManifestObject* target);
                 const DataTypes::IManifestObject* GetManifestObject() const;
                 const DataTypes::IManifestObject* GetManifestObject() const;
 
 
+                bool ModifyTooltip(QString& toolTipString);
+
             protected:
             protected:
                 bool InitSceneManifest();
                 bool InitSceneManifest();
 
 
@@ -63,7 +65,6 @@ namespace AZ
                 QScopedPointer<Ui::HeaderWidget> ui;
                 QScopedPointer<Ui::HeaderWidget> ui;
                 Containers::SceneManifest* m_sceneManifest; // Reference only, does not point to a local instance.
                 Containers::SceneManifest* m_sceneManifest; // Reference only, does not point to a local instance.
                 const DataTypes::IManifestObject* m_target; // Reference only, does not point to a local instance.
                 const DataTypes::IManifestObject* m_target; // Reference only, does not point to a local instance.
-                bool m_nameIsEditable;
             };
             };
         } // namespace UI
         } // namespace UI
     } // namespace SceneAPI
     } // namespace SceneAPI

+ 24 - 0
Code/Tools/SceneAPI/SceneUI/SceneWidgets/ManifestWidgetPage.cpp

@@ -15,7 +15,9 @@
 #include <AzFramework/StringFunc/StringFunc.h>
 #include <AzFramework/StringFunc/StringFunc.h>
 #include <AzCore/std/string/conversions.h>
 #include <AzCore/std/string/conversions.h>
 #include <SceneWidgets/ui_ManifestWidgetPage.h>
 #include <SceneWidgets/ui_ManifestWidgetPage.h>
+#include <SceneAPI/SceneCore/DataTypes/Groups/IGroup.h>
 #include <SceneAPI/SceneCore/DataTypes/IManifestObject.h>
 #include <SceneAPI/SceneCore/DataTypes/IManifestObject.h>
+#include <SceneAPI/SceneCore/DataTypes/Rules/IUnmodifiableRule.h>
 #include <SceneAPI/SceneCore/Containers/Scene.h>
 #include <SceneAPI/SceneCore/Containers/Scene.h>
 #include <SceneAPI/SceneCore/Containers/SceneManifest.h>
 #include <SceneAPI/SceneCore/Containers/SceneManifest.h>
 #include <SceneAPI/SceneCore/Containers/Views/PairIterator.h>
 #include <SceneAPI/SceneCore/Containers/Views/PairIterator.h>
@@ -40,6 +42,13 @@ namespace AZ
 
 
                 m_propertyEditor = new AzToolsFramework::ReflectedPropertyEditor(nullptr);
                 m_propertyEditor = new AzToolsFramework::ReflectedPropertyEditor(nullptr);
                 m_propertyEditor->Setup(context, this, true, 250);
                 m_propertyEditor->Setup(context, this, true, 250);
+
+                m_propertyEditor->SetReadOnlyQueryFunction(
+                    [this](const AzToolsFramework::InstanceDataNode* node)
+                    {
+                        return SetNodeReadOnlyStatus(node);
+                    });
+
                 ui->m_mainLayout->insertWidget(0, m_propertyEditor);
                 ui->m_mainLayout->insertWidget(0, m_propertyEditor);
 
 
                 BuildAndConnectAddButton();
                 BuildAndConnectAddButton();
@@ -417,6 +426,21 @@ namespace AZ
                     }
                     }
                 }
                 }
             }
             }
+
+            bool ManifestWidgetPage::SetNodeReadOnlyStatus(const AzToolsFramework::InstanceDataNode* node)
+            {
+                if (AzToolsFramework::InstanceDataNode* parentNode = node ? node->GetRoot() : nullptr)
+                {
+                    AZ::SceneAPI::DataTypes::IGroup* group = m_context->Cast<AZ::SceneAPI::DataTypes::IGroup*>(
+                        parentNode->FirstInstance(), parentNode->GetClassMetadata()->m_typeId);
+                    // If this group is unmodifiable, that means it's read only.
+                    if (group && group->GetRuleContainerConst().FindFirstByType<AZ::SceneAPI::DataTypes::IUnmodifiableRule>())
+                    {
+                        return true;
+                    }
+                }
+                return false;
+            }
         } // namespace UI
         } // namespace UI
     } // namespace SceneAPI
     } // namespace SceneAPI
 } // namespace AZ
 } // namespace AZ

+ 2 - 0
Code/Tools/SceneAPI/SceneUI/SceneWidgets/ManifestWidgetPage.h

@@ -88,6 +88,8 @@ namespace AZ
                 // ManifestMetaInfoBus
                 // ManifestMetaInfoBus
                 void ObjectUpdated(const Containers::Scene& scene, const DataTypes::IManifestObject* target, void* sender) override;
                 void ObjectUpdated(const Containers::Scene& scene, const DataTypes::IManifestObject* target, void* sender) override;
 
 
+                bool SetNodeReadOnlyStatus(const AzToolsFramework::InstanceDataNode* node);
+
                 AZStd::vector<AZ::Uuid> m_classTypeIds;
                 AZStd::vector<AZ::Uuid> m_classTypeIds;
                 AZStd::vector<AZStd::shared_ptr<DataTypes::IManifestObject>> m_objects;
                 AZStd::vector<AZStd::shared_ptr<DataTypes::IManifestObject>> m_objects;
                 QScopedPointer<Ui::ManifestWidgetPage> ui;
                 QScopedPointer<Ui::ManifestWidgetPage> ui;

+ 4 - 0
Gems/Prefab/PrefabBuilder/PrefabGroup/DefaultProceduralPrefab.cpp

@@ -25,6 +25,7 @@
 #include <SceneAPI/SceneData/Groups/MeshGroup.h>
 #include <SceneAPI/SceneData/Groups/MeshGroup.h>
 #include <SceneAPI/SceneData/Rules/CoordinateSystemRule.h>
 #include <SceneAPI/SceneData/Rules/CoordinateSystemRule.h>
 #include <SceneAPI/SceneData/Rules/LodRule.h>
 #include <SceneAPI/SceneData/Rules/LodRule.h>
+#include <SceneAPI/SceneData/Rules/UnmodifiableRule.h>
 #include <AzCore/Component/EntityId.h>
 #include <AzCore/Component/EntityId.h>
 
 
 namespace AZ
 namespace AZ
@@ -349,6 +350,9 @@ namespace AZ::SceneAPI
         // tag this mesh group as a "default mesh group" using this rule
         // tag this mesh group as a "default mesh group" using this rule
         meshGroup->GetRuleContainer().AddRule(AZStd::make_shared<AZ::SceneAPI::SceneData::ProceduralMeshGroupRule>());
         meshGroup->GetRuleContainer().AddRule(AZStd::make_shared<AZ::SceneAPI::SceneData::ProceduralMeshGroupRule>());
 
 
+        // Don't let users edit these mesh groups, because they're procedural they'll be re-generated and overwrite any changes.
+        meshGroup->GetRuleContainer().AddRule(AZStd::make_shared<AZ::SceneAPI::SceneData::UnmodifiableRule>());
+
         // this clears out the mesh coordinates each mesh group will be rotated and translated
         // this clears out the mesh coordinates each mesh group will be rotated and translated
         // using the attached scene graph node
         // using the attached scene graph node
         auto coordinateSystemRule = AZStd::make_shared<AZ::SceneAPI::SceneData::CoordinateSystemRule>();
         auto coordinateSystemRule = AZStd::make_shared<AZ::SceneAPI::SceneData::CoordinateSystemRule>();

+ 29 - 0
Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroup.cpp

@@ -16,6 +16,7 @@
 #include <AzCore/std/smart_ptr/make_shared.h>
 #include <AzCore/std/smart_ptr/make_shared.h>
 #include <AzCore/std/optional.h>
 #include <AzCore/std/optional.h>
 #include <AzFramework/FileFunc/FileFunc.h>
 #include <AzFramework/FileFunc/FileFunc.h>
+#include <SceneAPI/SceneCore/Containers/SceneManifest.h>
 
 
 namespace AZ::SceneAPI::SceneData
 namespace AZ::SceneAPI::SceneData
 {
 {
@@ -67,6 +68,27 @@ namespace AZ::SceneAPI::SceneData
         return m_nodeSelectionList;
         return m_nodeSelectionList;
     }
     }
 
 
+    void PrefabGroup::GetManifestObjectsToRemoveOnRemoved(
+        AZStd::vector<const IManifestObject*>& toRemove, const AZ::SceneAPI::Containers::SceneManifest& manifest) const
+    {
+        for (size_t manifestIndex = 0; manifestIndex < manifest.GetEntryCount(); ++manifestIndex)
+        {
+            const AZStd::shared_ptr<const DataTypes::IManifestObject> manifestObjectAtIndex = manifest.GetValue(manifestIndex);
+            if (manifestObjectAtIndex->RTTI_IsTypeOf(DataTypes::IGroup::TYPEINFO_Uuid()))
+            {
+                // Removing shared ownership is useful because this is about to be deleted.
+                const DataTypes::IGroup* sceneNodeGroup = azrtti_cast<const DataTypes::IGroup*>(manifestObjectAtIndex.get());
+                const Containers::RuleContainer& rules = sceneNodeGroup->GetRuleContainerConst();
+                // Anything with the procedural mesh group rule was added automatically for this prefab group, so mark it for removal.
+                if (rules.FindFirstByType<AZ::SceneAPI::SceneData::ProceduralMeshGroupRule>())
+                {
+                    // Add it to the list to remove.
+                    toRemove.push_back(sceneNodeGroup);
+                }
+            }
+        }
+    }
+
     void PrefabGroup::SetPrefabDom(AzToolsFramework::Prefab::PrefabDom prefabDom)
     void PrefabGroup::SetPrefabDom(AzToolsFramework::Prefab::PrefabDom prefabDom)
     {
     {
         m_prefabDomData = AZStd::make_shared<Prefab::PrefabDomData>();
         m_prefabDomData = AZStd::make_shared<Prefab::PrefabDomData>();
@@ -119,6 +141,7 @@ namespace AZ::SceneAPI::SceneData
                     ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
                     ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
                         ->Attribute("AutoExpand", true)
                         ->Attribute("AutoExpand", true)
                         ->Attribute(AZ::Edit::Attributes::NameLabelOverride, "")
                         ->Attribute(AZ::Edit::Attributes::NameLabelOverride, "")
+                        ->Attribute(AZ::Edit::Attributes::CategoryStyle, "display divider")
                     ->DataElement(0,
                     ->DataElement(0,
                         &PrefabGroup::m_createProceduralPrefab,
                         &PrefabGroup::m_createProceduralPrefab,
                         "Create default procedural prefab?",
                         "Create default procedural prefab?",
@@ -161,4 +184,10 @@ namespace AZ::SceneAPI::SceneData
                 ->Property("prefabDomData", getPrefabDomData, setPrefabDomData);
                 ->Property("prefabDomData", getPrefabDomData, setPrefabDomData);
         }
         }
     }
     }
+
+    bool ProceduralMeshGroupRule::ModifyTooltip(AZStd::string& tooltip)
+    {
+        tooltip = AZStd::string::format("This group was generated by the procedural prefab. To remove this group, remove the procedural prefab. %.*s", AZ_STRING_ARG(tooltip));
+        return true;
+    }
 }
 }

+ 6 - 0
Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroup.h

@@ -50,6 +50,10 @@ namespace AZ::SceneAPI::SceneData
         DataTypes::ISceneNodeSelectionList& GetSceneNodeSelectionList() override;
         DataTypes::ISceneNodeSelectionList& GetSceneNodeSelectionList() override;
         const DataTypes::ISceneNodeSelectionList& GetSceneNodeSelectionList() const override;
         const DataTypes::ISceneNodeSelectionList& GetSceneNodeSelectionList() const override;
 
 
+        // IManifestObject
+        void GetManifestObjectsToRemoveOnRemoved(
+            AZStd::vector<const IManifestObject*>& toRemove, const AZ::SceneAPI::Containers::SceneManifest& manifest) const override;
+
         // Concrete API
         // Concrete API
         void SetId(Uuid id);
         void SetId(Uuid id);
         void SetName(AZStd::string name);
         void SetName(AZStd::string name);
@@ -77,5 +81,7 @@ namespace AZ::SceneAPI::SceneData
 
 
         ProceduralMeshGroupRule() = default;
         ProceduralMeshGroupRule() = default;
         ~ProceduralMeshGroupRule() override = default;
         ~ProceduralMeshGroupRule() override = default;
+
+        bool ModifyTooltip(AZStd::string& tooltip) override;
     };
     };
 }
 }

Some files were not shown because too many files changed in this diff