Merge branch 'development' of https://github.com/o3de/o3de into carlitosan/development
This commit is contained in:
@@ -1896,7 +1896,7 @@ void EditorViewportWidget::SetViewTM(const Matrix34& viewTM, bool bMoveOnly)
|
||||
|
||||
if (m_pressedKeyState != KeyPressedState::PressedInPreviousFrame)
|
||||
{
|
||||
CUndo undo("Move Camera");
|
||||
AzToolsFramework::ScopedUndoBatch undo("Move Camera");
|
||||
if (bMoveOnly)
|
||||
{
|
||||
// specify eObjectUpdateFlags_UserInput so that an undo command gets logged
|
||||
@@ -1932,7 +1932,7 @@ void EditorViewportWidget::SetViewTM(const Matrix34& viewTM, bool bMoveOnly)
|
||||
|
||||
if (m_pressedKeyState != KeyPressedState::PressedInPreviousFrame)
|
||||
{
|
||||
CUndo undo("Move Camera");
|
||||
AzToolsFramework::ScopedUndoBatch undo("Move Camera");
|
||||
if (bMoveOnly)
|
||||
{
|
||||
AZ::TransformBus::Event(
|
||||
@@ -1945,6 +1945,8 @@ void EditorViewportWidget::SetViewTM(const Matrix34& viewTM, bool bMoveOnly)
|
||||
m_viewEntityId, &AZ::TransformInterface::SetWorldTM,
|
||||
LYTransformToAZTransform(camMatrix));
|
||||
}
|
||||
|
||||
AzToolsFramework::ToolsApplicationRequestBus::Broadcast(&AzToolsFramework::ToolsApplicationRequests::AddDirtyEntity, m_viewEntityId);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -83,9 +83,9 @@ AZ::RPI::ViewportContextPtr LegacyViewportCameraControllerInstance::GetViewportC
|
||||
}
|
||||
|
||||
bool LegacyViewportCameraControllerInstance::HandleMouseMove(
|
||||
const AzFramework::ScreenPoint& currentMousePos, const AzFramework::ScreenPoint& previousMousePos)
|
||||
int dx, int dy)
|
||||
{
|
||||
if (previousMousePos == currentMousePos)
|
||||
if (dx == 0 && dy == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -105,7 +105,7 @@ bool LegacyViewportCameraControllerInstance::HandleMouseMove(
|
||||
|
||||
if (m_inMoveMode || m_inOrbitMode || m_inRotateMode || m_inZoomMode)
|
||||
{
|
||||
m_totalMouseMoveDelta += (QPoint(currentMousePos.m_x, currentMousePos.m_y)-QPoint(previousMousePos.m_x, previousMousePos.m_y)).manhattanLength();
|
||||
m_totalMouseMoveDelta += AZStd::abs(dx) + AZStd::abs(dy);
|
||||
}
|
||||
|
||||
if ((m_inRotateMode && m_inMoveMode) || m_inZoomMode)
|
||||
@@ -115,7 +115,7 @@ bool LegacyViewportCameraControllerInstance::HandleMouseMove(
|
||||
Vec3 ydir = m.GetColumn1().GetNormalized();
|
||||
Vec3 pos = m.GetTranslation();
|
||||
|
||||
const float posDelta = 0.2f * (previousMousePos.m_y - currentMousePos.m_y) * speedScale;
|
||||
const float posDelta = 0.2f * dy * speedScale;
|
||||
pos = pos - ydir * posDelta;
|
||||
m_orbitDistance = m_orbitDistance + posDelta;
|
||||
m_orbitDistance = fabs(m_orbitDistance);
|
||||
@@ -126,7 +126,7 @@ bool LegacyViewportCameraControllerInstance::HandleMouseMove(
|
||||
}
|
||||
else if (m_inRotateMode)
|
||||
{
|
||||
Ang3 angles(-currentMousePos.m_y + previousMousePos.m_y, 0, -currentMousePos.m_x + previousMousePos.m_x);
|
||||
Ang3 angles(dy, 0, dx);
|
||||
angles = angles * 0.002f * gSettings.cameraRotateSpeed;
|
||||
if (gSettings.invertYRotation)
|
||||
{
|
||||
@@ -158,7 +158,7 @@ bool LegacyViewportCameraControllerInstance::HandleMouseMove(
|
||||
}
|
||||
|
||||
Vec3 pos = m.GetTranslation();
|
||||
pos += 0.1f * xdir * (currentMousePos.m_x - previousMousePos.m_x) * speedScale + 0.1f * zdir * (previousMousePos.m_y - currentMousePos.m_y) * speedScale;
|
||||
pos += 0.1f * xdir * dx * speedScale + 0.1f * zdir * dy * speedScale;
|
||||
m.SetTranslation(pos);
|
||||
|
||||
AZ::Transform transform = viewportContext->GetCameraTransform();
|
||||
@@ -168,7 +168,7 @@ bool LegacyViewportCameraControllerInstance::HandleMouseMove(
|
||||
}
|
||||
else if (m_inOrbitMode)
|
||||
{
|
||||
Ang3 angles(-currentMousePos.m_y + previousMousePos.m_y, 0, -currentMousePos.m_x + previousMousePos.m_x);
|
||||
Ang3 angles(dy, 0, dx);
|
||||
angles = angles * 0.002f * gSettings.cameraRotateSpeed;
|
||||
|
||||
if (gSettings.invertPan)
|
||||
@@ -302,20 +302,19 @@ bool LegacyViewportCameraControllerInstance::HandleInputChannelEvent(const AzFra
|
||||
bool shouldCaptureCursor = m_capturingCursor;
|
||||
bool shouldConsumeEvent = false;
|
||||
|
||||
if (id == AzFramework::InputDeviceMouse::SystemCursorPosition)
|
||||
if (id == AzFramework::InputDeviceMouse::Movement::X || id == AzFramework::InputDeviceMouse::Movement::Y)
|
||||
{
|
||||
bool result = false;
|
||||
AzToolsFramework::ViewportInteraction::ViewportMouseCursorRequestBus::Event(
|
||||
GetViewportId(),
|
||||
[this, &result](AzToolsFramework::ViewportInteraction::ViewportMouseCursorRequests* mouseRequests)
|
||||
{
|
||||
if (auto previousMousePosition = mouseRequests->PreviousViewportCursorScreenPosition();
|
||||
previousMousePosition.has_value())
|
||||
{
|
||||
result = HandleMouseMove(mouseRequests->ViewportCursorScreenPosition(), previousMousePosition.value());
|
||||
}
|
||||
});
|
||||
return result;
|
||||
int dx = 0;
|
||||
int dy = 0;
|
||||
if (id == AzFramework::InputDeviceMouse::Movement::X)
|
||||
{
|
||||
dx = -aznumeric_cast<int>(event.m_inputChannel.GetValue());
|
||||
}
|
||||
else
|
||||
{
|
||||
dy = -aznumeric_cast<int>(event.m_inputChannel.GetValue());
|
||||
}
|
||||
return HandleMouseMove(dx, dy);
|
||||
}
|
||||
else if (id == MouseButton::Left)
|
||||
{
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace SandboxEditor
|
||||
|
||||
AZ::RPI::ViewportContextPtr GetViewportContext();
|
||||
|
||||
bool HandleMouseMove(const AzFramework::ScreenPoint& currentMousePos, const AzFramework::ScreenPoint& previousMousePos);
|
||||
bool HandleMouseMove(int dx, int dy);
|
||||
bool HandleMouseWheel(float zDelta);
|
||||
bool IsKeyDown(Qt::Key key) const;
|
||||
void UpdateCursorCapture(bool shouldCaptureCursor);
|
||||
|
||||
@@ -20,12 +20,12 @@
|
||||
#include "ValidationHandler.h"
|
||||
|
||||
#include <AzCore/Component/ComponentApplicationBus.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
|
||||
#include "AzToolsFramework/UI/PropertyEditor/InstanceDataHierarchy.h"
|
||||
#include <AzToolsFramework/UI/PropertyEditor/InstanceDataHierarchy.h>
|
||||
#include <AzToolsFramework/UI/PropertyEditor/PropertyManagerComponent.h>
|
||||
#include <AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.hxx>
|
||||
#include <AzToolsFramework/UI/UICore/WidgetHelpers.h>
|
||||
#include <Util/FileUtil.h>
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <QCloseEvent>
|
||||
@@ -52,7 +52,7 @@ namespace ProjectSettingsTool
|
||||
PlatformEnabled(PlatformId::Ios) ?
|
||||
ProjectSettingsContainer::PlistInitVector({
|
||||
ProjectSettingsContainer::PlatformAndPath
|
||||
{ PlatformId::Ios, m_projectRoot + PlatformResourcesFolder(PlatformId::Ios) }
|
||||
{ PlatformId::Ios, GetPlatformResource(PlatformId::Ios) }
|
||||
})
|
||||
:
|
||||
ProjectSettingsContainer::PlistInitVector())
|
||||
@@ -647,33 +647,38 @@ namespace ProjectSettingsTool
|
||||
// iOS can be disabled if the plist file is missing
|
||||
if (platformId == PlatformId::Ios)
|
||||
{
|
||||
const AZStd::string filename = m_projectRoot + PlatformResourcesFolder(platformId);
|
||||
return CFileUtil::FileExists(filename.c_str());
|
||||
AZStd::string plistPath = GetPlatformResource(platformId);
|
||||
return !plistPath.empty();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* ProjectSettingsToolWindow::PlatformResourcesFolder(PlatformId platformId)
|
||||
AZStd::string ProjectSettingsToolWindow::GetPlatformResource(PlatformId platformId)
|
||||
{
|
||||
if (platformId == PlatformId::Ios)
|
||||
{
|
||||
const AZStd::string firstfilename = m_projectRoot + "/Gem/Resources/Platform/iOS/Info.plist";
|
||||
if (CFileUtil::FileExists(firstfilename.c_str()))
|
||||
const char* searchPaths[] = {
|
||||
"Resources/Platform/iOS/Info.plist",
|
||||
|
||||
// legacy paths
|
||||
"Gem/Resources/Platform/iOS/Info.plist",
|
||||
"Gem/Resources/IOSLauncher/Info.plist",
|
||||
};
|
||||
|
||||
for (auto relPath : searchPaths)
|
||||
{
|
||||
return "/Gem/Resources/Platform/iOS/Info.plist";
|
||||
}
|
||||
else
|
||||
{
|
||||
const AZStd::string filename = m_projectRoot + "/Gem/Resources/IOSLauncher/Info.plist";
|
||||
if (CFileUtil::FileExists(filename.c_str()))
|
||||
AZ::IO::FixedMaxPath projectPlist{ m_projectRoot };
|
||||
projectPlist /= relPath;
|
||||
|
||||
if (AZ::IO::SystemFile::Exists(projectPlist.c_str()))
|
||||
{
|
||||
return "/Gem/Resources/IOSLauncher/Info.plist";
|
||||
return projectPlist.LexicallyNormal().String();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return AZStd::string();
|
||||
}
|
||||
|
||||
#include <moc_ProjectSettingsToolWindow.cpp>
|
||||
|
||||
@@ -137,8 +137,8 @@ namespace ProjectSettingsTool
|
||||
// returns true if the platform is enabled
|
||||
bool PlatformEnabled(PlatformId platformId);
|
||||
|
||||
// returns the resource folder
|
||||
const char* PlatformResourcesFolder(PlatformId platformId);
|
||||
// returns the main platform specific resource file e.g. for iOS it would be the Info.plist
|
||||
AZStd::string GetPlatformResource(PlatformId platformId);
|
||||
|
||||
// The ui for the window
|
||||
QScopedPointer<Ui::ProjectSettingsToolWidget> m_ui;
|
||||
|
||||
@@ -116,7 +116,7 @@ namespace AZ
|
||||
{
|
||||
const char* uuidString = nullptr;
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -97,6 +97,9 @@ namespace AzFramework
|
||||
|
||||
//! This is called when the window is deactivated from code or if the user closes the window.
|
||||
virtual void OnWindowClosed() {};
|
||||
|
||||
//! This is called when vsync interval is changed.
|
||||
virtual void OnVsyncIntervalChanged(uint32_t interval) { AZ_UNUSED(interval); };
|
||||
};
|
||||
using WindowNotificationBus = AZ::EBus<WindowNotifications>;
|
||||
|
||||
|
||||
@@ -63,6 +63,9 @@ namespace AzToolsFramework
|
||||
//! Signal the Python handler to stop
|
||||
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)
|
||||
virtual void WaitForInitialization() {}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include <AzFramework/Input/Buses/Notifications/InputChannelNotificationBus.h>
|
||||
#include <AzFramework/Input/Buses/Requests/InputChannelRequestBus.h>
|
||||
#include <AzQtComponents/Utilities/QtWindowUtilities.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QCursor>
|
||||
@@ -187,12 +188,6 @@ namespace AzToolsFramework
|
||||
|
||||
bool QtEventToAzInputMapper::HandlesInputEvent(const AzFramework::InputChannel& channel) const
|
||||
{
|
||||
const AzFramework::InputChannelId& channelId = channel.GetInputChannelId();
|
||||
if (channelId == AzFramework::InputDeviceMouse::Movement::X || channelId == AzFramework::InputDeviceMouse::Movement::Y)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// We map keyboard and mouse events from Qt, so flag all events coming from those devices
|
||||
// as handled by our synthetic event system.
|
||||
const AzFramework::InputDeviceId& deviceId = channel.GetInputDevice().GetInputDeviceId();
|
||||
@@ -210,6 +205,22 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
void QtEventToAzInputMapper::SetCursorCaptureEnabled(bool enabled)
|
||||
{
|
||||
if (m_capturingCursor != enabled)
|
||||
{
|
||||
m_capturingCursor = enabled;
|
||||
if (m_capturingCursor)
|
||||
{
|
||||
qApp->setOverrideCursor(Qt::BlankCursor);
|
||||
}
|
||||
else
|
||||
{
|
||||
qApp->restoreOverrideCursor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool QtEventToAzInputMapper::eventFilter(QObject* object, QEvent* event)
|
||||
{
|
||||
// Abort if processing isn't enabled.
|
||||
@@ -284,13 +295,25 @@ namespace AzToolsFramework
|
||||
{
|
||||
auto systemCursorChannel =
|
||||
GetInputChannel<AzFramework::InputChannelDeltaWithSharedPosition2D>(AzFramework::InputDeviceMouse::SystemCursorPosition);
|
||||
auto movementXChannel =
|
||||
GetInputChannel<AzFramework::InputChannelDeltaWithSharedPosition2D>(AzFramework::InputDeviceMouse::Movement::X);
|
||||
auto movementYChannel =
|
||||
GetInputChannel<AzFramework::InputChannelDeltaWithSharedPosition2D>(AzFramework::InputDeviceMouse::Movement::Y);
|
||||
auto mouseWheelChannel =
|
||||
GetInputChannel<AzFramework::InputChannelDeltaWithSharedPosition2D>(AzFramework::InputDeviceMouse::Movement::Z);
|
||||
|
||||
systemCursorChannel->ProcessRawInputEvent(m_cursorPosition->m_normalizedPositionDelta.GetLength());
|
||||
// Generate movement events based on the pixel delta divided by the DPI scaling factor, to calculate a rough approximation
|
||||
// of cursor movement velocity.
|
||||
movementXChannel->ProcessRawInputEvent(
|
||||
m_cursorPosition->m_normalizedPositionDelta.GetX() * aznumeric_cast<float>(m_sourceWidget->width()) / m_sourceWidget->devicePixelRatioF());
|
||||
movementYChannel->ProcessRawInputEvent(
|
||||
m_cursorPosition->m_normalizedPositionDelta.GetY() * aznumeric_cast<float>(m_sourceWidget->height()) / m_sourceWidget->devicePixelRatioF());
|
||||
mouseWheelChannel->ProcessRawInputEvent(0.f);
|
||||
|
||||
NotifyUpdateChannelIfNotIdle(systemCursorChannel, nullptr);
|
||||
NotifyUpdateChannelIfNotIdle(movementXChannel, nullptr);
|
||||
NotifyUpdateChannelIfNotIdle(movementYChannel, nullptr);
|
||||
NotifyUpdateChannelIfNotIdle(mouseWheelChannel, nullptr);
|
||||
}
|
||||
|
||||
@@ -318,16 +341,42 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
AZ::Vector2 QtEventToAzInputMapper::WidgetPositionToNormalizedPosition(QPoint position)
|
||||
{
|
||||
const float normalizedX = aznumeric_cast<float>(position.x()) / aznumeric_cast<float>(m_sourceWidget->width());
|
||||
const float normalizedY = aznumeric_cast<float>(position.y()) / aznumeric_cast<float>(m_sourceWidget->height());
|
||||
return AZ::Vector2{normalizedX, normalizedY};
|
||||
}
|
||||
|
||||
QPoint QtEventToAzInputMapper::NormalizedPositionToWidgetPosition(AZ::Vector2 normalizedPosition)
|
||||
{
|
||||
const int denormalizedX = aznumeric_cast<int>(normalizedPosition.GetX() * m_sourceWidget->width());
|
||||
const int denormalizedY = aznumeric_cast<int>(normalizedPosition.GetY() * m_sourceWidget->height());
|
||||
return QPoint{denormalizedX, denormalizedY};
|
||||
}
|
||||
|
||||
void QtEventToAzInputMapper::HandleMouseMoveEvent(QMouseEvent* mouseEvent)
|
||||
{
|
||||
AZ::Vector2 lastCursorPosition = m_cursorPosition->m_normalizedPosition;
|
||||
|
||||
const QPoint mousePos = mouseEvent->pos();
|
||||
const float normalizedX = aznumeric_cast<float>(mousePos.x()) / aznumeric_cast<float>(m_sourceWidget->width());
|
||||
const float normalizedY = aznumeric_cast<float>(mousePos.y()) / aznumeric_cast<float>(m_sourceWidget->height());
|
||||
const AZ::Vector2 normalizedPosition(normalizedX, normalizedY);
|
||||
const AZ::Vector2 normalizedPosition = WidgetPositionToNormalizedPosition(mousePos);
|
||||
m_cursorPosition->m_normalizedPositionDelta = normalizedPosition - m_cursorPosition->m_normalizedPosition;
|
||||
m_cursorPosition->m_normalizedPosition = normalizedPosition;
|
||||
ProcessPendingMouseEvents();
|
||||
m_mouseChannelsNeedUpdate = true;
|
||||
|
||||
if (m_capturingCursor)
|
||||
{
|
||||
// Reset our cursor position to the previous point.
|
||||
QPoint targetScreenPosition = m_sourceWidget->mapToGlobal(NormalizedPositionToWidgetPosition(lastCursorPosition));
|
||||
AzQtComponents::SetCursorPos(targetScreenPosition);
|
||||
|
||||
// Even though we just set the cursor position, there are edge cases such as remote desktop that will leave
|
||||
// the cursor position unchanged. For safety, we re-cache our last cursor position for delta generation.
|
||||
QPoint actualWidgetPosition = m_sourceWidget->mapFromGlobal(QCursor::pos());
|
||||
m_cursorPosition->m_normalizedPosition = WidgetPositionToNormalizedPosition(actualWidgetPosition);
|
||||
}
|
||||
}
|
||||
|
||||
void QtEventToAzInputMapper::HandleKeyEvent(QKeyEvent* keyEvent)
|
||||
|
||||
@@ -47,6 +47,12 @@ namespace AzToolsFramework
|
||||
//! Sets whether or not this input mapper should be updating its input channels from Qt events.
|
||||
void SetEnabled(bool enabled);
|
||||
|
||||
//! Sets whether or not the cursor should be constrained to the source widget and invisible.
|
||||
//! Internally, this will reset the cursor position after each move event to ensure movement
|
||||
//! events don't allow the cursor to escape. This can be used for typical camera controls
|
||||
//! like a dolly or rotation, where mouse movement is important but cursor location is not.
|
||||
void SetCursorCaptureEnabled(bool enabled);
|
||||
|
||||
// QObject overrides...
|
||||
bool eventFilter(QObject* object, QEvent* event) override;
|
||||
|
||||
@@ -106,6 +112,11 @@ namespace AzToolsFramework
|
||||
// Processes any pending mouse movement events, this allows mouse movement channels to close themselves.
|
||||
void ProcessPendingMouseEvents();
|
||||
|
||||
// Converts a point in logical source widget space [0..m_sourceWidget->size()] to normalized [0..1] space.
|
||||
AZ::Vector2 WidgetPositionToNormalizedPosition(QPoint position);
|
||||
// Converts a point in normalized [0..1] space to logical source widget space [0..m_sourceWidget->size()].
|
||||
QPoint NormalizedPositionToWidgetPosition(AZ::Vector2 normalizedPosition);
|
||||
|
||||
// Handle mouse click events.
|
||||
void HandleMouseButtonEvent(QMouseEvent* mouseEvent);
|
||||
// Handle mouse move events.
|
||||
@@ -144,6 +155,8 @@ namespace AzToolsFramework
|
||||
bool m_mouseChannelsNeedUpdate = false;
|
||||
// Flags whether or not Qt events should currently be processed.
|
||||
bool m_enabled = true;
|
||||
// Flags whether or not the cursor is being constrained to the source widget (for invisible mouse movement).
|
||||
bool m_capturingCursor = false;
|
||||
|
||||
// Our viewport-specific AZ devices. We control their internal input channel states.
|
||||
AZStd::unique_ptr<EditorQtMouseDevice> m_mouseDevice;
|
||||
|
||||
@@ -276,11 +276,6 @@ namespace AzToolsFramework
|
||||
virtual void EndCursorCapture() = 0;
|
||||
//! Gets the most recent recorded cursor position in the viewport in screen space coordinates.
|
||||
virtual AzFramework::ScreenPoint ViewportCursorScreenPosition() = 0;
|
||||
//! Gets the cursor position recorded prior to the most recent cursor position.
|
||||
//! Note: The cursor may be captured by the viewport, in which case this may not correspond to the last result
|
||||
//! from ViewportCursorScreenPosition. This method will always return the correct position to generate a mouse
|
||||
//! position delta.
|
||||
virtual AZStd::optional<AzFramework::ScreenPoint> PreviousViewportCursorScreenPosition() = 0;
|
||||
//! Is mouse over viewport.
|
||||
virtual bool IsMouseOver() const = 0;
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <SceneAPI/SceneCore/Components/ExportingComponent.h>
|
||||
#include <SceneAPI/SceneCore/Events/ExportProductList.h>
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
@@ -31,6 +33,12 @@ namespace AZ
|
||||
{
|
||||
serializeContext->Class<ExportingComponent, AZ::Component>()->Version(2);
|
||||
}
|
||||
|
||||
AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context);
|
||||
if (behaviorContext)
|
||||
{
|
||||
Events::ExportProductList::Reflect(behaviorContext);
|
||||
}
|
||||
}
|
||||
} // namespace SceneCore
|
||||
} // namespace SceneAPI
|
||||
|
||||
@@ -211,6 +211,7 @@ namespace AZ
|
||||
AZ::SceneAPI::Containers::SceneGraph::Reflect(context);
|
||||
AZ::SceneAPI::Containers::SceneManifest::Reflect(context);
|
||||
AZ::SceneAPI::Containers::RuleContainer::Reflect(context);
|
||||
AZ::SceneAPI::SceneCore::ExportingComponent::Reflect(context);
|
||||
}
|
||||
|
||||
void Activate()
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
*/
|
||||
|
||||
#include <SceneAPI/SceneCore/Events/ExportProductList.h>
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
#include <AzCore/std/limits.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
@@ -49,6 +51,45 @@ namespace AZ
|
||||
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,
|
||||
Data::ProductDependencyInfo::ProductDependencyFlags dependencyFlags)
|
||||
{
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
class ReflectContext;
|
||||
|
||||
namespace SceneAPI
|
||||
{
|
||||
namespace Events
|
||||
@@ -24,6 +26,7 @@ namespace AZ
|
||||
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,
|
||||
Data::ProductDependencyInfo::ProductDependencyFlags dependencyFlags = Data::ProductDependencyInfo::CreateFlags(Data::AssetLoadBehavior::NoLoad));
|
||||
ExportProduct() = default;
|
||||
ExportProduct(const ExportProduct& rhs) = default;
|
||||
SCENE_CORE_API ExportProduct(ExportProduct&& rhs);
|
||||
|
||||
@@ -54,6 +57,8 @@ namespace AZ
|
||||
class ExportProductList
|
||||
{
|
||||
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,
|
||||
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,
|
||||
@@ -69,3 +74,9 @@ namespace AZ
|
||||
} // namespace Events
|
||||
} // namespace SceneAPI
|
||||
} // 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/GenericStreams.h>
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <AzFramework/API/ApplicationAPI.h>
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
#include <SceneAPI/SceneCore/Import/ManifestImportRequestHandler.h>
|
||||
@@ -75,15 +76,16 @@ namespace AZ
|
||||
filename += s_extension;
|
||||
filename += s_generated;
|
||||
|
||||
AZStd::string altManifestPath = path;
|
||||
AZStd::string altManifestFolder = path;
|
||||
AzFramework::ApplicationRequests::Bus::Broadcast(
|
||||
&AzFramework::ApplicationRequests::Bus::Events::MakePathRootRelative,
|
||||
altManifestPath);
|
||||
&AzFramework::ApplicationRequests::Bus::Events::MakePathRelative,
|
||||
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;
|
||||
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);
|
||||
|
||||
if (!AZ::IO::FileIOBase::GetInstance()->Exists(generatedAssetInfoPath.c_str()))
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -25,213 +25,319 @@
|
||||
#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 AZ::SceneAPI::Behaviors
|
||||
{
|
||||
namespace SceneAPI
|
||||
class EditorPythonConsoleNotificationHandler final
|
||||
: protected AzToolsFramework::EditorPythonConsoleNotificationBus::Handler
|
||||
{
|
||||
namespace Behaviors
|
||||
public:
|
||||
EditorPythonConsoleNotificationHandler()
|
||||
{
|
||||
class EditorPythonConsoleNotificationHandler final
|
||||
: protected AzToolsFramework::EditorPythonConsoleNotificationBus::Handler
|
||||
BusConnect();
|
||||
}
|
||||
|
||||
~EditorPythonConsoleNotificationHandler()
|
||||
{
|
||||
BusDisconnect();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// AzToolsFramework::EditorPythonConsoleNotifications
|
||||
void OnTraceMessage([[maybe_unused]] AZStd::string_view message) override
|
||||
{
|
||||
using namespace AZ::SceneAPI::Utilities;
|
||||
AZ_TracePrintf(LogWindow, "%.*s \n", AZ_STRING_ARG(message));
|
||||
}
|
||||
|
||||
void OnErrorMessage([[maybe_unused]] AZStd::string_view message) override
|
||||
{
|
||||
using namespace AZ::SceneAPI::Utilities;
|
||||
AZ_TracePrintf(ErrorWindow, "[ERROR] %.*s \n", AZ_STRING_ARG(message));
|
||||
}
|
||||
|
||||
void OnExceptionMessage([[maybe_unused]] AZStd::string_view message) override
|
||||
{
|
||||
using namespace AZ::SceneAPI::Utilities;
|
||||
AZ_TracePrintf(ErrorWindow, "[EXCEPTION] %.*s \n", AZ_STRING_ARG(message));
|
||||
}
|
||||
};
|
||||
|
||||
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>;
|
||||
|
||||
// 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);
|
||||
|
||||
virtual ~ScriptBuildingNotificationBusHandler() = default;
|
||||
|
||||
AZStd::string OnUpdateManifest(Containers::Scene& scene) override
|
||||
{
|
||||
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))
|
||||
{
|
||||
public:
|
||||
EditorPythonConsoleNotificationHandler()
|
||||
{
|
||||
BusConnect();
|
||||
}
|
||||
behaviorContext->EBus<ScriptBuildingNotificationBus>("ScriptBuildingNotificationBus")
|
||||
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
|
||||
->Attribute(AZ::Script::Attributes::Module, "scene")
|
||||
->Handler<ScriptBuildingNotificationBusHandler>()
|
||||
->Event("OnUpdateManifest", &ScriptBuildingNotificationBus::Events::OnUpdateManifest)
|
||||
->Event("OnPrepareForExport", &ScriptBuildingNotificationBus::Events::OnPrepareForExport);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
~EditorPythonConsoleNotificationHandler()
|
||||
{
|
||||
BusDisconnect();
|
||||
}
|
||||
struct ScriptProcessorRuleBehavior::ExportEventHandler final
|
||||
: public AZ::SceneAPI::SceneCore::ExportingComponent
|
||||
{
|
||||
using PreExportEventContextFunction = AZStd::function<bool(Events::PreExportEventContext&)>;
|
||||
PreExportEventContextFunction m_preExportEventContextFunction;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// AzToolsFramework::EditorPythonConsoleNotifications
|
||||
void OnTraceMessage([[maybe_unused]] AZStd::string_view message) override
|
||||
{
|
||||
using namespace AZ::SceneAPI::Utilities;
|
||||
AZ_TracePrintf(LogWindow, "%.*s \n", AZ_STRING_ARG(message));
|
||||
}
|
||||
ExportEventHandler(PreExportEventContextFunction preExportEventContextFunction)
|
||||
: m_preExportEventContextFunction(preExportEventContextFunction)
|
||||
{
|
||||
BindToCall(&ExportEventHandler::PrepareForExport);
|
||||
AZ::SceneAPI::SceneCore::ExportingComponent::Activate();
|
||||
}
|
||||
|
||||
void OnErrorMessage([[maybe_unused]] AZStd::string_view message) override
|
||||
{
|
||||
using namespace AZ::SceneAPI::Utilities;
|
||||
AZ_TracePrintf(ErrorWindow, "[ERROR] %.*s \n", AZ_STRING_ARG(message));
|
||||
}
|
||||
~ExportEventHandler()
|
||||
{
|
||||
AZ::SceneAPI::SceneCore::ExportingComponent::Deactivate();
|
||||
}
|
||||
|
||||
void OnExceptionMessage([[maybe_unused]] AZStd::string_view message) override
|
||||
{
|
||||
using namespace AZ::SceneAPI::Utilities;
|
||||
AZ_TracePrintf(ErrorWindow, "[EXCEPTION] %.*s \n", AZ_STRING_ARG(message));
|
||||
}
|
||||
};
|
||||
// this allows a Python script to add product assets on "scene export"
|
||||
Events::ProcessingResult PrepareForExport(Events::PreExportEventContext& context)
|
||||
{
|
||||
return m_preExportEventContextFunction(context) ? Events::ProcessingResult::Success : Events::ProcessingResult::Failure;
|
||||
}
|
||||
};
|
||||
|
||||
// a event bus to signal during scene building
|
||||
struct ScriptBuildingNotifications
|
||||
: public AZ::EBusTraits
|
||||
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;
|
||||
}
|
||||
|
||||
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())
|
||||
{
|
||||
virtual AZStd::string OnUpdateManifest(Containers::Scene& scene) = 0;
|
||||
};
|
||||
using ScriptBuildingNotificationBus = AZ::EBus<ScriptBuildingNotifications>;
|
||||
AZ_Warning("scene", false, "Skipping an empty script filename in (%s)", scene.GetManifestFilename().c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
// a back end to handle scene builder events for a script
|
||||
struct ScriptBuildingNotificationBusHandler final
|
||||
: public ScriptBuildingNotificationBus::Handler
|
||||
, public AZ::BehaviorEBusHandler
|
||||
// check for file exist via absolute path
|
||||
if (!IO::FileIOBase::GetInstance()->Exists(scriptFilename.c_str()))
|
||||
{
|
||||
AZ_EBUS_BEHAVIOR_BINDER(
|
||||
ScriptBuildingNotificationBusHandler,
|
||||
"{DF2B51DE-A4D0-4139-B5D0-DF185832380D}",
|
||||
AZ::SystemAllocator,
|
||||
OnUpdateManifest);
|
||||
|
||||
virtual ~ScriptBuildingNotificationBusHandler() = default;
|
||||
|
||||
AZStd::string OnUpdateManifest(Containers::Scene& scene) override
|
||||
// check for script in the project folder
|
||||
AZ::IO::FixedMaxPath projectScriptPath = projectPath / scriptFilename;
|
||||
if (!IO::FileIOBase::GetInstance()->Exists(projectScriptPath.c_str()))
|
||||
{
|
||||
AZStd::string result;
|
||||
CallResult(result, FN_OnUpdateManifest, scene);
|
||||
return result;
|
||||
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);
|
||||
}
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context)
|
||||
// lazy load the Python interface
|
||||
auto editorPythonEventsInterface = AZ::Interface<AzToolsFramework::EditorPythonEventsInterface>::Get();
|
||||
if (editorPythonEventsInterface->IsPythonActive() == false)
|
||||
{
|
||||
const bool silenceWarnings = false;
|
||||
if (editorPythonEventsInterface->StartPython(silenceWarnings) == false)
|
||||
{
|
||||
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
|
||||
{
|
||||
behaviorContext->EBus<ScriptBuildingNotificationBus>("ScriptBuildingNotificationBus")
|
||||
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
|
||||
->Attribute(AZ::Script::Attributes::Module, "scene")
|
||||
->Handler<ScriptBuildingNotificationBusHandler>()
|
||||
->Event("OnUpdateManifest", &ScriptBuildingNotificationBus::Events::OnUpdateManifest);
|
||||
}
|
||||
editorPythonEventsInterface = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// 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;"
|
||||
"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 = [this, &scene, &manifestUpdate]()
|
||||
{
|
||||
EditorPythonRunnerRequestBus::Broadcast(&EditorPythonRunnerRequestBus::Events::ExecuteByFilename,
|
||||
m_scriptFilename.c_str());
|
||||
|
||||
ScriptBuildingNotificationBus::BroadcastResult(manifestUpdate, &ScriptBuildingNotificationBus::Events::OnUpdateManifest,
|
||||
scene);
|
||||
};
|
||||
|
||||
void ScriptProcessorRuleBehavior::Activate()
|
||||
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())
|
||||
{
|
||||
Events::AssetImportRequestBus::Handler::BusConnect();
|
||||
scene.GetManifest().Clear();
|
||||
for (size_t entryIndex = 0; entryIndex < sceneManifestLoader->GetEntryCount(); ++entryIndex)
|
||||
{
|
||||
scene.GetManifest().AddEntry(sceneManifestLoader->GetValue(entryIndex));
|
||||
}
|
||||
return Events::ProcessingResult::Success;
|
||||
}
|
||||
}
|
||||
return Events::ProcessingResult::Ignored;
|
||||
}
|
||||
|
||||
void ScriptProcessorRuleBehavior::Deactivate()
|
||||
{
|
||||
Events::AssetImportRequestBus::Handler::BusDisconnect();
|
||||
if (m_editorPythonEventsInterface)
|
||||
{
|
||||
const bool silenceWarnings = true;
|
||||
m_editorPythonEventsInterface->StopPython(silenceWarnings);
|
||||
m_editorPythonEventsInterface = nullptr;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// get project folder
|
||||
auto settingsRegistry = AZ::SettingsRegistry::Get();
|
||||
AZ::IO::FixedMaxPath projectPath;
|
||||
if (!settingsRegistry->Get(projectPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectPath))
|
||||
{
|
||||
return Events::ProcessingResult::Ignored;
|
||||
}
|
||||
|
||||
auto& sceneManifest = scene.GetManifest();
|
||||
auto view = Containers::MakeDerivedFilterView<DataTypes::IScriptProcessorRule>(sceneManifest.GetValueStorage());
|
||||
for (const auto& scriptItem : view)
|
||||
{
|
||||
AZ::IO::FixedMaxPath scriptFilename(scriptItem.GetScriptFilename());
|
||||
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;
|
||||
}
|
||||
|
||||
} // namespace Behaviors
|
||||
} // namespace SceneAPI
|
||||
} // namespace AZ
|
||||
|
||||
@@ -11,40 +11,56 @@
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <SceneAPI/SceneCore/Components/BehaviorComponent.h>
|
||||
#include <SceneAPI/SceneCore/Events/AssetImportRequest.h>
|
||||
#include <SceneAPI/SceneCore/Components/ExportingComponent.h>
|
||||
#include <SceneAPI/SceneCore/Events/ExportEventContext.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
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
|
||||
{
|
||||
class SCENE_DATA_CLASS ScriptProcessorRuleBehavior
|
||||
: public SceneCore::BehaviorComponent
|
||||
, public Events::AssetImportRequestBus::Handler
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(ScriptProcessorRuleBehavior, "{24054E73-1B92-43B0-AC13-174B2F0E3F66}", SceneCore::BehaviorComponent);
|
||||
public:
|
||||
AZ_COMPONENT(ScriptProcessorRuleBehavior, "{24054E73-1B92-43B0-AC13-174B2F0E3F66}", SceneCore::BehaviorComponent);
|
||||
|
||||
~ScriptProcessorRuleBehavior() override = default;
|
||||
~ScriptProcessorRuleBehavior() override = default;
|
||||
|
||||
SCENE_DATA_API void Activate() override;
|
||||
SCENE_DATA_API void Deactivate() override;
|
||||
static void Reflect(ReflectContext* context);
|
||||
SCENE_DATA_API void Activate() override;
|
||||
SCENE_DATA_API void Deactivate() override;
|
||||
static void Reflect(ReflectContext* context);
|
||||
|
||||
// AssetImportRequestBus::Handler
|
||||
SCENE_DATA_API Events::ProcessingResult UpdateManifest(
|
||||
Containers::Scene& scene,
|
||||
ManifestAction action,
|
||||
RequestingApplication requester) override;
|
||||
// AssetImportRequestBus::Handler
|
||||
SCENE_DATA_API Events::ProcessingResult UpdateManifest(
|
||||
Containers::Scene& scene,
|
||||
ManifestAction action,
|
||||
RequestingApplication requester) override;
|
||||
|
||||
private:
|
||||
AzToolsFramework::EditorPythonEventsInterface* m_editorPythonEventsInterface = nullptr;
|
||||
};
|
||||
} // namespace SceneData
|
||||
} // namespace SceneAPI
|
||||
} // namespace AZ
|
||||
|
||||
protected:
|
||||
bool LoadPython(const AZ::SceneAPI::Containers::Scene& scene);
|
||||
void UnloadPython();
|
||||
bool DoPrepareForExport(Events::PreExportEventContext& context);
|
||||
|
||||
private:
|
||||
AzToolsFramework::EditorPythonEventsInterface* m_editorPythonEventsInterface = nullptr;
|
||||
AZStd::string m_scriptFilename;
|
||||
|
||||
struct ExportEventHandler;
|
||||
AZStd::shared_ptr<ExportEventHandler> m_exportEventHandler;
|
||||
};
|
||||
} // namespace AZ::SceneAPI::Behaviors
|
||||
|
||||
Reference in New Issue
Block a user