فهرست منبع

{lyn14000} re-insert script processing rule logic (#11336)

* re-insert logic, first pass

Signed-off-by: Allen Jackson <[email protected]>

* fix up the test script

Signed-off-by: Allen Jackson <[email protected]>

* added an automated test for the script re-insert feature

Signed-off-by: Allen Jackson <[email protected]>

* removed comments

Signed-off-by: Allen Jackson <[email protected]>

* added comments to the test in Python Asset Builder Tests.py

Signed-off-by: Allen Jackson <[email protected]>

* updating test for scene manifest (activate/deactivate)

Signed-off-by: Allen Jackson <[email protected]>

Signed-off-by: Allen Jackson <[email protected]>
Allen Jackson 3 سال پیش
والد
کامیت
a91f93eb64

+ 3 - 0
AutomatedTesting/Assets/ap_test_assets/script_reinsert.fbx

@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1d4d3301fa9795fea660f5e04051a6335988510ca04add753bb17784a3a3a18b
+size 11756

+ 8 - 0
AutomatedTesting/Assets/ap_test_assets/script_reinsert.fbx.assetinfo

@@ -0,0 +1,8 @@
+{
+    "values": [
+        {
+            "$type": "ScriptProcessorRule",
+            "scriptFilename": "Assets/ap_test_assets/script_reinsert.py"
+        }
+    ]
+}

+ 78 - 0
AutomatedTesting/Assets/ap_test_assets/script_reinsert.py

@@ -0,0 +1,78 @@
+#
+# 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
+#
+#
+
+sceneJobHandler = None
+
+def clear_sceneJobHandler():
+    global sceneJobHandler
+    sceneJobHandler.disconnect()
+    sceneJobHandler = None
+
+def on_prepare_for_export(args): 
+    print (f'on_prepare_for_export')
+
+    import azlmbr.scene
+    import azlmbr.object
+    import azlmbr.paths
+    import json, os
+    import azlmbr.math
+
+    scene = args[0] # azlmbr.scene.Scene
+    outputDirectory = args[1] # string
+
+    # write out a fake export product asset so that it can be found and tested
+    jsonFilename = os.path.basename(scene.sourceFilename)
+    jsonFilename = os.path.join(outputDirectory, jsonFilename + '.fake_asset')
+
+    # prepare output folder
+    basePath, _ = os.path.split(jsonFilename)
+    outputPath = os.path.join(outputDirectory, basePath)
+    if not os.path.exists(outputPath):
+        os.makedirs(outputPath, False)
+
+    # write out a JSON file
+    with open(jsonFilename, "w") as jsonFile:
+        jsonFile.write("{}")
+
+    clear_sceneJobHandler()
+
+    exportProduct = azlmbr.scene.ExportProduct()
+    exportProduct.filename = jsonFilename
+    exportProduct.sourceId = scene.sourceGuid
+    exportProduct.subId = 2
+
+    exportProductList = azlmbr.scene.ExportProductList()
+    exportProductList.AddProduct(exportProduct)
+    return exportProductList
+
+def on_update_manifest(args):
+    print (f'on_update_manifest')
+    data = """{
+        "values": [
+            {
+                "$type": "{07B356B7-3635-40B5-878A-FAC4EFD5AD86} MeshGroup",
+                "name": "fake",
+                "nodeSelectionList": { "selectedNodes": ["fake_node"] }
+            }
+        ]
+    }"""
+    clear_sceneJobHandler()
+    return data
+
+# try to create SceneAPI handler for processing
+try:
+    import azlmbr.scene as sceneApi
+    
+    if sceneJobHandler is None:
+        sceneJobHandler = sceneApi.ScriptBuildingNotificationBusHandler()
+        sceneJobHandler.connect()
+        sceneJobHandler.add_callback('OnUpdateManifest', on_update_manifest)
+        sceneJobHandler.add_callback('OnPrepareForExport', on_prepare_for_export)
+
+except:
+    sceneJobHandler = None

+ 78 - 0
AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/script_basics/base_example.py

@@ -0,0 +1,78 @@
+#
+# 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
+#
+#
+
+sceneJobHandler = None
+
+def clear_sceneJobHandler():
+    global sceneJobHandler
+    sceneJobHandler.disconnect()
+    sceneJobHandler = None
+
+def on_prepare_for_export(args): 
+    print (f'on_prepare_for_export')
+
+    import azlmbr.scene
+    import azlmbr.object
+    import azlmbr.paths
+    import json, os
+    import azlmbr.math
+
+    scene = args[0] # azlmbr.scene.Scene
+    outputDirectory = args[1] # string
+
+    # write out a fake export product asset so that it can be found and tested
+    jsonFilename = os.path.basename(scene.sourceFilename)
+    jsonFilename = os.path.join(outputDirectory, jsonFilename + '.fake_asset')
+
+    # prepare output folder
+    basePath, _ = os.path.split(jsonFilename)
+    outputPath = os.path.join(outputDirectory, basePath)
+    if not os.path.exists(outputPath):
+        os.makedirs(outputPath, False)
+
+    # write out a JSON file
+    with open(jsonFilename, "w") as jsonFile:
+        jsonFile.write("{}")
+
+    clear_sceneJobHandler()
+
+    exportProduct = azlmbr.scene.ExportProduct()
+    exportProduct.filename = jsonFilename
+    exportProduct.sourceId = scene.sourceGuid
+    exportProduct.subId = 2
+
+    exportProductList = azlmbr.scene.ExportProductList()
+    exportProductList.AddProduct(exportProduct)
+    return exportProductList
+
+def on_update_manifest(args):
+    print (f'on_update_manifest')
+    data = """{
+        "values": [
+            {
+                "$type": "{07B356B7-3635-40B5-878A-FAC4EFD5AD86} MeshGroup",
+                "name": "fake",
+                "nodeSelectionList": { "selectedNodes": ["fake_node"] }
+            }
+        ]
+    }"""
+    clear_sceneJobHandler()
+    return data
+
+# try to create SceneAPI handler for processing
+try:
+    import azlmbr.scene as sceneApi
+    
+    if sceneJobHandler is None:
+        sceneJobHandler = sceneApi.ScriptBuildingNotificationBusHandler()
+        sceneJobHandler.connect()
+        sceneJobHandler.add_callback('OnUpdateManifest', on_update_manifest)
+        sceneJobHandler.add_callback('OnPrepareForExport', on_prepare_for_export)
+
+except:
+    sceneJobHandler = None

+ 3 - 0
AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/script_basics/simple.fbx

@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1d4d3301fa9795fea660f5e04051a6335988510ca04add753bb17784a3a3a18b
+size 11756

+ 8 - 0
AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/script_basics/simple.fbx.assetinfo

@@ -0,0 +1,8 @@
+{
+    "values": [
+      {
+        "$type": "ScriptProcessorRule",
+        "scriptFilename": "script_basics/base_example.py"
+      }
+    ]
+}

+ 18 - 0
AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/pythonassetbuildertests.py

@@ -195,3 +195,21 @@ class TestsPythonAssetProcessing_APBatch(object):
                     assert current_set == set(unselected_node_list), "Unselected node list does not contain the correct meshes"
                     assert current_set == set(unselected_node_list), "Unselected node list does not contain the correct meshes"
                     found_mesh_nodes.add(selected_node_list[0])
                     found_mesh_nodes.add(selected_node_list[0])
             assert found_mesh_nodes == expected_mesh_nodes, "Not all expected mesh groups found"
             assert found_mesh_nodes == expected_mesh_nodes, "Not all expected mesh groups found"
+
+    def test_ProcessAssetScript_SimpleFbx_HasExpectedProducts(self, workspace, ap_setup_fixture, asset_processor):
+        # This tests all of the entry points a scene builder can script:
+        # - OnUpdateManifest() returns a JSON string to use as the scene manifest; this must succeed to continue the scene pipe
+        # - OnPrepareForExport() returns an export product list; this test should produce a simple.fbx.fake_asset export product
+
+        asset_processor.prepare_test_environment(ap_setup_fixture["tests_dir"], "script_basics")
+
+        result, _ = asset_processor.batch_process(extra_params=self.asset_processor_extra_params)
+        assert result, "AP Batch failed"
+
+        expected_product_list = [
+            "simple.fbx.fake_asset"
+        ]
+
+        missing_assets, _ = utils.compare_assets_with_cache(expected_product_list, asset_processor.project_test_cache_folder())
+        assert not missing_assets, f'The following assets were expected to be in, but not found in cache: {str(missing_assets)}'
+

+ 29 - 6
Code/Tools/SceneAPI/SceneData/Behaviors/ScriptProcessorRuleBehavior.cpp

@@ -28,6 +28,7 @@
 #include <SceneAPI/SceneCore/Utilities/Reporting.h>
 #include <SceneAPI/SceneCore/Utilities/Reporting.h>
 #include <SceneAPI/SceneCore/Events/ExportProductList.h>
 #include <SceneAPI/SceneCore/Events/ExportProductList.h>
 #include <SceneAPI/SceneCore/Events/AssetImportRequest.h>
 #include <SceneAPI/SceneCore/Events/AssetImportRequest.h>
+#include <SceneAPI/SceneCore/Events/ImportEventContext.h>
 
 
 namespace AZ::SceneAPI::Behaviors
 namespace AZ::SceneAPI::Behaviors
 {
 {
@@ -180,20 +181,21 @@ namespace AZ::SceneAPI::Behaviors
         };
         };
     };
     };
 
 
