Переглянути джерело

Adding 2 new Editor tests for AssetPipeline (#12432)

* Signed-off-by: Rosario Cox <[email protected]>

Adding 2 new FBX tests for Morph Targets and the needed test files.
Updated dbgsgs for multiple_mesh_linked_materials test.

Signed-off-by: Rosario Cox <[email protected]>
LesaelR 2 роки тому
батько
коміт
ff1b171705

+ 14 - 2
AutomatedTesting/Gem/PythonTests/assetpipeline/CMakeLists.txt

@@ -10,6 +10,18 @@ add_subdirectory(asset_processor_tests)
 add_subdirectory(fbx_tests)
 add_subdirectory(scene_settings_tests)
 
+    ly_add_pytest(
+        NAME AssetPipelineTests.Periodic
+        PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Periodic.py
+        TEST_SUITE periodic
+        RUNTIME_DEPENDENCIES
+            AssetProcessor
+            AutomatedTesting.Assets
+            Editor
+        COMPONENT
+            Atom
+    )
+
 if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
 ## AP Python Tests ##
     ly_add_pytest(
@@ -18,7 +30,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
         EXCLUDE_TEST_RUN_TARGET_FROM_IDE
         TEST_SUITE periodic
         RUNTIME_DEPENDENCIES
-            AZ::AssetProcessorBatch   
+            AZ::AssetProcessorBatch
     )
-        
+   
 endif()

+ 22 - 0
AutomatedTesting/Gem/PythonTests/assetpipeline/TestSuite_Periodic.py

@@ -0,0 +1,22 @@
+"""
+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
+
+"""
+
+import pytest
+
+from ly_test_tools.o3de.editor_test import EditorTestSuite, EditorSingleTest
+
[email protected]_periodic
[email protected]("project", ["AutomatedTesting"])
[email protected]("launcher_platform", ['windows_editor'])
+class TestAutomation(EditorTestSuite):
+
+    class SubID_NoChange_MeshChanged(EditorSingleTest):
+        from .fbx_tests import hydra_SubID_NoChange_MeshChanged as test_module
+
+    class SubID_WarningReported_AssetRemoved(EditorSingleTest):
+        from .fbx_tests import hydra_SubID_WarningReported_AssetRemoved as test_module

+ 38 - 0
AutomatedTesting/Gem/PythonTests/assetpipeline/ap_fixtures/check_model_ready_fixture.py

@@ -0,0 +1,38 @@
+"""
+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
+"""
+import azlmbr.bus
+import azlmbr.asset
+from functools import partial
+from editor_python_test_tools.utils import TestHelper
+
+
+class OnModelReloaded:
+    def __init__(self):
+        self.isModelReloaded = False
+
+    def model_is_reloaded_predicate(self):
+        """
+        A predicate function what will be used in wait_for_condition.
+        """
+        return self.isModelReloaded
+
+    def on_model_reloaded(self, parameter):
+        self.isModelReloaded = True
+
+    def wait_for_on_model_reloaded(self, asset_id):
+        self.isModelReloaded = False
+        # Listen for notifications when assets are reloaded
+        self.onModelReloadedHandler = azlmbr.asset.AssetBusHandler()
+        self.onModelReloadedHandler.connect(asset_id)
+        self.onModelReloadedHandler.add_callback('OnAssetReloaded', self.on_model_reloaded)
+
+        waitCondition = partial(self.model_is_reloaded_predicate)
+
+        if TestHelper.wait_for_condition(waitCondition, 20.0):
+            return True
+        else:
+            return False

+ 114 - 0
AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/hydra_SubID_NoChange_MeshChanged.py

@@ -0,0 +1,114 @@
+"""
+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
+"""
+import assetpipeline.ap_fixtures.ap_idle_fixture
+
+
+class Tests:
+    asset_id_no_change = (
+        "Model AssetId matches after changes",
+        "P0: Model AssetId does not match expected output")
+
+def SubID_NoChange_MeshChanged():
+    """
+    Summary:
+    Opens a level with an entity containing a mesh component.
+    Verify updating a scene file where the product's asset subid remains the same still updates the mesh component.
+
+    Expected Behavior:
+    The updated asset's subid remains the same while the mesh referenced in the mesh component updates.
+
+    Test Steps:
+     1) Load the level
+     2) Start the Tracer to catch any errors and warnings
+     3) Record the original asset's subid and vert count
+     4) Add a scene settings file to update the scene products
+     5) Reload the level
+     6) Record the updated asset's subid and vert count
+     7) Verify there are no errors and warnings in the logs
+     8) Verify the subids match while the vert counts do not
+     9) Close the editor
+
+    :return: None
+    """
+
+    import os
+    import shutil
+    import time
+    from pathlib import Path
+
+    import azlmbr.bus
+    import azlmbr.editor as editor
+    from Atom.atom_utils.atom_constants import AtomComponentProperties
+    from EditorPythonTestTools.editor_python_test_tools.editor_entity_utils import EditorEntity
+    from EditorPythonTestTools.editor_python_test_tools.utils import Report, TestHelper, Tracer
+    from EditorPythonTestTools.editor_python_test_tools.asset_utils import Asset
+    from assetpipeline.ap_fixtures.check_model_ready_fixture import OnModelReloaded
+
+
+    with Tracer() as error_tracer:
+        # -- Test Setup Begins --
+        # Test Setup: Wait for Editor idle before loading the level and executing python hydra scripts.
+        TestHelper.init_idle()
+        TestHelper.open_level("AssetPipeline", "SceneTests")
+
+        # Test Setup: Set source and destination paths for assetinfo file.
+        dirpath = editor.EditorToolsApplicationRequestBus(azlmbr.bus.Broadcast, 'GetGameFolder')
+        src = os.path.join(dirpath, 'Objects', 'ShaderBall_simple', 'shaderball_simple_MeshChange_SameID.fbx.assetinfo')
+        dst = os.path.join(dirpath, 'Objects', 'shaderball_simple.fbx.assetinfo')
+
+        # Test Setup: Find the asset by asset path
+        model_path = os.path.join('objects', 'shaderball_simple.azmodel')
+        model = Asset.find_asset_by_path(model_path)
+
+        # Test Setup: Ensure there is no assetinfo file in the dst path, if there is, remove it.
+        checkModel = OnModelReloaded()
+        if os.path.exists(dst):
+            os.remove(dst)
+            checkModel.wait_for_on_model_reloaded(model.id)
+
+        # Test Setup: Find the entity, and it's mesh component, within the level.
+        find_entity = EditorEntity.find_editor_entity(AtomComponentProperties.mesh())
+        find_component = find_entity.get_components_of_type([AtomComponentProperties.mesh()])[0]
+
+        #Test Setup: Record the initial values for the Model Asset and the Vertex Count of LOD0.
+        original_component_id = find_component.get_component_property_value(AtomComponentProperties.mesh('Model Asset'))
+        original_vert_count = find_component.get_component_property_value(AtomComponentProperties.mesh('Vertex Count LOD0'))
+
+        # -- Test Begins --
+        # 1. Copy an assetinfo file to change scene output.
+        shutil.copyfile(src, dst)
+        checkModel.wait_for_on_model_reloaded(model.id)
+
+        # 2. Reload the level to reflect changes.
+        TestHelper.open_level("", "Base")
+        time.sleep(0.2)
+        TestHelper.open_level("AssetPipeline", "SceneTests")
+        time.sleep(0.2)
+
+        # 3. Record the current values for the Model Asset and the Vertex Count of LOD0.
+        current_component_id = find_component.get_component_property_value(AtomComponentProperties.mesh('Model Asset'))
+        current_vert_count = find_component.get_component_property_value(AtomComponentProperties.mesh('Vertex Count LOD0'))
+        result = current_component_id == original_component_id and original_vert_count != current_vert_count
+
+        # 4. Look for errors or asserts.
+        TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0)
+        for error_info in error_tracer.errors:
+            Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}")
+        for assert_info in error_tracer.asserts:
+            Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}")
+
+        # 5. Clean-up.
+        os.remove(dst)
+        checkModel.wait_for_on_model_reloaded(model.id)
+
+        # 6. Report the test results gathered and end the test.
+        Report.critical_result(Tests.asset_id_no_change, result)
+
+
+if __name__ == "__main__":
+    from editor_python_test_tools.utils import Report
+    Report.start_test(SubID_NoChange_MeshChanged)

