{LYN-4514} Engine updates to enable PAB for the Blast Gem (#2140)

* {LYN-4514} Engine updates to enable PAB for the Blast Gem

* Engine updates to enable Python Asset Building for the Blast Gem
* added API to detect IsPythonActive()
* ExportProductList behavior
* scene manifest usage of generated assetinfo
* updated ScriptProcessorRuleBehavior to handle OnPrepareForExport

Tests: new tests for scene behavior via ExportProduct

Signed-off-by: Jackson <23512001+jackalbe@users.noreply.github.com>

* updated base on PR feedback
added more comments for IsPythonActive()
added a serializeContext for export product list

Signed-off-by: Jackson <23512001+jackalbe@users.noreply.github.com>
monroegm-disable-blank-issue-2
jackalbe 4 years ago committed by GitHub
parent 8f706c09d6
commit ffcfa44b49
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -116,7 +116,7 @@ namespace AZ
{ {
const char* uuidString = nullptr; const char* uuidString = nullptr;
unsigned int uuidStringLength = 0; unsigned int uuidStringLength = 0;
if (dc.ReadArg(0, uuidString) && dc.ReadValue(1, uuidStringLength)) if (dc.ReadArg(0, uuidString) && dc.ReadArg(1, uuidStringLength))
{ {
dc.PushResult(Uuid(uuidString, uuidStringLength)); dc.PushResult(Uuid(uuidString, uuidStringLength));
} }

@ -63,6 +63,9 @@ namespace AzToolsFramework
//! Signal the Python handler to stop //! Signal the Python handler to stop
virtual bool StopPython(bool silenceWarnings = false) = 0; virtual bool StopPython(bool silenceWarnings = false) = 0;
//! Query to determine if the Python VM has been initialized indicating an active state
virtual bool IsPythonActive() = 0;
//! Determines if the caller needs to wait for the Python VM to initialize (non-main thread only) //! Determines if the caller needs to wait for the Python VM to initialize (non-main thread only)
virtual void WaitForInitialization() {} virtual void WaitForInitialization() {}

@ -7,6 +7,8 @@
#include <AzCore/Serialization/SerializeContext.h> #include <AzCore/Serialization/SerializeContext.h>
#include <SceneAPI/SceneCore/Components/ExportingComponent.h> #include <SceneAPI/SceneCore/Components/ExportingComponent.h>
#include <SceneAPI/SceneCore/Events/ExportProductList.h>
#include <AzCore/RTTI/BehaviorContext.h>
namespace AZ namespace AZ
{ {
@ -31,6 +33,12 @@ namespace AZ
{ {
serializeContext->Class<ExportingComponent, AZ::Component>()->Version(2); serializeContext->Class<ExportingComponent, AZ::Component>()->Version(2);
} }
AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context);
if (behaviorContext)
{
Events::ExportProductList::Reflect(behaviorContext);
}
} }
} // namespace SceneCore } // namespace SceneCore
} // namespace SceneAPI } // namespace SceneAPI

@ -211,6 +211,7 @@ namespace AZ
AZ::SceneAPI::Containers::SceneGraph::Reflect(context); AZ::SceneAPI::Containers::SceneGraph::Reflect(context);
AZ::SceneAPI::Containers::SceneManifest::Reflect(context); AZ::SceneAPI::Containers::SceneManifest::Reflect(context);
AZ::SceneAPI::Containers::RuleContainer::Reflect(context); AZ::SceneAPI::Containers::RuleContainer::Reflect(context);
AZ::SceneAPI::SceneCore::ExportingComponent::Reflect(context);
} }
void Activate() void Activate()