-    struct ScriptProcessorRuleBehavior::ExportEventHandler final
+    struct ScriptProcessorRuleBehavior::EventHandler final
         : public AZ::SceneAPI::SceneCore::ExportingComponent
         : public AZ::SceneAPI::SceneCore::ExportingComponent
     {
     {
         using PreExportEventContextFunction = AZStd::function<bool(Events::PreExportEventContext&)>;
         using PreExportEventContextFunction = AZStd::function<bool(Events::PreExportEventContext&)>;
         PreExportEventContextFunction m_preExportEventContextFunction;
         PreExportEventContextFunction m_preExportEventContextFunction;
 
 
-        ExportEventHandler(PreExportEventContextFunction preExportEventContextFunction)
+        EventHandler(PreExportEventContextFunction preExportEventContextFunction)
             : m_preExportEventContextFunction(preExportEventContextFunction)
             : m_preExportEventContextFunction(preExportEventContextFunction)
         {
         {
-            BindToCall(&ExportEventHandler::PrepareForExport);
+            BindToCall(&EventHandler::PrepareForExport);
+            BindToCall(&EventHandler::PreImportEventContext);
             AZ::SceneAPI::SceneCore::ExportingComponent::Activate();
             AZ::SceneAPI::SceneCore::ExportingComponent::Activate();
         }
         }
 
 
-        ~ExportEventHandler()
+        ~EventHandler()
         {
         {
             AZ::SceneAPI::SceneCore::ExportingComponent::Deactivate();
             AZ::SceneAPI::SceneCore::ExportingComponent::Deactivate();
         }
         }
@@ -203,12 +205,21 @@ namespace AZ::SceneAPI::Behaviors
         {
         {
             return m_preExportEventContextFunction(context) ? Events::ProcessingResult::Success : Events::ProcessingResult::Failure;
             return m_preExportEventContextFunction(context) ? Events::ProcessingResult::Success : Events::ProcessingResult::Failure;
         }
         }
+
+        // used to detect that the "next" source scene is starting to be processed
+        Events::ProcessingResult PreImportEventContext([[maybe_unused]] Events::PreImportEventContext& context)
+        {
+            m_pythonScriptStack.clear();
+            return Events::ProcessingResult::Success;
+        }
+
+        AZStd::vector<AZStd::string> m_pythonScriptStack;
     };
     };
 
 
     void ScriptProcessorRuleBehavior::Activate()
     void ScriptProcessorRuleBehavior::Activate()
     {
     {
         Events::AssetImportRequestBus::Handler::BusConnect();
         Events::AssetImportRequestBus::Handler::BusConnect();
-        m_exportEventHandler = AZStd::make_shared<ExportEventHandler>([this](Events::PreExportEventContext& context)
+        m_eventHandler = AZStd::make_shared<EventHandler>([this](Events::PreExportEventContext& context)
         {
         {
             return this->DoPrepareForExport(context);
             return this->DoPrepareForExport(context);
         });
         });
@@ -216,7 +227,7 @@ namespace AZ::SceneAPI::Behaviors
 
 
     void ScriptProcessorRuleBehavior::Deactivate()
     void ScriptProcessorRuleBehavior::Deactivate()
     {
     {
-        m_exportEventHandler.reset();
+        m_eventHandler.reset();
         Events::AssetImportRequestBus::Handler::BusDisconnect();
         Events::AssetImportRequestBus::Handler::BusDisconnect();
         UnloadPython();
         UnloadPython();
     }
     }
@@ -271,6 +282,14 @@ namespace AZ::SceneAPI::Behaviors
             break;
             break;
         }
         }
 
 