+ 112 - 0
AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/hydra_SubID_WarningReported_AssetRemoved.py

@@ -0,0 +1,112 @@
+"""
+Copyright (c) Contributors to the Open 3D Engine Project.
+For complete copyright and license terms please see the LICENSE at the root of this distribution.
+
+SPDX-License-Identifier: Apache-2.0 OR MIT
+"""
+
+
+class Tests:
+
+    asset_id_missing_warning_reported = (
+        "Successfully reported missing asset",
+        "P0: Expected warning not reported from missing asset")
+
+    asset_id_no_match = (
+        "Missing asset successfully reports no matching asset id",
+        "P0: Asset id matched for a missing asset")
+
+
+def SubID_WarningReported_AssetRemoved():
+    """
+    Summary:
+    Opens a level with an entity containing a mesh component.
+    Verify updating a scene file where the product's asset subid remains the same still updates the mesh component.
+
+    Expected Behavior:
+    The updated asset's subid remains the same while the mesh referenced in the mesh component updates.
+
+    Test Steps:
+     1) Load the level
+     2) Start the Tracer to catch any errors and warnings
+     3) Record the original asset's subid and vert count
+     4) Add a scene settings file to update the scene products
+     5) Reload the level
+     6) Record the updated asset's subid and vert count
+     7) Verify there are no errors and warnings in the logs
+     8) Verify the subids match while the vert counts do not
+     9) Close the editor
+
+    :return: None
+    """
+
+    import os
+    import shutil
+    import time
+    from pathlib import Path
+
+    import azlmbr.bus
+    import azlmbr.editor as editor
+    from assetpipeline.ap_fixtures.check_model_ready_fixture import OnModelReloaded
+    from EditorPythonTestTools.editor_python_test_tools.utils import Report, TestHelper, Tracer
+    from EditorPythonTestTools.editor_python_test_tools.asset_utils import Asset
+
+    # -- Test Setup Begins --
+    # Test Setup: Wait for Editor idle before loading the level and executing Python hydra scripts.
+    TestHelper.init_idle()
+    TestHelper.open_level("AssetPipeline", "SceneTests")
+
+    # Test Setup: Set up source and destination for assetinfo file, then verify there is no file there already
+    dirpath = editor.EditorToolsApplicationRequestBus(azlmbr.bus.Broadcast, 'GetGameFolder')
+    src = os.path.join(dirpath, 'Objects', 'ShaderBall_simple', 'shaderball_simple_NoMesh_NoID.fbx.assetinfo')
+    dst = os.path.join(dirpath, 'Objects', 'shaderball_simple.fbx.assetinfo')
+
+    # Test Setup: Find the asset by asset path - Use azmaterial as we will be removing the azmodels during the test.
+    model_path = os.path.join('objects', 'shaderball_simple_phong_0_17699592688871882463.azmaterial')
+    model = Asset.find_asset_by_path(model_path)
+    checkModel = OnModelReloaded()
+
+    # Test Setup: Ensure there is no assetinfo file in the dst path, if there is, remove it.
+    if os.path.exists(dst):
+        os.remove(dst)
+        checkModel.wait_for_on_model_reloaded(model.id)
+
+    # -- Test Begins --
+    # 1. Copy an assetinfo file to change scene output by removing all meshes
+    shutil.copyfile(src, dst)
+    checkModel.wait_for_on_model_reloaded(model.id)
+
+    # 2. Start the Tracer to catch any errors and warnings
+    with Tracer() as error_tracer:
+        # 2.1 Reload the current level causing the Editor to attempt to reload the now missing entity.
+        TestHelper.open_level("", "Base")
+        time.sleep(0.2)
+        TestHelper.open_level("AssetPipeline", "SceneTests")
+        time.sleep(0.2)
+
+    # 3. Search for the specific warnings we are expecting.
+    warning_reported = False
+    warning = "[AssetManager] GetAsset called for asset which does not exist in asset catalog and cannot be loaded. " \
+              "Asset may be missing, not processed or moved. AssetId: {AA31FE94-4C6A-5355-B040-C1795BAED142}:10201c91"
+    for warning_msg in error_tracer.warnings:
+        if warning in str(warning_msg):
+            warning_reported = True
+
+    # 4. Look for other errors or asserts.
+    TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0)
+    for error_info in error_tracer.errors:
+        Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}")
+    for assert_info in error_tracer.asserts:
+        Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}")
+
+    # 5. Clean-up
+    os.remove(dst)
+    checkModel.wait_for_on_model_reloaded(model.id)
+
+    # 6. Report the test results gathered and end the test.
+    Report.critical_result(Tests.asset_id_missing_warning_reported, warning_reported)
+
+
+if __name__ == "__main__":
+    from editor_python_test_tools.utils import Report
+    Report.start_test(SubID_WarningReported_AssetRemoved)