@ -6,6 +6,8 @@
*/ */
#include <SceneAPI/SceneCore/Events/ExportProductList.h> #include <SceneAPI/SceneCore/Events/ExportProductList.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/std/limits.h>
namespace AZ namespace AZ
{ {
@ -49,6 +51,45 @@ namespace AZ
return *this; return *this;
} }
void ExportProductList::Reflect(ReflectContext* context)
{
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<ExportProduct>()->Version(1);
serializeContext->Class<ExportProductList>()->Version(1);
}
if (auto* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->Class<ExportProduct>("ExportProduct")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Module, "scene")
->Property("filename", BehaviorValueProperty(&ExportProduct::m_filename))
->Property("sourceId", BehaviorValueProperty(&ExportProduct::m_id))
->Property("assetType", BehaviorValueProperty(&ExportProduct::m_assetType))
->Property("productDependencies", BehaviorValueProperty(&ExportProduct::m_productDependencies))
->Property("subId",
[](ExportProduct* self) { return self->m_subId.has_value() ? self->m_subId.value() : 0; },
[](ExportProduct* self, u32 subId) { self->m_subId = AZStd::optional<u32>(subId); });
behaviorContext->Class<ExportProductList>("ExportProductList")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Module, "scene")
->Method("AddProduct", [](ExportProductList& self, ExportProduct& product)
{
self.AddProduct(
product.m_filename,
product.m_id,
product.m_assetType,
product.m_lod,
product.m_subId,
product.m_dependencyFlags);
})
->Method("GetProducts", &ExportProductList::GetProducts)
->Method("AddDependencyToProduct", &ExportProductList::AddDependencyToProduct);
}
}
ExportProduct& ExportProductList::AddProduct(const AZStd::string& filename, Uuid id, Data::AssetType assetType, AZStd::optional<u8> lod, AZStd::optional<u32> subId, ExportProduct& ExportProductList::AddProduct(const AZStd::string& filename, Uuid id, Data::AssetType assetType, AZStd::optional<u8> lod, AZStd::optional<u32> subId,
Data::ProductDependencyInfo::ProductDependencyFlags dependencyFlags) Data::ProductDependencyInfo::ProductDependencyFlags dependencyFlags)
{ {

@ -14,6 +14,8 @@
namespace AZ namespace AZ
{ {
class ReflectContext;
namespace SceneAPI namespace SceneAPI
{ {
namespace Events namespace Events
@ -24,6 +26,7 @@ namespace AZ
Data::ProductDependencyInfo::ProductDependencyFlags dependencyFlags = Data::ProductDependencyInfo::CreateFlags(Data::AssetLoadBehavior::NoLoad)); Data::ProductDependencyInfo::ProductDependencyFlags dependencyFlags = Data::ProductDependencyInfo::CreateFlags(Data::AssetLoadBehavior::NoLoad));
SCENE_CORE_API ExportProduct(AZStd::string&& filename, Uuid id, Data::AssetType assetType, AZStd::optional<u8> lod, AZStd::optional<u32> subId, SCENE_CORE_API ExportProduct(AZStd::string&& filename, Uuid id, Data::AssetType assetType, AZStd::optional<u8> lod, AZStd::optional<u32> subId,
Data::ProductDependencyInfo::ProductDependencyFlags dependencyFlags = Data::ProductDependencyInfo::CreateFlags(Data::AssetLoadBehavior::NoLoad)); Data::ProductDependencyInfo::ProductDependencyFlags dependencyFlags = Data::ProductDependencyInfo::CreateFlags(Data::AssetLoadBehavior::NoLoad));
ExportProduct() = default;
ExportProduct(const ExportProduct& rhs) = default; ExportProduct(const ExportProduct& rhs) = default;
SCENE_CORE_API ExportProduct(ExportProduct&& rhs); SCENE_CORE_API ExportProduct(ExportProduct&& rhs);
@ -54,6 +57,8 @@ namespace AZ
class ExportProductList class ExportProductList
{ {
public: public:
static void Reflect(ReflectContext* context);
SCENE_CORE_API ExportProduct& AddProduct(const AZStd::string& filename, Uuid id, Data::AssetType assetType, AZStd::optional<u8> lod, AZStd::optional<u32> subId, SCENE_CORE_API ExportProduct& AddProduct(const AZStd::string& filename, Uuid id, Data::AssetType assetType, AZStd::optional<u8> lod, AZStd::optional<u32> subId,
Data::ProductDependencyInfo::ProductDependencyFlags dependencyFlags = Data::ProductDependencyInfo::CreateFlags(Data::AssetLoadBehavior::NoLoad)); Data::ProductDependencyInfo::ProductDependencyFlags dependencyFlags = Data::ProductDependencyInfo::CreateFlags(Data::AssetLoadBehavior::NoLoad));
SCENE_CORE_API ExportProduct& AddProduct(AZStd::string&& filename, Uuid id, Data::AssetType assetType, AZStd::optional<u8> lod, AZStd::optional<u32> subId, SCENE_CORE_API ExportProduct& AddProduct(AZStd::string&& filename, Uuid id, Data::AssetType assetType, AZStd::optional<u8> lod, AZStd::optional<u32> subId,
@ -69,3 +74,9 @@ namespace AZ
} // namespace Events } // namespace Events
} // namespace SceneAPI } // namespace SceneAPI
} // namespace AZ } // namespace AZ
namespace AZ
{
AZ_TYPE_INFO_SPECIALIZE(SceneAPI::Events::ExportProduct, "{6054EDCB-4C04-4D96-BF26-704999FFB725}");
AZ_TYPE_INFO_SPECIALIZE(SceneAPI::Events::ExportProductList, "{1C76A51F-431B-4987-B653-CFCC940D0D0F}");
}

@ -10,6 +10,7 @@
#include <AzCore/IO/SystemFile.h> #include <AzCore/IO/SystemFile.h>
#include <AzCore/IO/GenericStreams.h> #include <AzCore/IO/GenericStreams.h>
#include <AzCore/Memory/SystemAllocator.h> #include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Utils/Utils.h>
#include <AzFramework/API/ApplicationAPI.h> #include <AzFramework/API/ApplicationAPI.h>
#include <AzFramework/StringFunc/StringFunc.h> #include <AzFramework/StringFunc/StringFunc.h>
#include <SceneAPI/SceneCore/Import/ManifestImportRequestHandler.h> #include <SceneAPI/SceneCore/Import/ManifestImportRequestHandler.h>
@ -75,15 +76,16 @@ namespace AZ
filename += s_extension; filename += s_extension;
filename += s_generated; filename += s_generated;
AZStd::string altManifestPath = path; AZStd::string altManifestFolder = path;
AzFramework::ApplicationRequests::Bus::Broadcast( AzFramework::ApplicationRequests::Bus::Broadcast(
&AzFramework::ApplicationRequests::Bus::Events::MakePathRootRelative, &AzFramework::ApplicationRequests::Bus::Events::MakePathRelative,
altManifestPath); altManifestFolder,
AZ::Utils::GetProjectPath().c_str());
AZ::StringFunc::Path::GetFolderPath(altManifestPath.c_str(), altManifestPath); AZ::StringFunc::Path::GetFolderPath(altManifestFolder.c_str(), altManifestFolder);
AZStd::string generatedAssetInfoPath; AZStd::string generatedAssetInfoPath;
AZ::StringFunc::Path::Join(assetCacheRoot.c_str(), altManifestPath.c_str(), generatedAssetInfoPath); AZ::StringFunc::Path::Join(assetCacheRoot.c_str(), altManifestFolder.c_str(), generatedAssetInfoPath);
AZ::StringFunc::Path::ConstructFull(generatedAssetInfoPath.c_str(), filename.c_str(), generatedAssetInfoPath); AZ::StringFunc::Path::ConstructFull(generatedAssetInfoPath.c_str(), filename.c_str(), generatedAssetInfoPath);
if (!AZ::IO::FileIOBase::GetInstance()->Exists(generatedAssetInfoPath.c_str())) if (!AZ::IO::FileIOBase::GetInstance()->Exists(generatedAssetInfoPath.c_str()))

@ -25,213 +25,319 @@
#include <SceneAPI/SceneCore/Containers/Views/PairIterator.h> #include <SceneAPI/SceneCore/Containers/Views/PairIterator.h>
#include <SceneAPI/SceneData/Rules/ScriptProcessorRule.h> #include <SceneAPI/SceneData/Rules/ScriptProcessorRule.h>
#include <SceneAPI/SceneCore/Utilities/Reporting.h> #include <SceneAPI/SceneCore/Utilities/Reporting.h>
#include <SceneAPI/SceneCore/Events/ExportProductList.h>
namespace AZ namespace AZ::SceneAPI::Behaviors
{ {
namespace SceneAPI class EditorPythonConsoleNotificationHandler final
: protected AzToolsFramework::EditorPythonConsoleNotificationBus::Handler
{ {
namespace Behaviors public:
EditorPythonConsoleNotificationHandler()
{ {
class EditorPythonConsoleNotificationHandler final BusConnect();
: protected AzToolsFramework::EditorPythonConsoleNotificationBus::Handler }
{
public:
EditorPythonConsoleNotificationHandler()
{
BusConnect();
}
~EditorPythonConsoleNotificationHandler() ~EditorPythonConsoleNotificationHandler()
{ {
BusDisconnect(); BusDisconnect();
} }
//////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////
// AzToolsFramework::EditorPythonConsoleNotifications // AzToolsFramework::EditorPythonConsoleNotifications
void OnTraceMessage([[maybe_unused]] AZStd::string_view message) override void OnTraceMessage([[maybe_unused]] AZStd::string_view message) override
{ {
using namespace AZ::SceneAPI::Utilities; using namespace AZ::SceneAPI::Utilities;
AZ_TracePrintf(LogWindow, "%.*s \n", AZ_STRING_ARG(message)); AZ_TracePrintf(LogWindow, "%.*s \n", AZ_STRING_ARG(message));
} }
void OnErrorMessage([[maybe_unused]] AZStd::string_view message) override void OnErrorMessage([[maybe_unused]] AZStd::string_view message) override
{ {
using namespace AZ::SceneAPI::Utilities; using namespace AZ::SceneAPI::Utilities;
AZ_TracePrintf(ErrorWindow, "[ERROR] %.*s \n", AZ_STRING_ARG(message)); AZ_TracePrintf(ErrorWindow, "[ERROR] %.*s \n", AZ_STRING_ARG(message));
} }
void OnExceptionMessage([[maybe_unused]] AZStd::string_view message) override void OnExceptionMessage([[maybe_unused]] AZStd::string_view message) override
{ {
using namespace AZ::SceneAPI::Utilities; using namespace AZ::SceneAPI::Utilities;
AZ_TracePrintf(ErrorWindow, "[EXCEPTION] %.*s \n", AZ_STRING_ARG(message)); AZ_TracePrintf(ErrorWindow, "[EXCEPTION] %.*s \n", AZ_STRING_ARG(message));
} }
}; };
// a event bus to signal during scene building using ExportProductList = AZ::SceneAPI::Events::ExportProductList;
struct ScriptBuildingNotifications
: public AZ::EBusTraits // a event bus to signal during scene building
{ struct ScriptBuildingNotifications
virtual AZStd::string OnUpdateManifest(Containers::Scene& scene) = 0; : public AZ::EBusTraits
}; {
using ScriptBuildingNotificationBus = AZ::EBus<ScriptBuildingNotifications>; virtual AZStd::string OnUpdateManifest(Containers::Scene& scene) = 0;
virtual ExportProductList OnPrepareForExport(
const Containers::Scene& scene,
AZStd::string_view outputDirectory,
AZStd::string_view platformIdentifier,
const ExportProductList& productList) = 0;
};
using ScriptBuildingNotificationBus = AZ::EBus<ScriptBuildingNotifications>;
// a back end to handle scene builder events for a script
struct ScriptBuildingNotificationBusHandler final
: public ScriptBuildingNotificationBus::Handler
, public AZ::BehaviorEBusHandler
{
AZ_EBUS_BEHAVIOR_BINDER(
ScriptBuildingNotificationBusHandler,
"{DF2B51DE-A4D0-4139-B5D0-DF185832380D}",
AZ::SystemAllocator,
OnUpdateManifest,
OnPrepareForExport);
// a back end to handle scene builder events for a script virtual ~ScriptBuildingNotificationBusHandler() = default;
struct ScriptBuildingNotificationBusHandler final
: public ScriptBuildingNotificationBus::Handler AZStd::string OnUpdateManifest(Containers::Scene& scene) override
, public AZ::BehaviorEBusHandler {
AZStd::string result;
CallResult(result, FN_OnUpdateManifest, scene);
return result;
}
ExportProductList OnPrepareForExport(
const Containers::Scene& scene,
AZStd::string_view outputDirectory,
AZStd::string_view platformIdentifier,
const ExportProductList& productList) override
{
ExportProductList result;
CallResult(result, FN_OnPrepareForExport, scene, outputDirectory, platformIdentifier, productList);
return result;
}
static void Reflect(AZ::ReflectContext* context)
{
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{ {
AZ_EBUS_BEHAVIOR_BINDER( behaviorContext->EBus<ScriptBuildingNotificationBus>("ScriptBuildingNotificationBus")
ScriptBuildingNotificationBusHandler, ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
"{DF2B51DE-A4D0-4139-B5D0-DF185832380D}", ->Attribute(AZ::Script::Attributes::Module, "scene")
AZ::SystemAllocator, ->Handler<ScriptBuildingNotificationBusHandler>()
OnUpdateManifest); ->Event("OnUpdateManifest", &ScriptBuildingNotificationBus::Events::OnUpdateManifest)
->Event("OnPrepareForExport", &ScriptBuildingNotificationBus::Events::OnPrepareForExport);
}
}
};
virtual ~ScriptBuildingNotificationBusHandler() = default; struct ScriptProcessorRuleBehavior::ExportEventHandler final
: public AZ::SceneAPI::SceneCore::ExportingComponent
{
using PreExportEventContextFunction = AZStd::function<bool(Events::PreExportEventContext&)>;
PreExportEventContextFunction m_preExportEventContextFunction;
AZStd::string OnUpdateManifest(Containers::Scene& scene) override ExportEventHandler(PreExportEventContextFunction preExportEventContextFunction)
{ : m_preExportEventContextFunction(preExportEventContextFunction)
AZStd::string result; {
CallResult(result, FN_OnUpdateManifest, scene); BindToCall(&ExportEventHandler::PrepareForExport);
return result; AZ::SceneAPI::SceneCore::ExportingComponent::Activate();
} }
static void Reflect(AZ::ReflectContext* context) ~ExportEventHandler()
{ {
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context)) AZ::SceneAPI::SceneCore::ExportingComponent::Deactivate();
{ }
behaviorContext->EBus<ScriptBuildingNotificationBus>("ScriptBuildingNotificationBus")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation) // this allows a Python script to add product assets on "scene export"
->Attribute(AZ::Script::Attributes::Module, "scene") Events::ProcessingResult PrepareForExport(Events::PreExportEventContext& context)
->Handler<ScriptBuildingNotificationBusHandler>() {
->Event("OnUpdateManifest", &ScriptBuildingNotificationBus::Events::OnUpdateManifest); return m_preExportEventContextFunction(context) ? Events::ProcessingResult::Success : Events::ProcessingResult::Failure;
} }
} };
};
void ScriptProcessorRuleBehavior::Activate()
{
Events::AssetImportRequestBus::Handler::BusConnect();
m_exportEventHandler = AZStd::make_shared<ExportEventHandler>([this](Events::PreExportEventContext& context)
{
return this->DoPrepareForExport(context);
});
}
void ScriptProcessorRuleBehavior::Deactivate()
{
m_exportEventHandler.reset();
Events::AssetImportRequestBus::Handler::BusDisconnect();
UnloadPython();
}
bool ScriptProcessorRuleBehavior::LoadPython(const AZ::SceneAPI::Containers::Scene& scene)
{
if (m_editorPythonEventsInterface && !m_scriptFilename.empty())
{
return true;
}
// get project folder
auto settingsRegistry = AZ::SettingsRegistry::Get();
AZ::IO::FixedMaxPath projectPath;
if (!settingsRegistry->Get(projectPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectPath))
{
return false;
}
void ScriptProcessorRuleBehavior::Activate() const AZ::SceneAPI::Containers::SceneManifest& manifest = scene.GetManifest();
auto view = Containers::MakeDerivedFilterView<DataTypes::IScriptProcessorRule>(manifest.GetValueStorage());
for (const auto& scriptItem : view)
{
AZ::IO::FixedMaxPath scriptFilename(scriptItem.GetScriptFilename());
if (scriptFilename.empty())
{ {
Events::AssetImportRequestBus::Handler::BusConnect(); AZ_Warning("scene", false, "Skipping an empty script filename in (%s)", scene.GetManifestFilename().c_str());
continue;
} }
void ScriptProcessorRuleBehavior::Deactivate() // check for file exist via absolute path
if (!IO::FileIOBase::GetInstance()->Exists(scriptFilename.c_str()))
{ {
Events::AssetImportRequestBus::Handler::BusDisconnect(); // check for script in the project folder
if (m_editorPythonEventsInterface) AZ::IO::FixedMaxPath projectScriptPath = projectPath / scriptFilename;
if (!IO::FileIOBase::GetInstance()->Exists(projectScriptPath.c_str()))
{ {
const bool silenceWarnings = true; AZ_Warning("scene", false, "Skipping a missing script (%s) in manifest file (%s)",
m_editorPythonEventsInterface->StopPython(silenceWarnings); scriptFilename.c_str(),
m_editorPythonEventsInterface = nullptr; scene.GetManifestFilename().c_str());
continue;
} }
scriptFilename = AZStd::move(projectScriptPath);
} }
void ScriptProcessorRuleBehavior::Reflect(ReflectContext* context) // lazy load the Python interface
auto editorPythonEventsInterface = AZ::Interface<AzToolsFramework::EditorPythonEventsInterface>::Get();
if (editorPythonEventsInterface->IsPythonActive() == false)
{ {
ScriptBuildingNotificationBusHandler::Reflect(context); const bool silenceWarnings = false;
if (editorPythonEventsInterface->StartPython(silenceWarnings) == false)
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
if (serializeContext)
{ {
serializeContext->Class<ScriptProcessorRuleBehavior, BehaviorComponent>()->Version(1); editorPythonEventsInterface = nullptr;
} }
} }
Events::ProcessingResult ScriptProcessorRuleBehavior::UpdateManifest( // both Python and the script need to be ready
Containers::Scene& scene, if (editorPythonEventsInterface == nullptr || scriptFilename.empty())
Events::AssetImportRequest::ManifestAction action,
[[maybe_unused]] Events::AssetImportRequest::RequestingApplication requester)
{ {
using namespace AzToolsFramework; AZ_Warning("scene", false,"The scene manifest (%s) attempted to use script(%s) but Python is not enabled;"
"please add the EditorPythonBinding gem & PythonAssetBuilder gem to your project.",
scene.GetManifestFilename().c_str(), scriptFilename.c_str());
if (action != ManifestAction::Update) return false;
{ }
return Events::ProcessingResult::Ignored;
}
// get project folder m_editorPythonEventsInterface = editorPythonEventsInterface;
auto settingsRegistry = AZ::SettingsRegistry::Get(); m_scriptFilename = scriptFilename.c_str();
AZ::IO::FixedMaxPath projectPath; return true;
if (!settingsRegistry->Get(projectPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectPath)) }
{ return false;
return Events::ProcessingResult::Ignored; }
}
void ScriptProcessorRuleBehavior::UnloadPython()
{
if (m_editorPythonEventsInterface)
{
const bool silenceWarnings = true;
m_editorPythonEventsInterface->StopPython(silenceWarnings);
m_editorPythonEventsInterface = nullptr;
}
}
bool ScriptProcessorRuleBehavior::DoPrepareForExport(Events::PreExportEventContext& context)
{
using namespace AzToolsFramework;
auto executeCallback = [this, &context]()
{
// set up script's hook callback
EditorPythonRunnerRequestBus::Broadcast(&EditorPythonRunnerRequestBus::Events::ExecuteByFilename,
m_scriptFilename.c_str());
// call script's callback to allow extra products
ExportProductList extraProducts;
ScriptBuildingNotificationBus::BroadcastResult(extraProducts, &ScriptBuildingNotificationBus::Events::OnPrepareForExport,
context.GetScene(),
context.GetOutputDirectory(),
context.GetPlatformIdentifier(),
context.GetProductList()
);
auto& sceneManifest = scene.GetManifest(); // add new products
auto view = Containers::MakeDerivedFilterView<DataTypes::IScriptProcessorRule>(sceneManifest.GetValueStorage()); for (const auto& product : extraProducts.GetProducts())
for (const auto& scriptItem : view) {
context.GetProductList().AddProduct(
product.m_filename,
product.m_id,
product.m_assetType,
product.m_lod,
product.m_subId,
product.m_dependencyFlags);
}
};
if (LoadPython(context.GetScene()))
{
EditorPythonConsoleNotificationHandler logger;
m_editorPythonEventsInterface->ExecuteWithLock(executeCallback);
}
return true;
}
void ScriptProcessorRuleBehavior::Reflect(ReflectContext* context)
{
ScriptBuildingNotificationBusHandler::Reflect(context);
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<ScriptProcessorRuleBehavior, BehaviorComponent>()->Version(1);
}
}
Events::ProcessingResult ScriptProcessorRuleBehavior::UpdateManifest(
Containers::Scene& scene,
Events::AssetImportRequest::ManifestAction action,
[[maybe_unused]] Events::AssetImportRequest::RequestingApplication requester)
{
using namespace AzToolsFramework;
if (action != ManifestAction::Update)
{
return Events::ProcessingResult::Ignored;
}
if (LoadPython(scene))
{
AZStd::string manifestUpdate;
auto executeCallback = [this, &scene, &manifestUpdate]()
{
EditorPythonRunnerRequestBus::Broadcast(&EditorPythonRunnerRequestBus::Events::ExecuteByFilename,
m_scriptFilename.c_str());
ScriptBuildingNotificationBus::BroadcastResult(manifestUpdate, &ScriptBuildingNotificationBus::Events::OnUpdateManifest,
scene);
};
EditorPythonConsoleNotificationHandler logger;
m_editorPythonEventsInterface->ExecuteWithLock(executeCallback);
// attempt to load the manifest string back to a JSON-scene-manifest
auto sceneManifestLoader = AZStd::make_unique<AZ::SceneAPI::Containers::SceneManifest>();
auto loadOutcome = sceneManifestLoader->LoadFromString(manifestUpdate);
if (loadOutcome.IsSuccess())
{
scene.GetManifest().Clear();
for (size_t entryIndex = 0; entryIndex < sceneManifestLoader->GetEntryCount(); ++entryIndex)
{ {
AZ::IO::FixedMaxPath scriptFilename(scriptItem.GetScriptFilename()); scene.GetManifest().AddEntry(sceneManifestLoader->GetValue(entryIndex));
if (scriptFilename.empty())
{
AZ_Warning("scene", false, "Skipping an empty script filename in (%s)", scene.GetManifestFilename().c_str());
continue;
}
// check for file exist via absolute path
if (!IO::FileIOBase::GetInstance()->Exists(scriptFilename.c_str()))
{
// check for script in the project folder
AZ::IO::FixedMaxPath projectScriptPath = projectPath / scriptFilename;
if (!IO::FileIOBase::GetInstance()->Exists(projectScriptPath.c_str()))
{
AZ_Warning("scene", false, "Skipping a missing script (%s) in manifest file (%s)",
scriptFilename.c_str(),
scene.GetManifestFilename().c_str());
continue;
}
scriptFilename = AZStd::move(projectScriptPath);
}
// lazy load the Python interface
if (!m_editorPythonEventsInterface)
{
m_editorPythonEventsInterface = AZ::Interface<AzToolsFramework::EditorPythonEventsInterface>::Get();
const bool silenceWarnings = true;
m_editorPythonEventsInterface->StartPython(silenceWarnings);
}
if (!m_editorPythonEventsInterface && !scriptFilename.empty())
{
AZ_Warning("scene", false,
"The scene manifest (%s) attempted to use script(%s) but Python is not enabled;"
"please add the EditorPythonBinding gem & PythonAssetBuilder gem to your project.",
scene.GetManifestFilename().c_str(), scriptFilename.c_str());
return Events::ProcessingResult::Ignored;
}
AZStd::string manifestUpdate;
auto executeCallback = [&scene, &scriptFilename, &manifestUpdate]()
{
EditorPythonRunnerRequestBus::Broadcast(
&EditorPythonRunnerRequestBus::Events::ExecuteByFilename,
scriptFilename.c_str());
ScriptBuildingNotificationBus::BroadcastResult(
manifestUpdate,
&ScriptBuildingNotificationBus::Events::OnUpdateManifest,
scene);
};
EditorPythonConsoleNotificationHandler logger;
m_editorPythonEventsInterface->ExecuteWithLock(executeCallback);
// attempt to load the manifest string back to a JSON-scene-manifest
auto sceneManifestLoader = AZStd::make_unique<AZ::SceneAPI::Containers::SceneManifest>();
auto loadOutcome = sceneManifestLoader->LoadFromString(manifestUpdate);
if (loadOutcome.IsSuccess())
{
sceneManifest.Clear();
for (size_t entryIndex = 0; entryIndex < sceneManifestLoader->GetEntryCount(); ++entryIndex)
{
sceneManifest.AddEntry(sceneManifestLoader->GetValue(entryIndex));
}
return Events::ProcessingResult::Success;
}
} }
return Events::ProcessingResult::Ignored; return Events::ProcessingResult::Success;
} }
}
return Events::ProcessingResult::Ignored;
}
} // namespace Behaviors
} // namespace SceneAPI
} // namespace AZ } // namespace AZ

@ -11,40 +11,56 @@
#include <AzCore/std/string/string.h> #include <AzCore/std/string/string.h>
#include <SceneAPI/SceneCore/Components/BehaviorComponent.h> #include <SceneAPI/SceneCore/Components/BehaviorComponent.h>
#include <SceneAPI/SceneCore/Events/AssetImportRequest.h> #include <SceneAPI/SceneCore/Events/AssetImportRequest.h>
#include <SceneAPI/SceneCore/Components/ExportingComponent.h>
#include <SceneAPI/SceneCore/Events/ExportEventContext.h>
namespace AzToolsFramework namespace AzToolsFramework
{ {
class EditorPythonEventsInterface; class EditorPythonEventsInterface;
} }
namespace AZ namespace AZ::SceneAPI::Events
{ {
namespace SceneAPI class PreExportEventContext;
}
namespace AZ::SceneAPI::Containers
{
class Scene;
}
namespace AZ::SceneAPI::Behaviors
{
class SCENE_DATA_CLASS ScriptProcessorRuleBehavior
: public SceneCore::BehaviorComponent
, public Events::AssetImportRequestBus::Handler
{ {
namespace Behaviors public:
{ AZ_COMPONENT(ScriptProcessorRuleBehavior, "{24054E73-1B92-43B0-AC13-174B2F0E3F66}", SceneCore::BehaviorComponent);
class SCENE_DATA_CLASS ScriptProcessorRuleBehavior
: public SceneCore::BehaviorComponent ~ScriptProcessorRuleBehavior() override = default;
, public Events::AssetImportRequestBus::Handler
{ SCENE_DATA_API void Activate() override;
public: SCENE_DATA_API void Deactivate() override;
AZ_COMPONENT(ScriptProcessorRuleBehavior, "{24054E73-1B92-43B0-AC13-174B2F0E3F66}", SceneCore::BehaviorComponent); static void Reflect(ReflectContext* context);
~ScriptProcessorRuleBehavior() override = default; // AssetImportRequestBus::Handler
SCENE_DATA_API Events::ProcessingResult UpdateManifest(
SCENE_DATA_API void Activate() override; Containers::Scene& scene,
SCENE_DATA_API void Deactivate() override; ManifestAction action,
static void Reflect(ReflectContext* context); RequestingApplication requester) override;
// AssetImportRequestBus::Handler
SCENE_DATA_API Events::ProcessingResult UpdateManifest( protected:
Containers::Scene& scene, bool LoadPython(const AZ::SceneAPI::Containers::Scene& scene);
ManifestAction action, void UnloadPython();
RequestingApplication requester) override; bool DoPrepareForExport(Events::PreExportEventContext& context);
private: private:
AzToolsFramework::EditorPythonEventsInterface* m_editorPythonEventsInterface = nullptr; AzToolsFramework::EditorPythonEventsInterface* m_editorPythonEventsInterface = nullptr;
}; AZStd::string m_scriptFilename;
} // namespace SceneData
} // namespace SceneAPI struct ExportEventHandler;
} // namespace AZ AZStd::shared_ptr<ExportEventHandler> m_exportEventHandler;
};
} // namespace AZ::SceneAPI::Behaviors

@ -237,8 +237,8 @@ namespace EditorPythonBindings
{ {
ec->Class<PythonSystemComponent>("PythonSystemComponent", "The Python interpreter") ec->Class<PythonSystemComponent>("PythonSystemComponent", "The Python interpreter")
->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("System")) ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("System"))
->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
; ;
} }
} }
@ -284,10 +284,10 @@ namespace EditorPythonBindings
ReleaseFunction m_releaseFunction; ReleaseFunction m_releaseFunction;
}; };
ReleaseInitalizeWaiterScope scope([this]() ReleaseInitalizeWaiterScope scope([this]()
{ {
m_initalizeWaiter.release(m_initalizeWaiterCount); m_initalizeWaiter.release(m_initalizeWaiterCount);
m_initalizeWaiterCount = 0; m_initalizeWaiterCount = 0;
}); });
if (Py_IsInitialized()) if (Py_IsInitialized())
{ {
@ -327,6 +327,11 @@ namespace EditorPythonBindings
return result; return result;
} }
bool PythonSystemComponent::IsPythonActive()
{
return Py_IsInitialized() != 0;
}
void PythonSystemComponent::WaitForInitialization() void PythonSystemComponent::WaitForInitialization()
{ {
m_initalizeWaiterCount++; m_initalizeWaiterCount++;

@ -44,6 +44,7 @@ namespace EditorPythonBindings
// AzToolsFramework::EditorPythonEventsInterface // AzToolsFramework::EditorPythonEventsInterface
bool StartPython(bool silenceWarnings = false) override; bool StartPython(bool silenceWarnings = false) override;
bool StopPython(bool silenceWarnings = false) override; bool StopPython(bool silenceWarnings = false) override;
bool IsPythonActive() override;
void WaitForInitialization() override; void WaitForInitialization() override;
void ExecuteWithLock(AZStd::function<void()> executionCallback) override; void ExecuteWithLock(AZStd::function<void()> executionCallback) override;
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////

Loading…
Cancel
Save