+        if (scriptDiscoveryAttempts == 0)
+        {
+            if (!m_eventHandler->m_pythonScriptStack.empty())
+            {
+                scriptPath = m_eventHandler->m_pythonScriptStack.back();
+            }
+        }
+
         if (scriptPath.empty())
         if (scriptPath.empty())
         {
         {
             AZ_Warning("scene", scriptDiscoveryAttempts == 0,
             AZ_Warning("scene", scriptDiscoveryAttempts == 0,
@@ -278,6 +297,10 @@ namespace AZ::SceneAPI::Behaviors
                 scene.GetManifestFilename().c_str());
                 scene.GetManifestFilename().c_str());
             return false;
             return false;
         }
         }
+        else
+        {
+            m_eventHandler->m_pythonScriptStack.push_back(scriptPath);
+        }
 
 
         // already prepared the Python VM?
         // already prepared the Python VM?
         if (m_editorPythonEventsInterface)
         if (m_editorPythonEventsInterface)

+ 2 - 2
Code/Tools/SceneAPI/SceneData/Behaviors/ScriptProcessorRuleBehavior.h

@@ -61,7 +61,7 @@ namespace AZ::SceneAPI::Behaviors
     private:
     private:
         AzToolsFramework::EditorPythonEventsInterface* m_editorPythonEventsInterface = nullptr;
         AzToolsFramework::EditorPythonEventsInterface* m_editorPythonEventsInterface = nullptr;
 
 