+ 117 - 0
AutomatedTesting/Levels/AssetPipeline/SceneTests/SceneTests.prefab

@@ -0,0 +1,117 @@
+{
+    "ContainerEntity": {
+        "Id": "Entity_[1146574390643]",
+        "Name": "Level",
+        "Components": {
+            "Component_[10641544592923449938]": {
+                "$type": "EditorInspectorComponent",
+                "Id": 10641544592923449938
+            },
+            "Component_[12039882709170782873]": {
+                "$type": "EditorOnlyEntityComponent",
+                "Id": 12039882709170782873
+            },
+            "Component_[12265484671603697631]": {
+                "$type": "EditorPendingCompositionComponent",
+                "Id": 12265484671603697631
+            },
+            "Component_[12525693718544601521]": {
+                "$type": "LocalViewBookmarkComponent",
+                "Id": 12525693718544601521,
+                "LocalBookmarkFileName": "SceneTests_734920582163.setreg"
+            },
+            "Component_[14126657869720434043]": {
+                "$type": "EditorEntitySortComponent",
+                "Id": 14126657869720434043,
+                "Child Entity Order": [
+                    "Entity_[475748213635]"
+                ]
+            },
+            "Component_[15230859088967841193]": {
+                "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
+                "Id": 15230859088967841193,
+                "Parent Entity": ""
+            },
+            "Component_[16239496886950819870]": {
+                "$type": "EditorDisabledCompositionComponent",
+                "Id": 16239496886950819870
+            },
+            "Component_[5688118765544765547]": {
+                "$type": "EditorEntityIconComponent",
+                "Id": 5688118765544765547
+            },
+            "Component_[7247035804068349658]": {
+                "$type": "EditorPrefabComponent",
+                "Id": 7247035804068349658
+            },
+            "Component_[9307224322037797205]": {
+                "$type": "EditorLockComponent",
+                "Id": 9307224322037797205
+            },
+            "Component_[9562516168917670048]": {
+                "$type": "EditorVisibilityComponent",
+                "Id": 9562516168917670048
+            }
+        }
+    },
+    "Entities": {
+        "Entity_[475748213635]": {
+            "Id": "Entity_[475748213635]",
+            "Name": "Mesh",
+            "Components": {
+                "Component_[10011348966764081830]": {
+                    "$type": "EditorInspectorComponent",
+                    "Id": 10011348966764081830
+                },
+                "Component_[10262971955851394590]": {
+                    "$type": "EditorPendingCompositionComponent",
+                    "Id": 10262971955851394590
+                },
+                "Component_[11969282660788213627]": {
+                    "$type": "EditorOnlyEntityComponent",
+                    "Id": 11969282660788213627
+                },
+                "Component_[12067543803579208206]": {
+                    "$type": "EditorDisabledCompositionComponent",
+                    "Id": 12067543803579208206
+                },
+                "Component_[12384986108013432265]": {
+                    "$type": "AZ::Render::EditorMeshComponent",
+                    "Id": 12384986108013432265,
+                    "Controller": {
+                        "Configuration": {
+                            "ModelAsset": {
+                                "assetId": {
+                                    "guid": "{AA31FE94-4C6A-5355-B040-C1795BAED142}",
+                                    "subId": 270539921
+                                },
+                                "assetHint": "objects/shaderball_simple.azmodel"
+                            }
+                        }
+                    }
+                },
+                "Component_[1413574472083822157]": {
+                    "$type": "EditorEntityIconComponent",
+                    "Id": 1413574472083822157
+                },
+                "Component_[15884790472628278846]": {
+                    "$type": "EditorEntitySortComponent",
+                    "Id": 15884790472628278846
+                },
+                "Component_[497350952225718964]": {
+                    "$type": "EditorLockComponent",
+                    "Id": 497350952225718964
+                },
+                "Component_[515018632742364507]": {
+                    "$type": "EditorVisibilityComponent",
+                    "Id": 515018632742364507
+                },
+                "Component_[8761931983484708703]": {
+                    "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
+                    "Id": 8761931983484708703,
+                    "Parent Entity": "Entity_[1146574390643]"
+                }
+            }
+        }
+    }
+}

