@ -25,12 +25,9 @@
# include <SceneAPI/SceneCore/Containers/Views/PairIterator.h>
# include <SceneAPI/SceneData/Rules/ScriptProcessorRule.h>
# include <SceneAPI/SceneCore/Utilities/Reporting.h>
# include <SceneAPI/SceneCore/Events/ExportProductList.h>
namespace AZ
{
namespace SceneAPI
{
namespace Behaviors
namespace AZ : : SceneAPI : : Behaviors
{
class EditorPythonConsoleNotificationHandler final
: protected AzToolsFramework : : EditorPythonConsoleNotificationBus : : Handler
@ -67,11 +64,18 @@ namespace AZ
}
} ;
using ExportProductList = AZ : : SceneAPI : : Events : : ExportProductList ;
// a event bus to signal during scene building
struct ScriptBuildingNotifications
: public AZ : : EBusTraits
{
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 > ;
@ -84,7 +88,8 @@ namespace AZ
ScriptBuildingNotificationBusHandler ,
" {DF2B51DE-A4D0-4139-B5D0-DF185832380D} " ,
AZ : : SystemAllocator ,
OnUpdateManifest ) ;
OnUpdateManifest ,
OnPrepareForExport ) ;
virtual ~ ScriptBuildingNotificationBusHandler ( ) = default ;
@ -95,6 +100,17 @@ namespace AZ
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 ) )
@ -103,49 +119,58 @@ namespace AZ
- > Attribute ( AZ : : Script : : Attributes : : Scope , AZ : : Script : : Attributes : : ScopeFlags : : Automation )
- > Attribute ( AZ : : Script : : Attributes : : Module , " scene " )
- > Handler < ScriptBuildingNotificationBusHandler > ( )
- > Event ( " OnUpdateManifest " , & ScriptBuildingNotificationBus : : Events : : OnUpdateManifest ) ;
- > Event ( " OnUpdateManifest " , & ScriptBuildingNotificationBus : : Events : : OnUpdateManifest )
- > Event ( " OnPrepareForExport " , & ScriptBuildingNotificationBus : : Events : : OnPrepareForExport ) ;
}
}
} ;
void ScriptProcessorRuleBehavior : : Activate ( )
struct ScriptProcessorRuleBehavior : : ExportEventHandler final
: public AZ : : SceneAPI : : SceneCore : : ExportingComponent
{
Events : : AssetImportRequestBus : : Handler : : BusConnect ( ) ;
}
using PreExportEventContextFunction = AZStd : : function < bool ( Events : : PreExportEventContext & ) > ;
PreExportEventContextFunction m_preExportEventContextFunction ;
void ScriptProcessorRuleBehavior : : Deactivate ( )
ExportEventHandler ( PreExportEventContextFunction preExportEventContextFunction )
: m_preExportEventContextFunction ( preExportEventContextFunction )
{
Events : : AssetImportRequestBus : : Handler : : BusDisconnect ( ) ;
if ( m_editorPythonEventsInterface )
{
const bool silenceWarnings = true ;
m_editorPythonEventsInterface - > StopPython ( silenceWarnings ) ;
m_editorPythonEventsInterface = nullptr ;
}
BindToCall ( & ExportEventHandler : : PrepareForExport ) ;
AZ : : SceneAPI : : SceneCore : : ExportingComponent : : Activate ( ) ;
}
void ScriptProcessorRuleBehavior : : Reflect ( ReflectContext * context )
~ ExportEventHandler ( )
{
ScriptBuildingNotificationBusHandler : : Reflect ( context ) ;
AZ : : SceneAPI : : SceneCore : : ExportingComponent : : Deactivate ( ) ;
}
SerializeContext * serializeContext = azrtti_cast < SerializeContext * > ( context ) ;
if ( serializeC ontext)
// this allows a Python script to add product assets on "scene export"
Events : : ProcessingResult PrepareForExport ( Events : : PreExportEventContext & c ontext)
{
serializeContext - > Class < ScriptProcessorRuleBehavior , BehaviorComponent > ( ) - > Version ( 1 ) ;
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 ) ;
} ) ;
}
Events : : ProcessingResult ScriptProcessorRuleBehavior : : UpdateManifest (
Containers : : Scene & scene ,
Events : : AssetImportRequest : : ManifestAction action ,
[[maybe_unused]] Events : : AssetImportRequest : : RequestingApplication requester )
void ScriptProcessorRuleBehavior : : Deactivate ( )
{
using namespace AzToolsFramework ;
m_exportEventHandler . reset ( ) ;
Events : : AssetImportRequestBus : : Handler : : BusDisconnect ( ) ;
UnloadPython ( ) ;
}
if ( action ! = ManifestAction : : Update )
bool ScriptProcessorRuleBehavior : : LoadPython ( const AZ : : SceneAPI : : Containers : : Scene & scen e)
{
return Events : : ProcessingResult : : Ignored ;
if ( m_editorPythonEventsInterface & & ! m_scriptFilename . empty ( ) )
{
return true ;
}
// get project folder
@ -153,11 +178,11 @@ namespace AZ
AZ : : IO : : FixedMaxPath projectPath ;
if ( ! settingsRegistry - > Get ( projectPath . Native ( ) , AZ : : SettingsRegistryMergeUtils : : FilePathKey_ProjectPath ) )
{
return Events : : ProcessingResult : : Ignored ;
return false ;
}
auto & sceneM anifest = scene . GetManifest ( ) ;
auto view = Containers : : MakeDerivedFilterView < DataTypes : : IScriptProcessorRule > ( sceneM anifest. GetValueStorage ( ) ) ;
const AZ : : SceneAPI : : Containers : : SceneManifest & m anifest = scene . GetManifest ( ) ;
auto view = Containers : : MakeDerivedFilterView < DataTypes : : IScriptProcessorRule > ( m anifest. GetValueStorage ( ) ) ;
for ( const auto & scriptItem : view )
{
AZ : : IO : : FixedMaxPath scriptFilename ( scriptItem . GetScriptFilename ( ) ) ;
@ -177,42 +202,125 @@ namespace AZ
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 )
auto editorPythonEventsInterface = AZ : : Interface < AzToolsFramework : : EditorPythonEventsInterface > : : Get ( ) ;
if ( editorPythonEventsInterface - > IsPythonActive ( ) = = false )
{
m_editorPythonEventsInterface = AZ : : Interface < AzToolsFramework : : EditorPythonEventsInterface > : : Get ( ) ;
const bool silenceWarnings = true ;
m_editorPythonEventsInterface - > StartPython ( silenceWarnings ) ;
const bool silenceWarnings = false ;
if ( editorPythonEventsInterface - > StartPython ( silenceWarnings ) = = false )
{
editorPythonEventsInterface = nullptr ;
}
}
if ( ! m_editorPythonEventsInterface & & ! scriptFilename . empty ( ) )
// both Python and the script need to be ready
if ( editorPythonEventsInterface = = nullptr | | scriptFilename . empty ( ) )
{
AZ_Warning ( " scene " , false ,
" The scene manifest (%s) attempted to use script(%s) but Python is not enabled; "
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 false ;
}
m_editorPythonEventsInterface = editorPythonEventsInterface ;
m_scriptFilename = scriptFilename . c_str ( ) ;
return true ;
}
return false ;
}
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 ( )
) ;
// add new products
for ( const auto & product : extraProducts . GetProducts ( ) )
{
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 = [ & scene , & scriptFilename , & manifestUpdate ] ( )
auto executeCallback = [ this , & scen e, & manifestUpdate ] ( )
{
EditorPythonRunnerRequestBus : : Broadcast (
& EditorPythonRunnerRequestBus : : Events : : ExecuteByFilename ,
scriptFilename . c_str ( ) ) ;
EditorPythonRunnerRequestBus : : Broadcast ( & EditorPythonRunnerRequestBus : : Events : : ExecuteByFilename ,
m_scriptFilename . c_str ( ) ) ;
ScriptBuildingNotificationBus : : BroadcastResult (
manifestUpdate ,
& ScriptBuildingNotificationBus : : Events : : OnUpdateManifest ,
ScriptBuildingNotificationBus : : BroadcastResult ( manifestUpdate , & ScriptBuildingNotificationBus : : Events : : OnUpdateManifest ,
scene ) ;
} ;
EditorPythonConsoleNotificationHandler logger ;
m_editorPythonEventsInterface - > ExecuteWithLock ( executeCallback ) ;
@ -221,10 +329,10 @@ namespace AZ
auto loadOutcome = sceneManifestLoader - > LoadFromString ( manifestUpdate ) ;
if ( loadOutcome . IsSuccess ( ) )
{
scene Manifest. Clear ( ) ;
scene . Get Manifest( ) . Clear ( ) ;
for ( size_t entryIndex = 0 ; entryIndex < sceneManifestLoader - > GetEntryCount ( ) ; + + entryIndex )
{
scene Manifest. AddEntry ( sceneManifestLoader - > GetValue ( entryIndex ) ) ;
scene . Get Manifest( ) . AddEntry ( sceneManifestLoader - > GetValue ( entryIndex ) ) ;
}
return Events : : ProcessingResult : : Success ;
}
@ -232,6 +340,4 @@ namespace AZ
return Events : : ProcessingResult : : Ignored ;
}
} // namespace Behaviors
} // namespace SceneAPI
} // namespace AZ