-        struct ExportEventHandler;
-        AZStd::shared_ptr<ExportEventHandler> m_exportEventHandler;
+        struct EventHandler;
+        AZStd::shared_ptr<EventHandler> m_eventHandler;
     };
     };
 } // namespace AZ::SceneAPI::Behaviors
 } // namespace AZ::SceneAPI::Behaviors

+ 2 - 0
Code/Tools/SceneAPI/SceneData/Tests/SceneManifest/SceneManifestRuleTests.cpp

@@ -269,7 +269,9 @@ namespace AZ
             EXPECT_FALSE(scene.GetManifest().IsEmpty());
             EXPECT_FALSE(scene.GetManifest().IsEmpty());
 
 
             auto scriptProcessorRuleBehavior =  AZ::SceneAPI::Behaviors::ScriptProcessorRuleBehavior();
             auto scriptProcessorRuleBehavior =  AZ::SceneAPI::Behaviors::ScriptProcessorRuleBehavior();
+            scriptProcessorRuleBehavior.Activate();
             auto update = scriptProcessorRuleBehavior.UpdateManifest(scene, AssetImportRequest::Update, AssetImportRequest::Generic);
             auto update = scriptProcessorRuleBehavior.UpdateManifest(scene, AssetImportRequest::Update, AssetImportRequest::Generic);
+            scriptProcessorRuleBehavior.Deactivate();
             EXPECT_EQ(update, ProcessingResult::Ignored);
             EXPECT_EQ(update, ProcessingResult::Ignored);
         }
         }