+ 122 - 0
AutomatedTesting/Objects/ShaderBall_simple/shaderball_simple_MeshChange_SameID.fbx.assetinfo

@@ -0,0 +1,122 @@
+{
+    "values": [
+        {
+            "$type": "{5B03C8E6-8CEE-4DA0-A7FA-CD88689DD45B} MeshGroup",
+            "id": "{A2C9B60B-5532-5D8B-8822-0E6CF19B4FBC}",
+            "name": "ShaderBall_simple",
+            "NodeSelectionList": {
+                "unselectedNodes": [
+                    "RootNode",
+                    "RootNode.ShaderBall",
+                    "RootNode.ShaderBall.custom_properties",
+                    "RootNode.ShaderBall.Material_Prev",
+                    "RootNode.ShaderBall.inner_shaderball",
+                    "RootNode.ShaderBall.Material_Prev.custom_properties",
+                    "RootNode.ShaderBall.Material_Prev.Cushion",
+                    "RootNode.ShaderBall.Material_Prev.MaterialBaseInside",
+                    "RootNode.ShaderBall.Material_Prev.GrayInlay",
+                    "RootNode.ShaderBall.Material_Prev.MaterialBase",
+                    "RootNode.ShaderBall.Material_Prev.MaterialSphere",
+                    "RootNode.ShaderBall.inner_shaderball.custom_properties",
+                    "RootNode.ShaderBall.inner_shaderball.sphere",
+                    "RootNode.ShaderBall.inner_shaderball.subsphere",
+                    "RootNode.ShaderBall.Material_Prev.Cushion.transform",
+                    "RootNode.ShaderBall.Material_Prev.Cushion.custom_properties",
+                    "RootNode.ShaderBall.Material_Prev.Cushion.UVW",
+                    "RootNode.ShaderBall.Material_Prev.Cushion.phong_0",
+                    "RootNode.ShaderBall.Material_Prev.MaterialBaseInside.transform",
+                    "RootNode.ShaderBall.Material_Prev.MaterialBaseInside.custom_properties",
+                    "RootNode.ShaderBall.Material_Prev.MaterialBaseInside.UVW",
+                    "RootNode.ShaderBall.Material_Prev.MaterialBaseInside.phong_0",
+                    "RootNode.ShaderBall.Material_Prev.GrayInlay.transform",
+                    "RootNode.ShaderBall.Material_Prev.GrayInlay.custom_properties",
+                    "RootNode.ShaderBall.Material_Prev.GrayInlay.UVW",
+                    "RootNode.ShaderBall.Material_Prev.GrayInlay.phong_0",
+                    "RootNode.ShaderBall.Material_Prev.MaterialBase.transform",
+                    "RootNode.ShaderBall.Material_Prev.MaterialBase.custom_properties",
+                    "RootNode.ShaderBall.Material_Prev.MaterialBase.UVW",
+                    "RootNode.ShaderBall.Material_Prev.MaterialBase.phong_0",
+                    "RootNode.ShaderBall.Material_Prev.MaterialSphere.transform",
+                    "RootNode.ShaderBall.Material_Prev.MaterialSphere.custom_properties",
+                    "RootNode.ShaderBall.Material_Prev.MaterialSphere.UVW",
+                    "RootNode.ShaderBall.Material_Prev.MaterialSphere.phong_0",
+                    "RootNode.ShaderBall.inner_shaderball.sphere.transform",
+                    "RootNode.ShaderBall.inner_shaderball.sphere.custom_properties",
+                    "RootNode.ShaderBall.inner_shaderball.sphere.UVW",
+                    "RootNode.ShaderBall.inner_shaderball.sphere.phong_0",
+                    "RootNode.ShaderBall.inner_shaderball.subsphere.transform",
+                    "RootNode.ShaderBall.inner_shaderball.subsphere.custom_properties",
+                    "RootNode.ShaderBall.inner_shaderball.subsphere.UVW",
+                    "RootNode.ShaderBall.inner_shaderball.subsphere.phong_0"
+                ]
+            },
+            "PhysicsMaterialSlots": {
+                "Slots": [
+                    {
+                        "Name": "Entire object"
+                    }
+                ]
+            }
+        },
+        {
+            "$type": "{07B356B7-3635-40B5-878A-FAC4EFD5AD86} MeshGroup",
+            "name": "ShaderBall_simple",
+            "nodeSelectionList": {
+                "selectedNodes": [
+                    "RootNode",
+                    "RootNode.ShaderBall",
+                    "RootNode.ShaderBall.custom_properties",
+                    "RootNode.ShaderBall.inner_shaderball",
+                    "RootNode.ShaderBall.inner_shaderball.custom_properties",
+                    "RootNode.ShaderBall.inner_shaderball.sphere",
+                    "RootNode.ShaderBall.inner_shaderball.subsphere",
+                    "RootNode.ShaderBall.inner_shaderball.sphere.transform",
+                    "RootNode.ShaderBall.inner_shaderball.sphere.custom_properties",
+                    "RootNode.ShaderBall.inner_shaderball.sphere.UVW",
+                    "RootNode.ShaderBall.inner_shaderball.sphere.phong_0",
+                    "RootNode.ShaderBall.inner_shaderball.subsphere.transform",
+                    "RootNode.ShaderBall.inner_shaderball.subsphere.custom_properties",
+                    "RootNode.ShaderBall.inner_shaderball.subsphere.UVW",
+                    "RootNode.ShaderBall.inner_shaderball.subsphere.phong_0"
+                ],
+                "unselectedNodes": [
+                    "RootNode.ShaderBall.Material_Prev",
+                    "RootNode.ShaderBall.Material_Prev.custom_properties",
+                    "RootNode.ShaderBall.Material_Prev.Cushion",
+                    "RootNode.ShaderBall.Material_Prev.MaterialBaseInside",
+                    "RootNode.ShaderBall.Material_Prev.GrayInlay",
+                    "RootNode.ShaderBall.Material_Prev.MaterialBase",
+                    "RootNode.ShaderBall.Material_Prev.MaterialSphere",
+                    "RootNode.ShaderBall.Material_Prev.Cushion.transform",
+                    "RootNode.ShaderBall.Material_Prev.Cushion.custom_properties",
+                    "RootNode.ShaderBall.Material_Prev.Cushion.UVW",
+                    "RootNode.ShaderBall.Material_Prev.Cushion.phong_0",
+                    "RootNode.ShaderBall.Material_Prev.MaterialBaseInside.transform",
+                    "RootNode.ShaderBall.Material_Prev.MaterialBaseInside.custom_properties",
+                    "RootNode.ShaderBall.Material_Prev.MaterialBaseInside.UVW",
+                    "RootNode.ShaderBall.Material_Prev.MaterialBaseInside.phong_0",
+                    "RootNode.ShaderBall.Material_Prev.GrayInlay.transform",
+                    "RootNode.ShaderBall.Material_Prev.GrayInlay.custom_properties",
+                    "RootNode.ShaderBall.Material_Prev.GrayInlay.UVW",
+                    "RootNode.ShaderBall.Material_Prev.GrayInlay.phong_0",
+                    "RootNode.ShaderBall.Material_Prev.MaterialBase.transform",
+                    "RootNode.ShaderBall.Material_Prev.MaterialBase.custom_properties",
+                    "RootNode.ShaderBall.Material_Prev.MaterialBase.UVW",
+                    "RootNode.ShaderBall.Material_Prev.MaterialBase.phong_0",
+                    "RootNode.ShaderBall.Material_Prev.MaterialSphere.transform",
+                    "RootNode.ShaderBall.Material_Prev.MaterialSphere.custom_properties",
+                    "RootNode.ShaderBall.Material_Prev.MaterialSphere.UVW",
+                    "RootNode.ShaderBall.Material_Prev.MaterialSphere.phong_0"
+                ]
+            },
+            "rules": {
+                "rules": [
+                    {
+                        "$type": "MaterialRule"
+                    }
+                ]
+            },
+            "id": "{861A8967-CEFA-5EC6-A257-C6EBB3CB6564}"
+        }
+    ]
+}

+ 32 - 0
AutomatedTesting/Objects/ShaderBall_simple/shaderball_simple_NoMesh_NoID.fbx.assetinfo

@@ -0,0 +1,32 @@
+{
+    "values": [
+        {
+            "$type": "{5B03C8E6-8CEE-4DA0-A7FA-CD88689DD45B} MeshGroup",
+            "id": "{A2C9B60B-5532-5D8B-8822-0E6CF19B4FBC}",
+            "name": "shaderball_simple",
+            "NodeSelectionList": {
+                "unselectedNodes": [
+                    {},
+                    "RootNode",
+                    "RootNode.ShaderBall",
+                    "RootNode.ShaderBall.Material_Prev",
+                    "RootNode.ShaderBall.inner_shaderball",
+                    "RootNode.ShaderBall.Material_Prev.Cushion",
+                    "RootNode.ShaderBall.Material_Prev.MaterialBaseInside",
+                    "RootNode.ShaderBall.Material_Prev.GrayInlay",
+                    "RootNode.ShaderBall.Material_Prev.MaterialBase",
+                    "RootNode.ShaderBall.Material_Prev.MaterialSphere",
+                    "RootNode.ShaderBall.inner_shaderball.sphere",
+                    "RootNode.ShaderBall.inner_shaderball.subsphere"
+                ]
+            },
+            "PhysicsMaterialSlots": {
+                "Slots": [
+                    {
+                        "Name": "Entire object"
+                    }
+                ]
+            }
+        }
+    ]
+}