Merge branch 'development' of https://github.com/o3de/o3de into jckand/EditorAutomationOptimization
@@ -19,6 +19,66 @@ namespace UnitTest
|
||||
|
||||
using AzToolsFramework::ViewportInteraction::MouseInteractionEvent;
|
||||
|
||||
class ViewportMouseCursorRequestImpl : public AzToolsFramework::ViewportInteraction::ViewportMouseCursorRequestBus::Handler
|
||||
{
|
||||
public:
|
||||
void Connect(const AzFramework::ViewportId viewportId, AzToolsFramework::QtEventToAzInputMapper* inputChannelMapper)
|
||||
{
|
||||
AzToolsFramework::ViewportInteraction::ViewportMouseCursorRequestBus::Handler::BusConnect(viewportId);
|
||||
m_inputChannelMapper = inputChannelMapper;
|
||||
}
|
||||
|
||||
void Disconnect()
|
||||
{
|
||||
AzToolsFramework::ViewportInteraction::ViewportMouseCursorRequestBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
// ViewportMouseCursorRequestBus overrides ...
|
||||
void BeginCursorCapture() override;
|
||||
void EndCursorCapture() override;
|
||||
bool IsMouseOver() const override;
|
||||
|
||||
private:
|
||||
AzToolsFramework::QtEventToAzInputMapper* m_inputChannelMapper = nullptr;
|
||||
};
|
||||
|
||||
void ViewportMouseCursorRequestImpl::BeginCursorCapture()
|
||||
{
|
||||
m_inputChannelMapper->SetCursorCaptureEnabled(true);
|
||||
}
|
||||
|
||||
void ViewportMouseCursorRequestImpl::EndCursorCapture()
|
||||
{
|
||||
m_inputChannelMapper->SetCursorCaptureEnabled(false);
|
||||
}
|
||||
|
||||
bool ViewportMouseCursorRequestImpl::IsMouseOver() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
class TestModularCameraViewportContextImpl : public AtomToolsFramework::ModularCameraViewportContext
|
||||
{
|
||||
public:
|
||||
AZ::Transform GetCameraTransform() const override
|
||||
{
|
||||
return m_cameraTransform;
|
||||
}
|
||||
|
||||
void SetCameraTransform(const AZ::Transform& transform) override
|
||||
{
|
||||
m_cameraTransform = transform;
|
||||
}
|
||||
|
||||
void ConnectViewMatrixChangedHandler(AZ::RPI::ViewportContext::MatrixChangedEvent::Handler&) override
|
||||
{
|
||||
// noop
|
||||
}
|
||||
|
||||
private:
|
||||
AZ::Transform m_cameraTransform = AZ::Transform::CreateIdentity();
|
||||
};
|
||||
|
||||
class ModularViewportCameraControllerFixture : public AllocatorsTestFixture
|
||||
{
|
||||
public:
|
||||
@@ -48,123 +108,159 @@ namespace UnitTest
|
||||
AllocatorsTestFixture::TearDown();
|
||||
}
|
||||
|
||||
void PrepareCollaborators()
|
||||
{
|
||||
AzFramework::NativeWindowHandle nativeWindowHandle = nullptr;
|
||||
|
||||
// listen for events signaled from QtEventToAzInputMapper and forward to the controller list
|
||||
QObject::connect(
|
||||
m_inputChannelMapper.get(), &AzToolsFramework::QtEventToAzInputMapper::InputChannelUpdated, m_rootWidget.get(),
|
||||
[this, nativeWindowHandle](const AzFramework::InputChannel* inputChannel, [[maybe_unused]] QEvent* event)
|
||||
{
|
||||
m_controllerList->HandleInputChannelEvent(
|
||||
AzFramework::ViewportControllerInputEvent{ TestViewportId, nativeWindowHandle, *inputChannel });
|
||||
});
|
||||
|
||||
m_mockWindowRequests.Connect(nativeWindowHandle);
|
||||
|
||||
using ::testing::Return;
|
||||
// note: WindowRequests is used internally by ModularViewportCameraController, this ensures it returns the viewport size we want
|
||||
ON_CALL(m_mockWindowRequests, GetClientAreaSize())
|
||||
.WillByDefault(Return(AzFramework::WindowSize(WidgetSize.width(), WidgetSize.height())));
|
||||
|
||||
// respond to begin/end cursor capture events
|
||||
m_viewportMouseCursorRequests.Connect(TestViewportId, m_inputChannelMapper.get());
|
||||
|
||||
// create editor modular camera
|
||||
auto controller = CreateModularViewportCameraController(TestViewportId);
|
||||
|
||||
// set some overrides for the test
|
||||
controller->SetCameraViewportContextBuilderCallback(
|
||||
[this](AZStd::unique_ptr<AtomToolsFramework::ModularCameraViewportContext>& cameraViewportContext)
|
||||
{
|
||||
cameraViewportContext = AZStd::make_unique<TestModularCameraViewportContextImpl>();
|
||||
m_cameraViewportContextView = cameraViewportContext.get();
|
||||
});
|
||||
|
||||
// disable smoothing in the test
|
||||
controller->SetCameraPropsBuilderCallback(
|
||||
[](AzFramework::CameraProps& cameraProps)
|
||||
{
|
||||
cameraProps.m_rotateSmoothingEnabledFn = []
|
||||
{
|
||||
return false;
|
||||
};
|
||||
|
||||
cameraProps.m_translateSmoothingEnabledFn = []
|
||||
{
|
||||
return false;
|
||||
};
|
||||
});
|
||||
|
||||
m_controllerList->Add(controller);
|
||||
}
|
||||
|
||||
void HaltCollaborators()
|
||||
{
|
||||
m_mockWindowRequests.Disconnect();
|
||||
m_viewportMouseCursorRequests.Disconnect();
|
||||
m_cameraViewportContextView = nullptr;
|
||||
}
|
||||
|
||||
void RepeatDiagonalMouseMovements(const AZStd::function<float()>& deltaTimeFn)
|
||||
{
|
||||
// move to the center of the screen
|
||||
auto start = QPoint(WidgetSize.width() / 2, WidgetSize.height() / 2);
|
||||
MouseMove(m_rootWidget.get(), start, QPoint(0, 0));
|
||||
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(deltaTimeFn()), AZ::ScriptTimePoint() });
|
||||
|
||||
// move mouse diagonally to top right, then to bottom left and back repeatedly
|
||||
auto current = start;
|
||||
auto halfDelta = QPoint(200, -200);
|
||||
const int iterationsPerDiagonal = 50;
|
||||
for (int diagonals = 0; diagonals < 80; ++diagonals)
|
||||
{
|
||||
for (int i = 0; i < iterationsPerDiagonal; ++i)
|
||||
{
|
||||
MousePressAndMove(m_rootWidget.get(), current, halfDelta / iterationsPerDiagonal, Qt::MouseButton::RightButton);
|
||||
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(deltaTimeFn()), AZ::ScriptTimePoint() });
|
||||
current += halfDelta / iterationsPerDiagonal;
|
||||
}
|
||||
|
||||
if (diagonals % 2 == 0)
|
||||
{
|
||||
halfDelta.setX(halfDelta.x() * -1);
|
||||
halfDelta.setY(halfDelta.y() * -1);
|
||||
}
|
||||
}
|
||||
|
||||
QTest::mouseRelease(m_rootWidget.get(), Qt::MouseButton::RightButton, Qt::KeyboardModifier::NoModifier, current);
|
||||
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(deltaTimeFn()), AZ::ScriptTimePoint() });
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<QWidget> m_rootWidget;
|
||||
AzFramework::ViewportControllerListPtr m_controllerList;
|
||||
AZStd::unique_ptr<AzToolsFramework::QtEventToAzInputMapper> m_inputChannelMapper;
|
||||
::testing::NiceMock<MockWindowRequests> m_mockWindowRequests;
|
||||
ViewportMouseCursorRequestImpl m_viewportMouseCursorRequests;
|
||||
AtomToolsFramework::ModularCameraViewportContext* m_cameraViewportContextView = nullptr;
|
||||
};
|
||||
|
||||
const AzFramework::ViewportId ModularViewportCameraControllerFixture::TestViewportId = AzFramework::ViewportId(0);
|
||||
|
||||
class TestModularCameraViewportContextImpl : public AtomToolsFramework::ModularCameraViewportContext
|
||||
TEST_F(ModularViewportCameraControllerFixture, MouseMovementDoesNotAccumulateExcessiveDriftInModularViewportCameraWithVaryingDeltaTime)
|
||||
{
|
||||
public:
|
||||
AZ::Transform GetCameraTransform() const override
|
||||
{
|
||||
return m_cameraTransform;
|
||||
}
|
||||
|
||||
void SetCameraTransform(const AZ::Transform& transform) override
|
||||
{
|
||||
m_cameraTransform = transform;
|
||||
}
|
||||
|
||||
void ConnectViewMatrixChangedHandler(AZ::RPI::ViewportContext::MatrixChangedEvent::Handler&) override
|
||||
{
|
||||
// noop
|
||||
}
|
||||
|
||||
private:
|
||||
AZ::Transform m_cameraTransform = AZ::Transform::CreateIdentity();
|
||||
};
|
||||
|
||||
TEST_F(ModularViewportCameraControllerFixture, Mouse_movement_does_not_accumulate_excessive_drift_in_modular_viewport_camera)
|
||||
{
|
||||
AzFramework::NativeWindowHandle nativeWindowHandle = nullptr;
|
||||
|
||||
const float deltaTime = 1.0f / 60.0f; // mimic 60fps
|
||||
|
||||
// Given
|
||||
// listen for events signaled from QtEventToAzInputMapper and forward to the controller list
|
||||
QObject::connect(
|
||||
m_inputChannelMapper.get(), &AzToolsFramework::QtEventToAzInputMapper::InputChannelUpdated, m_rootWidget.get(),
|
||||
[this, nativeWindowHandle](const AzFramework::InputChannel* inputChannel, [[maybe_unused]] QEvent* event)
|
||||
{
|
||||
m_controllerList->HandleInputChannelEvent(
|
||||
AzFramework::ViewportControllerInputEvent{ TestViewportId, nativeWindowHandle, *inputChannel });
|
||||
});
|
||||
|
||||
using ::testing::NiceMock;
|
||||
using ::testing::Return;
|
||||
|
||||
NiceMock<MockWindowRequests> mockWindowRequests;
|
||||
mockWindowRequests.Connect(nativeWindowHandle);
|
||||
|
||||
// note: WindowRequests is used internally by ModularViewportCameraController, this ensures it returns the viewport size we want
|
||||
ON_CALL(mockWindowRequests, GetClientAreaSize())
|
||||
.WillByDefault(Return(AzFramework::WindowSize(WidgetSize.width(), WidgetSize.height())));
|
||||
|
||||
// create editor modular camera
|
||||
auto controller = CreateModularViewportCameraController(TestViewportId);
|
||||
|
||||
// set some overrides for the test
|
||||
AtomToolsFramework::ModularCameraViewportContext* cameraViewportContextView = nullptr;
|
||||
controller->SetCameraViewportContextBuilderCallback(
|
||||
[&cameraViewportContextView](AZStd::unique_ptr<AtomToolsFramework::ModularCameraViewportContext>& cameraViewportContext)
|
||||
{
|
||||
cameraViewportContext = AZStd::make_unique<TestModularCameraViewportContextImpl>();
|
||||
cameraViewportContextView = cameraViewportContext.get();
|
||||
});
|
||||
|
||||
controller->SetCameraPropsBuilderCallback(
|
||||
[](AzFramework::CameraProps& cameraProps)
|
||||
{
|
||||
cameraProps.m_rotateSmoothingEnabledFn = []
|
||||
{
|
||||
return false;
|
||||
};
|
||||
|
||||
cameraProps.m_translateSmoothingEnabledFn = []
|
||||
{
|
||||
return false;
|
||||
};
|
||||
});
|
||||
|
||||
m_controllerList->Add(controller);
|
||||
|
||||
// move to the center of the screen
|
||||
auto start = QPoint(WidgetSize.width() / 2, WidgetSize.height() / 2);
|
||||
MouseMove(m_rootWidget.get(), start, QPoint(0, 0));
|
||||
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(deltaTime), AZ::ScriptTimePoint() });
|
||||
PrepareCollaborators();
|
||||
|
||||
// When
|
||||
// move mouse diagonally to top right, then to bottom left and back repeatedly
|
||||
auto current = start;
|
||||
auto halfDelta = QPoint(200, -200);
|
||||
const int iterationsPerDiagonal = 50;
|
||||
for (int diagonals = 0; diagonals < 80; ++diagonals)
|
||||
{
|
||||
for (int i = 0; i < iterationsPerDiagonal; ++i)
|
||||
RepeatDiagonalMouseMovements(
|
||||
[t = 0.0f]() mutable
|
||||
{
|
||||
MousePressAndMove(m_rootWidget.get(), current, halfDelta / iterationsPerDiagonal, Qt::MouseButton::RightButton);
|
||||
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(deltaTime), AZ::ScriptTimePoint() });
|
||||
current += halfDelta / iterationsPerDiagonal;
|
||||
}
|
||||
|
||||
if (diagonals % 2 == 0)
|
||||
{
|
||||
halfDelta.setX(halfDelta.x() * -1);
|
||||
halfDelta.setY(halfDelta.y() * -1);
|
||||
}
|
||||
}
|
||||
|
||||
QTest::mouseRelease(m_rootWidget.get(), Qt::MouseButton::RightButton, Qt::KeyboardModifier::NoModifier, current);
|
||||
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(deltaTime), AZ::ScriptTimePoint() });
|
||||
// vary between 30 and 50 fps (40 +/- 10)
|
||||
const float fps = 40.0f + (10.0f * AZStd::sin(t));
|
||||
t += AZ::DegToRad(5.0f);
|
||||
return 1.0f / fps;
|
||||
});
|
||||
|
||||
// Then
|
||||
// ensure the camera rotation is the identity (no significant drift has occurred as we moved the mouse)
|
||||
const AZ::Transform cameraRotation = cameraViewportContextView->GetCameraTransform();
|
||||
const AZ::Transform cameraRotation = m_cameraViewportContextView->GetCameraTransform();
|
||||
EXPECT_THAT(cameraRotation.GetRotation(), IsClose(AZ::Quaternion::CreateIdentity()));
|
||||
|
||||
mockWindowRequests.Disconnect();
|
||||
// Clean-up
|
||||
HaltCollaborators();
|
||||
}
|
||||
|
||||
class ModularViewportCameraControllerDeltaTimeParamFixture
|
||||
: public ModularViewportCameraControllerFixture
|
||||
, public ::testing::WithParamInterface<float> // delta time
|
||||
{
|
||||
};
|
||||
|
||||
TEST_P(
|
||||
ModularViewportCameraControllerDeltaTimeParamFixture,
|
||||
MouseMovementDoesNotAccumulateExcessiveDriftInModularViewportCameraWithFixedDeltaTime)
|
||||
{
|
||||
// Given
|
||||
PrepareCollaborators();
|
||||
|
||||
// When
|
||||
RepeatDiagonalMouseMovements(
|
||||
[this]
|
||||
{
|
||||
return GetParam();
|
||||
});
|
||||
|
||||
// Then
|
||||
// ensure the camera rotation is the identity (no significant drift has occurred as we moved the mouse)
|
||||
const AZ::Transform cameraRotation = m_cameraViewportContextView->GetCameraTransform();
|
||||
EXPECT_THAT(cameraRotation.GetRotation(), IsClose(AZ::Quaternion::CreateIdentity()));
|
||||
|
||||
// Clean-up
|
||||
HaltCollaborators();
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
All, ModularViewportCameraControllerDeltaTimeParamFixture, testing::Values(1.0f / 60.0f, 1.0f / 50.0f, 1.0f / 30.0f));
|
||||
} // namespace UnitTest
|
||||
|
||||
@@ -35,6 +35,7 @@ namespace AzFramework
|
||||
//! Predefined input event listener priority, used to sort handlers from highest to lowest
|
||||
inline static AZ::s32 GetPriorityFirst() { return std::numeric_limits<AZ::s32>::max(); }
|
||||
inline static AZ::s32 GetPriorityDebug() { return (GetPriorityFirst() / 4) * 3; }
|
||||
inline static AZ::s32 GetPriorityDebugUI() { return (GetPriorityFirst() / 8) * 5; }
|
||||
inline static AZ::s32 GetPriorityUI() { return GetPriorityFirst() / 2; }
|
||||
inline static AZ::s32 GetPriorityDefault() { return 0; }
|
||||
inline static AZ::s32 GetPriorityLast() { return std::numeric_limits<AZ::s32>::min(); }
|
||||
|
||||
@@ -28,6 +28,7 @@ namespace AzFramework
|
||||
//! Predefined text event listener priority, used to sort handlers from highest to lowest
|
||||
inline static AZ::s32 GetPriorityFirst() { return std::numeric_limits<AZ::s32>::max(); }
|
||||
inline static AZ::s32 GetPriorityDebug() { return (GetPriorityFirst() / 4) * 3; }
|
||||
inline static AZ::s32 GetPriorityDebugUI() { return (GetPriorityFirst() / 8) * 5; }
|
||||
inline static AZ::s32 GetPriorityUI() { return GetPriorityFirst() / 2; }
|
||||
inline static AZ::s32 GetPriorityDefault() { return 0; }
|
||||
inline static AZ::s32 GetPriorityLast() { return std::numeric_limits<AZ::s32>::min(); }
|
||||
|
||||
@@ -802,7 +802,6 @@ namespace AzFramework
|
||||
{
|
||||
return VerticalMotionEvent{ aznumeric_cast<int>(inputChannel.GetValue()) };
|
||||
}
|
||||
|
||||
else if (inputChannelId == InputDeviceMouse::Movement::Z)
|
||||
{
|
||||
return ScrollEvent{ inputChannel.GetValue() };
|
||||
|
||||
@@ -650,7 +650,10 @@ namespace AzToolsFramework
|
||||
|
||||
AZStd::unique_ptr<AZ::Entity> Instance::DetachContainerEntity()
|
||||
{
|
||||
m_instanceEntityMapper->UnregisterEntity(m_containerEntity->GetId());
|
||||
if (m_containerEntity)
|
||||
{
|
||||
m_instanceEntityMapper->UnregisterEntity(m_containerEntity->GetId());
|
||||
}
|
||||
return AZStd::move(m_containerEntity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,16 +65,24 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
AzFramework::Spawnable::EntityList& entities = spawnable->GetEntities();
|
||||
for (auto it = entities.begin(); it != entities.end(); )
|
||||
{
|
||||
(*it)->InvalidateDependencies();
|
||||
AZ::Entity::DependencySortOutcome evaluation = (*it)->EvaluateDependenciesGetDetails();
|
||||
if (evaluation.IsSuccess())
|
||||
if (*it)
|
||||
{
|
||||
++it;
|
||||
(*it)->InvalidateDependencies();
|
||||
AZ::Entity::DependencySortOutcome evaluation = (*it)->EvaluateDependenciesGetDetails();
|
||||
if (evaluation.IsSuccess())
|
||||
{
|
||||
++it;
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error(
|
||||
"Prefabs", false, "Entity '%s' %s cannot be activated for the following reason: %s", (*it)->GetName().c_str(),
|
||||
(*it)->GetId().ToString().c_str(), evaluation.GetError().m_message.c_str());
|
||||
it = entities.erase(it);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error("Prefabs", false, "Entity '%s' %s cannot be activated for the following reason: %s",
|
||||
(*it)->GetName().c_str(), (*it)->GetId().ToString().c_str(), evaluation.GetError().m_message.c_str());
|
||||
it = entities.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,9 +275,7 @@ namespace AzToolsFramework
|
||||
virtual void BeginCursorCapture() = 0;
|
||||
//! Restores the cursor and ends locking it in place, allowing it to be moved freely.
|
||||
virtual void EndCursorCapture() = 0;
|
||||
//! Gets the most recent recorded cursor position in the viewport in screen space coordinates.
|
||||
virtual AzFramework::ScreenPoint ViewportCursorScreenPosition() = 0;
|
||||
//! Is mouse over viewport.
|
||||
//! Is the mouse over the viewport.
|
||||
virtual bool IsMouseOver() const = 0;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -20,8 +20,7 @@ QPushButton:focus {
|
||||
QTabBar {
|
||||
background-color: transparent;
|
||||
}
|
||||
QTabWidget::tab-bar
|
||||
{
|
||||
QTabWidget::tab-bar {
|
||||
left: 78px; /* make room for the logo */
|
||||
}
|
||||
QTabBar::tab {
|
||||
@@ -32,27 +31,35 @@ QTabBar::tab {
|
||||
margin-right:40px;
|
||||
border-bottom: 3px solid transparent;
|
||||
}
|
||||
QTabBar::tab:text
|
||||
{
|
||||
QTabBar::tab:text {
|
||||
text-align:left;
|
||||
}
|
||||
QTabWidget::pane {
|
||||
background-color: #333333;
|
||||
border:0 none;
|
||||
}
|
||||
QTabBar::tab:selected
|
||||
{
|
||||
QTabBar::tab:selected {
|
||||
background-color: transparent;
|
||||
border-bottom: 3px solid #1e70eb;
|
||||
color: #1e70eb;
|
||||
font-weight: 500;
|
||||
}
|
||||
QTabBar::tab:hover
|
||||
{
|
||||
QTabBar::tab:hover {
|
||||
color: #1e70eb;
|
||||
font-weight: 500;
|
||||
}
|
||||
QTabBar::tab:pressed
|
||||
{
|
||||
QTabBar::tab:pressed {
|
||||
color: #0e60eb;
|
||||
}
|
||||
QTabBar::focus {
|
||||
outline: 0px;
|
||||
outline: none;
|
||||
outline-style: none;
|
||||
}
|
||||
QTabBar::tab:focus {
|
||||
background-color: #525252;
|
||||
color: #4082eb;
|
||||
}
|
||||
|
||||
/************** General (Forms) **************/
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@ namespace O3DE::ProjectManager
|
||||
gemNames.reserve(gems.size());
|
||||
for (const QModelIndex& modelIndex : gems)
|
||||
{
|
||||
gemNames.push_back(GemModel::GetName(modelIndex));
|
||||
gemNames.push_back(GemModel::GetDisplayName(modelIndex));
|
||||
}
|
||||
return gemNames;
|
||||
}
|
||||
|
||||
@@ -156,7 +156,7 @@ namespace O3DE::ProjectManager
|
||||
if (!result.IsSuccess())
|
||||
{
|
||||
QMessageBox::critical(nullptr, "Operation failed",
|
||||
QString("Cannot add gem %1 to project.\n\nError:\n%2").arg(GemModel::GetName(modelIndex), result.GetError().c_str()));
|
||||
QString("Cannot add gem %1 to project.\n\nError:\n%2").arg(GemModel::GetDisplayName(modelIndex), result.GetError().c_str()));
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -169,7 +169,7 @@ namespace O3DE::ProjectManager
|
||||
if (!result.IsSuccess())
|
||||
{
|
||||
QMessageBox::critical(nullptr, "Operation failed",
|
||||
QString("Cannot remove gem %1 from project.\n\nError:\n%2").arg(GemModel::GetName(modelIndex), result.GetError().c_str()));
|
||||
QString("Cannot remove gem %1 from project.\n\nError:\n%2").arg(GemModel::GetDisplayName(modelIndex), result.GetError().c_str()));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace O3DE::ProjectManager
|
||||
m_mainWidget->hide();
|
||||
}
|
||||
|
||||
m_nameLabel->setText(m_model->GetName(modelIndex));
|
||||
m_nameLabel->setText(m_model->GetDisplayName(modelIndex));
|
||||
m_creatorLabel->setText(m_model->GetCreator(modelIndex));
|
||||
|
||||
m_summaryLabel->setText(m_model->GetSummary(modelIndex));
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace O3DE::ProjectManager
|
||||
}
|
||||
|
||||
// Gem name
|
||||
QString gemName = GemModel::GetName(modelIndex);
|
||||
QString gemName = GemModel::GetDisplayName(modelIndex);
|
||||
QFont gemNameFont(options.font);
|
||||
const int firstColumnMaxTextWidth = s_summaryStartX - 30;
|
||||
gemNameFont.setPixelSize(static_cast<int>(s_gemNameFontSize));
|
||||
|
||||
@@ -30,6 +30,7 @@ namespace O3DE::ProjectManager
|
||||
item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
|
||||
|
||||
item->setData(gemInfo.m_name, RoleName);
|
||||
item->setData(gemInfo.m_displayName, RoleDisplayName);
|
||||
item->setData(gemInfo.m_creator, RoleCreator);
|
||||
item->setData(gemInfo.m_gemOrigin, RoleGemOrigin);
|
||||
item->setData(aznumeric_cast<int>(gemInfo.m_platforms), RolePlatforms);
|
||||
@@ -64,6 +65,20 @@ namespace O3DE::ProjectManager
|
||||
return modelIndex.data(RoleName).toString();
|
||||
}
|
||||
|
||||
QString GemModel::GetDisplayName(const QModelIndex& modelIndex)
|
||||
{
|
||||
QString displayName = modelIndex.data(RoleDisplayName).toString();
|
||||
|
||||
if (displayName.isEmpty())
|
||||
{
|
||||
return GetName(modelIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
return displayName;
|
||||
}
|
||||
}
|
||||
|
||||
QString GemModel::GetCreator(const QModelIndex& modelIndex)
|
||||
{
|
||||
return modelIndex.data(RoleCreator).toString();
|
||||
@@ -117,7 +132,7 @@ namespace O3DE::ProjectManager
|
||||
QModelIndex modelIndex = FindIndexByNameString(dependingGemString);
|
||||
if (modelIndex.isValid())
|
||||
{
|
||||
dependingGemString = GetName(modelIndex);
|
||||
dependingGemString = GetDisplayName(modelIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ namespace O3DE::ProjectManager
|
||||
QStringList GetConflictingGemNames(const QModelIndex& modelIndex);
|
||||
|
||||
static QString GetName(const QModelIndex& modelIndex);
|
||||
static QString GetDisplayName(const QModelIndex& modelIndex);
|
||||
static QString GetCreator(const QModelIndex& modelIndex);
|
||||
static GemInfo::GemOrigin GetGemOrigin(const QModelIndex& modelIndex);
|
||||
static GemInfo::Platforms GetPlatforms(const QModelIndex& modelIndex);
|
||||
@@ -69,6 +70,7 @@ namespace O3DE::ProjectManager
|
||||
enum UserRole
|
||||
{
|
||||
RoleName = Qt::UserRole,
|
||||
RoleDisplayName,
|
||||
RoleCreator,
|
||||
RoleGemOrigin,
|
||||
RolePlatforms,
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace O3DE::ProjectManager
|
||||
painter->fillRect(itemRect, itemBackgroundColor);
|
||||
|
||||
// Gem name
|
||||
QString gemName = GemModel::GetName(modelIndex);
|
||||
QString gemName = GemModel::GetDisplayName(modelIndex);
|
||||
QFont gemNameFont(options.font);
|
||||
const int firstColumnMaxTextWidth = s_summaryStartX - 30;
|
||||
gemName = QFontMetrics(gemNameFont).elidedText(gemName, Qt::TextElideMode::ElideRight, firstColumnMaxTextWidth);
|
||||
|
||||
@@ -28,9 +28,26 @@ namespace O3DE::ProjectManager
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_sourceModel->GetName(sourceIndex).contains(m_searchString, Qt::CaseInsensitive))
|
||||
// Search Bar
|
||||
if (!m_sourceModel->GetDisplayName(sourceIndex).contains(m_searchString, Qt::CaseInsensitive) &&
|
||||
!m_sourceModel->GetName(sourceIndex).contains(m_searchString, Qt::CaseInsensitive) &&
|
||||
!m_sourceModel->GetCreator(sourceIndex).contains(m_searchString, Qt::CaseInsensitive) &&
|
||||
!m_sourceModel->GetSummary(sourceIndex).contains(m_searchString, Qt::CaseInsensitive))
|
||||
{
|
||||
return false;
|
||||
bool foundFeature = false;
|
||||
for (const QString& feature : m_sourceModel->GetFeatures(sourceIndex))
|
||||
{
|
||||
if (feature.contains(m_searchString, Qt::CaseInsensitive))
|
||||
{
|
||||
foundFeature = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundFeature)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Gem status
|
||||
|
||||
@@ -52,6 +52,7 @@ namespace O3DE::ProjectManager
|
||||
|
||||
if (projectButton)
|
||||
{
|
||||
projectButton->SetProjectBuilding();
|
||||
projectButton->SetProjectButtonAction(tr("Cancel Build"), [this] { HandleCancel(); });
|
||||
|
||||
if (m_lastProgress != 0)
|
||||
@@ -111,6 +112,10 @@ namespace O3DE::ProjectManager
|
||||
emit Done(false);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_projectInfo.m_buildFailed = false;
|
||||
}
|
||||
|
||||
emit Done(true);
|
||||
}
|
||||
|
||||
@@ -162,22 +162,9 @@ namespace O3DE::ProjectManager
|
||||
QDesktopServices::openUrl(m_logUrl);
|
||||
}
|
||||
|
||||
ProjectButton::ProjectButton(const ProjectInfo& projectInfo, QWidget* parent, bool processing)
|
||||
ProjectButton::ProjectButton(const ProjectInfo& projectInfo, QWidget* parent)
|
||||
: QFrame(parent)
|
||||
, m_projectInfo(projectInfo)
|
||||
{
|
||||
BaseSetup();
|
||||
if (processing)
|
||||
{
|
||||
ProcessingSetup();
|
||||
}
|
||||
else
|
||||
{
|
||||
ReadySetup();
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectButton::BaseSetup()
|
||||
{
|
||||
setObjectName("projectButton");
|
||||
|
||||
@@ -199,50 +186,63 @@ namespace O3DE::ProjectManager
|
||||
}
|
||||
m_projectImageLabel->setPixmap(QPixmap(projectPreviewPath).scaled(m_projectImageLabel->size(), Qt::KeepAspectRatioByExpanding));
|
||||
|
||||
m_projectFooter = new QFrame(this);
|
||||
QFrame* projectFooter = new QFrame(this);
|
||||
QHBoxLayout* hLayout = new QHBoxLayout();
|
||||
hLayout->setContentsMargins(0, 0, 0, 0);
|
||||
m_projectFooter->setLayout(hLayout);
|
||||
projectFooter->setLayout(hLayout);
|
||||
{
|
||||
QLabel* projectNameLabel = new QLabel(m_projectInfo.GetProjectDisplayName(), this);
|
||||
hLayout->addWidget(projectNameLabel);
|
||||
|
||||
QMenu* menu = new QMenu(this);
|
||||
menu->addAction(tr("Edit Project Settings..."), this, [this]() { emit EditProject(m_projectInfo.m_path); });
|
||||
menu->addAction(tr("Build"), this, [this]() { emit BuildProject(m_projectInfo); });
|
||||
menu->addSeparator();
|
||||
menu->addAction(tr("Open Project folder..."), this, [this]()
|
||||
{
|
||||
AzQtComponents::ShowFileOnDesktop(m_projectInfo.m_path);
|
||||
});
|
||||
menu->addSeparator();
|
||||
menu->addAction(tr("Duplicate"), this, [this]() { emit CopyProject(m_projectInfo); });
|
||||
menu->addSeparator();
|
||||
menu->addAction(tr("Remove from O3DE"), this, [this]() { emit RemoveProject(m_projectInfo.m_path); });
|
||||
menu->addAction(tr("Delete this Project"), this, [this]() { emit DeleteProject(m_projectInfo.m_path); });
|
||||
|
||||
m_projectMenuButton = new QPushButton(this);
|
||||
m_projectMenuButton->setObjectName("projectMenuButton");
|
||||
m_projectMenuButton->setMenu(menu);
|
||||
hLayout->addWidget(m_projectMenuButton);
|
||||
}
|
||||
|
||||
vLayout->addWidget(m_projectFooter);
|
||||
vLayout->addWidget(projectFooter);
|
||||
|
||||
connect(m_projectImageLabel->GetOpenEditorButton(), &QPushButton::clicked, [this](){ emit OpenProject(m_projectInfo.m_path); });
|
||||
}
|
||||
|
||||
void ProjectButton::ProcessingSetup()
|
||||
const ProjectInfo& ProjectButton::GetProjectInfo() const
|
||||
{
|
||||
m_projectImageLabel->SetEnabled(false);
|
||||
m_projectImageLabel->SetOverlayText(tr("Processing...\n\n"));
|
||||
return m_projectInfo;
|
||||
}
|
||||
|
||||
void ProjectButton::RestoreDefaultState()
|
||||
{
|
||||
m_projectImageLabel->SetEnabled(true);
|
||||
m_projectImageLabel->SetOverlayText("");
|
||||
m_projectMenuButton->setVisible(true);
|
||||
|
||||
QProgressBar* progressBar = m_projectImageLabel->GetProgressBar();
|
||||
progressBar->setVisible(true);
|
||||
progressBar->setVisible(false);
|
||||
progressBar->setValue(0);
|
||||
}
|
||||
|
||||
void ProjectButton::ReadySetup()
|
||||
{
|
||||
connect(m_projectImageLabel->GetOpenEditorButton(), &QPushButton::clicked, [this](){ emit OpenProject(m_projectInfo.m_path); });
|
||||
QPushButton* projectActionButton = m_projectImageLabel->GetActionButton();
|
||||
projectActionButton->setVisible(false);
|
||||
if (m_actionButtonConnection)
|
||||
{
|
||||
disconnect(m_actionButtonConnection);
|
||||
}
|
||||
|
||||
QMenu* menu = new QMenu(this);
|
||||
menu->addAction(tr("Edit Project Settings..."), this, [this]() { emit EditProject(m_projectInfo.m_path); });
|
||||
menu->addAction(tr("Build"), this, [this]() { emit BuildProject(m_projectInfo); });
|
||||
menu->addSeparator();
|
||||
menu->addAction(tr("Open Project folder..."), this, [this]()
|
||||
{
|
||||
AzQtComponents::ShowFileOnDesktop(m_projectInfo.m_path);
|
||||
});
|
||||
menu->addSeparator();
|
||||
menu->addAction(tr("Duplicate"), this, [this]() { emit CopyProject(m_projectInfo); });
|
||||
menu->addSeparator();
|
||||
menu->addAction(tr("Remove from O3DE"), this, [this]() { emit RemoveProject(m_projectInfo.m_path); });
|
||||
menu->addAction(tr("Delete this Project"), this, [this]() { emit DeleteProject(m_projectInfo.m_path); });
|
||||
|
||||
QPushButton* projectMenuButton = new QPushButton(this);
|
||||
projectMenuButton->setObjectName("projectMenuButton");
|
||||
projectMenuButton->setMenu(menu);
|
||||
m_projectFooter->layout()->addWidget(projectMenuButton);
|
||||
m_projectImageLabel->GetWarningIcon()->setVisible(false);
|
||||
m_projectImageLabel->GetWarningLabel()->setVisible(false);
|
||||
}
|
||||
|
||||
void ProjectButton::SetProjectButtonAction(const QString& text, AZStd::function<void()> lambda)
|
||||
@@ -292,9 +292,15 @@ namespace O3DE::ProjectManager
|
||||
SetProjectButtonAction(tr("Build Project"), [this]() { emit BuildProject(m_projectInfo); });
|
||||
}
|
||||
|
||||
void ProjectButton::BuildThisProject()
|
||||
void ProjectButton::SetProjectBuilding()
|
||||
{
|
||||
emit BuildProject(m_projectInfo);
|
||||
m_projectImageLabel->SetEnabled(false);
|
||||
m_projectImageLabel->SetOverlayText(tr("Building...\n\n"));
|
||||
m_projectMenuButton->setVisible(false);
|
||||
|
||||
QProgressBar* progressBar = m_projectImageLabel->GetProgressBar();
|
||||
progressBar->setVisible(true);
|
||||
progressBar->setValue(0);
|
||||
}
|
||||
|
||||
void ProjectButton::SetLaunchButtonEnabled(bool enabled)
|
||||
|
||||
@@ -56,13 +56,14 @@ namespace O3DE::ProjectManager
|
||||
void OnLinkActivated(const QString& link);
|
||||
|
||||
private:
|
||||
QVBoxLayout* m_buildOverlayLayout;
|
||||
QLabel* m_overlayLabel;
|
||||
QProgressBar* m_progressBar;
|
||||
QPushButton* m_openEditorButton;
|
||||
QPushButton* m_actionButton;
|
||||
QLabel* m_warningText;
|
||||
QLabel* m_warningIcon;
|
||||
QVBoxLayout* m_buildOverlayLayout = nullptr;
|
||||
QLabel* m_overlayLabel = nullptr;
|
||||
QProgressBar* m_progressBar = nullptr;
|
||||
QPushButton* m_openEditorButton = nullptr;
|
||||
QPushButton* m_actionButton = nullptr;
|
||||
QLabel* m_warningText = nullptr;
|
||||
QLabel* m_warningIcon = nullptr;
|
||||
|
||||
QUrl m_logUrl;
|
||||
bool m_enabled = true;
|
||||
};
|
||||
@@ -73,13 +74,18 @@ namespace O3DE::ProjectManager
|
||||
Q_OBJECT // AUTOMOC
|
||||
|
||||
public:
|
||||
explicit ProjectButton(const ProjectInfo& m_projectInfo, QWidget* parent = nullptr, bool processing = false);
|
||||
explicit ProjectButton(const ProjectInfo& m_projectInfo, QWidget* parent = nullptr);
|
||||
~ProjectButton() = default;
|
||||
|
||||
const ProjectInfo& GetProjectInfo() const;
|
||||
|
||||
void RestoreDefaultState();
|
||||
|
||||
void SetProjectButtonAction(const QString& text, AZStd::function<void()> lambda);
|
||||
void SetProjectBuildButtonAction();
|
||||
void SetBuildLogsLink(const QUrl& logUrl);
|
||||
void ShowBuildFailed(bool show, const QUrl& logUrl);
|
||||
void SetProjectBuilding();
|
||||
|
||||
void SetLaunchButtonEnabled(bool enabled);
|
||||
void SetButtonOverlayText(const QString& text);
|
||||
@@ -95,17 +101,14 @@ namespace O3DE::ProjectManager
|
||||
void BuildProject(const ProjectInfo& projectInfo);
|
||||
|
||||
private:
|
||||
void BaseSetup();
|
||||
void ProcessingSetup();
|
||||
void ReadySetup();
|
||||
void enterEvent(QEvent* event) override;
|
||||
void leaveEvent(QEvent* event) override;
|
||||
void BuildThisProject();
|
||||
|
||||
ProjectInfo m_projectInfo;
|
||||
LabelButton* m_projectImageLabel;
|
||||
QFrame* m_projectFooter;
|
||||
QLayout* m_requiresBuildLayout;
|
||||
|
||||
LabelButton* m_projectImageLabel = nullptr;
|
||||
QPushButton* m_projectMenuButton = nullptr;
|
||||
QLayout* m_requiresBuildLayout = nullptr;
|
||||
|
||||
QMetaObject::Connection m_actionButtonConnection;
|
||||
};
|
||||
|
||||
@@ -68,9 +68,7 @@ namespace O3DE::ProjectManager
|
||||
}
|
||||
|
||||
ProjectsScreen::~ProjectsScreen()
|
||||
|
||||
{
|
||||
delete m_currentBuilder;
|
||||
}
|
||||
|
||||
QFrame* ProjectsScreen::CreateFirstTimeContent()
|
||||
@@ -114,10 +112,8 @@ namespace O3DE::ProjectManager
|
||||
return frame;
|
||||
}
|
||||
|
||||
QFrame* ProjectsScreen::CreateProjectsContent(QString buildProjectPath, ProjectButton** projectButton)
|
||||
QFrame* ProjectsScreen::CreateProjectsContent()
|
||||
{
|
||||
RemoveInvalidProjects();
|
||||
|
||||
QFrame* frame = new QFrame(this);
|
||||
frame->setObjectName("projectsContent");
|
||||
{
|
||||
@@ -126,7 +122,7 @@ namespace O3DE::ProjectManager
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
frame->setLayout(layout);
|
||||
|
||||
QFrame* header = new QFrame(this);
|
||||
QFrame* header = new QFrame(frame);
|
||||
QHBoxLayout* headerLayout = new QHBoxLayout();
|
||||
{
|
||||
QLabel* titleLabel = new QLabel(tr("My Projects"), this);
|
||||
@@ -150,87 +146,34 @@ namespace O3DE::ProjectManager
|
||||
|
||||
layout->addWidget(header);
|
||||
|
||||
// Get all projects and create a horizontal scrolling list of them
|
||||
auto projectsResult = PythonBindingsInterface::Get()->GetProjects();
|
||||
if (projectsResult.IsSuccess() && !projectsResult.GetValue().isEmpty())
|
||||
{
|
||||
QScrollArea* projectsScrollArea = new QScrollArea(this);
|
||||
QWidget* scrollWidget = new QWidget();
|
||||
QScrollArea* projectsScrollArea = new QScrollArea(this);
|
||||
QWidget* scrollWidget = new QWidget();
|
||||
|
||||
FlowLayout* flowLayout = new FlowLayout(0, s_spacerSize, s_spacerSize);
|
||||
scrollWidget->setLayout(flowLayout);
|
||||
m_projectsFlowLayout = new FlowLayout(0, s_spacerSize, s_spacerSize);
|
||||
scrollWidget->setLayout(m_projectsFlowLayout);
|
||||
|
||||
projectsScrollArea->setWidget(scrollWidget);
|
||||
projectsScrollArea->setWidgetResizable(true);
|
||||
projectsScrollArea->setWidget(scrollWidget);
|
||||
projectsScrollArea->setWidgetResizable(true);
|
||||
|
||||
QVector<ProjectInfo> nonProcessingProjects;
|
||||
buildProjectPath = QDir::fromNativeSeparators(buildProjectPath);
|
||||
for (auto& project : projectsResult.GetValue())
|
||||
{
|
||||
if (projectButton && !*projectButton)
|
||||
{
|
||||
if (QDir::fromNativeSeparators(project.m_path) == buildProjectPath)
|
||||
{
|
||||
*projectButton = CreateProjectButton(project, flowLayout, true);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
ResetProjectsContent();
|
||||
|
||||
nonProcessingProjects.append(project);
|
||||
}
|
||||
|
||||
for (auto& project : nonProcessingProjects)
|
||||
{
|
||||
ProjectButton* projectButtonWidget = CreateProjectButton(project, flowLayout);
|
||||
|
||||
if (BuildQueueContainsProject(project.m_path))
|
||||
{
|
||||
projectButtonWidget->SetProjectButtonAction(tr("Cancel Queued Build"),
|
||||
[this, project]
|
||||
{
|
||||
UnqueueBuildProject(project);
|
||||
SuggestBuildProjectMsg(project, false);
|
||||
});
|
||||
}
|
||||
else if (RequiresBuildProjectIterator(project.m_path) != m_requiresBuild.end())
|
||||
{
|
||||
auto buildProjectIterator = RequiresBuildProjectIterator(project.m_path);
|
||||
if (buildProjectIterator != m_requiresBuild.end())
|
||||
{
|
||||
if (buildProjectIterator->m_buildFailed)
|
||||
{
|
||||
projectButtonWidget->ShowBuildFailed(true, buildProjectIterator->m_logUrl);
|
||||
}
|
||||
else
|
||||
{
|
||||
projectButtonWidget->SetProjectBuildButtonAction();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
layout->addWidget(projectsScrollArea);
|
||||
}
|
||||
layout->addWidget(projectsScrollArea);
|
||||
}
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
ProjectButton* ProjectsScreen::CreateProjectButton(ProjectInfo& project, QLayout* flowLayout, bool processing)
|
||||
ProjectButton* ProjectsScreen::CreateProjectButton(const ProjectInfo& project)
|
||||
{
|
||||
ProjectButton* projectButton = new ProjectButton(project, this, processing);
|
||||
ProjectButton* projectButton = new ProjectButton(project, this);
|
||||
m_projectButtons.insert(project.m_path, projectButton);
|
||||
m_projectsFlowLayout->addWidget(projectButton);
|
||||
|
||||
flowLayout->addWidget(projectButton);
|
||||
|
||||
if (!processing)
|
||||
{
|
||||
connect(projectButton, &ProjectButton::OpenProject, this, &ProjectsScreen::HandleOpenProject);
|
||||
connect(projectButton, &ProjectButton::EditProject, this, &ProjectsScreen::HandleEditProject);
|
||||
connect(projectButton, &ProjectButton::CopyProject, this, &ProjectsScreen::HandleCopyProject);
|
||||
connect(projectButton, &ProjectButton::RemoveProject, this, &ProjectsScreen::HandleRemoveProject);
|
||||
connect(projectButton, &ProjectButton::DeleteProject, this, &ProjectsScreen::HandleDeleteProject);
|
||||
}
|
||||
connect(projectButton, &ProjectButton::OpenProject, this, &ProjectsScreen::HandleOpenProject);
|
||||
connect(projectButton, &ProjectButton::EditProject, this, &ProjectsScreen::HandleEditProject);
|
||||
connect(projectButton, &ProjectButton::CopyProject, this, &ProjectsScreen::HandleCopyProject);
|
||||
connect(projectButton, &ProjectButton::RemoveProject, this, &ProjectsScreen::HandleRemoveProject);
|
||||
connect(projectButton, &ProjectButton::DeleteProject, this, &ProjectsScreen::HandleDeleteProject);
|
||||
connect(projectButton, &ProjectButton::BuildProject, this, &ProjectsScreen::QueueBuildProject);
|
||||
|
||||
return projectButton;
|
||||
@@ -238,29 +181,128 @@ namespace O3DE::ProjectManager
|
||||
|
||||
void ProjectsScreen::ResetProjectsContent()
|
||||
{
|
||||
// refresh the projects content by re-creating it for now
|
||||
if (m_projectsContent)
|
||||
RemoveInvalidProjects();
|
||||
|
||||
// Get all projects and create a vertical scrolling list of them
|
||||
// Sort building and queued projects first
|
||||
auto projectsResult = PythonBindingsInterface::Get()->GetProjects();
|
||||
if (projectsResult.IsSuccess() && !projectsResult.GetValue().isEmpty())
|
||||
{
|
||||
m_stack->removeWidget(m_projectsContent);
|
||||
m_projectsContent->deleteLater();
|
||||
QVector<ProjectInfo> projectsVector = projectsResult.GetValue();
|
||||
// If a project path is in this set then the button for it will be kept
|
||||
QSet<QString> keepProject;
|
||||
for (const ProjectInfo& project : projectsVector)
|
||||
{
|
||||
keepProject.insert(project.m_path);
|
||||
}
|
||||
|
||||
// Clear flow and delete buttons for removed projects
|
||||
auto projectButtonsIter = m_projectButtons.begin();
|
||||
while (projectButtonsIter != m_projectButtons.end())
|
||||
{
|
||||
m_projectsFlowLayout->removeWidget(projectButtonsIter.value());
|
||||
|
||||
if (!keepProject.contains(projectButtonsIter.key()))
|
||||
{
|
||||
projectButtonsIter = m_projectButtons.erase(projectButtonsIter);
|
||||
}
|
||||
else
|
||||
{
|
||||
++projectButtonsIter;
|
||||
}
|
||||
}
|
||||
|
||||
QString buildProjectPath = "";
|
||||
if (m_currentBuilder)
|
||||
{
|
||||
buildProjectPath = m_currentBuilder->GetProjectInfo().m_path;
|
||||
}
|
||||
|
||||
// Put currently building project in front, then queued projects, then sorts alphabetically
|
||||
std::sort(projectsVector.begin(), projectsVector.end(), [buildProjectPath, this](const ProjectInfo& arg1, const ProjectInfo& arg2)
|
||||
{
|
||||
if (arg1.m_path == buildProjectPath)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (arg2.m_path == buildProjectPath)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool arg1InBuildQueue = BuildQueueContainsProject(arg1.m_path);
|
||||
bool arg2InBuildQueue = BuildQueueContainsProject(arg2.m_path);
|
||||
if (arg1InBuildQueue && !arg2InBuildQueue)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (!arg1InBuildQueue && arg2InBuildQueue)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return arg1.m_displayName.toLower() < arg2.m_displayName.toLower();
|
||||
}
|
||||
});
|
||||
|
||||
// Add any missing project buttons and restore buttons to default state
|
||||
for (const ProjectInfo& project : projectsVector)
|
||||
{
|
||||
if (!m_projectButtons.contains(project.m_path))
|
||||
{
|
||||
m_projectButtons.insert(project.m_path, CreateProjectButton(project));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto projectButtonIter = m_projectButtons.find(project.m_path);
|
||||
if (projectButtonIter != m_projectButtons.end())
|
||||
{
|
||||
projectButtonIter.value()->RestoreDefaultState();
|
||||
m_projectsFlowLayout->addWidget(projectButtonIter.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Setup building button again
|
||||
auto buildProjectIter = m_projectButtons.find(buildProjectPath);
|
||||
if (buildProjectIter != m_projectButtons.end())
|
||||
{
|
||||
m_currentBuilder->SetProjectButton(buildProjectIter.value());
|
||||
}
|
||||
|
||||
for (const ProjectInfo& project : m_buildQueue)
|
||||
{
|
||||
auto projectIter = m_projectButtons.find(project.m_path);
|
||||
if (projectIter != m_projectButtons.end())
|
||||
{
|
||||
projectIter.value()->SetProjectButtonAction(
|
||||
tr("Cancel Queued Build"),
|
||||
[this, project]
|
||||
{
|
||||
UnqueueBuildProject(project);
|
||||
SuggestBuildProjectMsg(project, false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for (const ProjectInfo& project : m_requiresBuild)
|
||||
{
|
||||
auto projectIter = m_projectButtons.find(project.m_path);
|
||||
if (projectIter != m_projectButtons.end())
|
||||
{
|
||||
if (project.m_buildFailed)
|
||||
{
|
||||
projectIter.value()->ShowBuildFailed(true, project.m_logUrl);
|
||||
}
|
||||
else
|
||||
{
|
||||
projectIter.value()->SetProjectBuildButtonAction();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_background.load(":/Backgrounds/DefaultBackground.jpg");
|
||||
|
||||
// Make sure to update builder with latest Project Button
|
||||
if (m_currentBuilder)
|
||||
{
|
||||
ProjectButton* projectButtonPtr = nullptr;
|
||||
|
||||
m_projectsContent = CreateProjectsContent(m_currentBuilder->GetProjectInfo().m_path, &projectButtonPtr);
|
||||
m_currentBuilder->SetProjectButton(projectButtonPtr);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_projectsContent = CreateProjectsContent();
|
||||
}
|
||||
|
||||
m_stack->addWidget(m_projectsContent);
|
||||
m_stack->setCurrentWidget(m_projectsContent);
|
||||
}
|
||||
|
||||
@@ -466,7 +508,7 @@ namespace O3DE::ProjectManager
|
||||
if (m_buildQueue.empty() && !m_currentBuilder)
|
||||
{
|
||||
StartProjectBuild(projectInfo);
|
||||
// Projects Content is already reset in fuction
|
||||
// Projects Content is already reset in function
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -491,6 +533,7 @@ namespace O3DE::ProjectManager
|
||||
}
|
||||
else
|
||||
{
|
||||
m_background.load(":/Backgrounds/DefaultBackground.jpg");
|
||||
ResetProjectsContent();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ QT_FORWARD_DECLARE_CLASS(QPaintEvent)
|
||||
QT_FORWARD_DECLARE_CLASS(QFrame)
|
||||
QT_FORWARD_DECLARE_CLASS(QStackedWidget)
|
||||
QT_FORWARD_DECLARE_CLASS(QLayout)
|
||||
QT_FORWARD_DECLARE_CLASS(FlowLayout)
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
@@ -59,8 +60,8 @@ namespace O3DE::ProjectManager
|
||||
|
||||
private:
|
||||
QFrame* CreateFirstTimeContent();
|
||||
QFrame* CreateProjectsContent(QString buildProjectPath = "", ProjectButton** projectButton = nullptr);
|
||||
ProjectButton* CreateProjectButton(ProjectInfo& project, QLayout* flowLayout, bool processing = false);
|
||||
QFrame* CreateProjectsContent();
|
||||
ProjectButton* CreateProjectButton(const ProjectInfo& project);
|
||||
void ResetProjectsContent();
|
||||
bool ShouldDisplayFirstTimeContent();
|
||||
bool RemoveInvalidProjects();
|
||||
@@ -75,7 +76,9 @@ namespace O3DE::ProjectManager
|
||||
QPixmap m_background;
|
||||
QFrame* m_firstTimeContent = nullptr;
|
||||
QFrame* m_projectsContent = nullptr;
|
||||
FlowLayout* m_projectsFlowLayout = nullptr;
|
||||
QStackedWidget* m_stack = nullptr;
|
||||
QHash<QString, ProjectButton*> m_projectButtons;
|
||||
QList<ProjectInfo> m_requiresBuild;
|
||||
QQueue<ProjectInfo> m_buildQueue;
|
||||
ProjectBuilderController* m_currentBuilder = nullptr;
|
||||
|
||||
@@ -30,6 +30,7 @@ namespace O3DE::ProjectManager
|
||||
|
||||
// add a tab widget at the bottom of the stack
|
||||
m_tabWidget = new QTabWidget();
|
||||
m_tabWidget->tabBar()->setFocusPolicy(Qt::TabFocus);
|
||||
m_screenStack->addWidget(m_tabWidget);
|
||||
connect(m_tabWidget, &QTabWidget::currentChanged, this, &ScreensCtrl::TabChanged);
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ namespace O3DE::ProjectManager
|
||||
QTabWidget* tabWidget = new QTabWidget();
|
||||
tabWidget->setObjectName("projectSettingsTab");
|
||||
tabWidget->tabBar()->setObjectName("projectSettingsTabBar");
|
||||
tabWidget->tabBar()->setFocusPolicy(Qt::TabFocus);
|
||||
tabWidget->addTab(m_updateSettingsScreen, tr("General"));
|
||||
|
||||
QPushButton* gemsButton = new QPushButton(tr("Configure Gems"), this);
|
||||
|
||||
@@ -24,7 +24,12 @@ namespace AZ
|
||||
: public AZ::EBusTraits
|
||||
{
|
||||
public:
|
||||
virtual void ReportJobDependencies(JobDependencyList& jobDependencyList, const char* platformIdentifier) = 0;
|
||||
//! Builders can implement this function to add job dependencies on other assets that may be used in the scene file conversion process.
|
||||
virtual void ReportJobDependencies(JobDependencyList& jobDependencyList, const char* platformIdentifier) { AZ_UNUSED(jobDependencyList); AZ_UNUSED(platformIdentifier); }
|
||||
|
||||
//! Builders can implement this function to append to the job analysis fingerprint. This can be used to trigger rebuilds when global configuration changes.
|
||||
//! See also AssetBuilderDesc::m_analysisFingerprint.
|
||||
virtual void AddFingerprintInfo(AZStd::set<AZStd::string>& fingerprintInfo) { AZ_UNUSED(fingerprintInfo); }
|
||||
};
|
||||
using SceneBuilderDependencyBus = EBus<SceneBuilderDependencyRequests>;
|
||||
} // namespace SceneAPI
|
||||
|
||||
@@ -62,9 +62,9 @@ namespace AZ
|
||||
void BuildDrawPacketList(size_t modelLodIndex);
|
||||
void SetRayTracingData();
|
||||
void SetSortKey(RHI::DrawItemSortKey sortKey);
|
||||
RHI::DrawItemSortKey GetSortKey();
|
||||
void SetLodOverride(RPI::Cullable::LodOverride lodOverride);
|
||||
RPI::Cullable::LodOverride GetLodOverride();
|
||||
RHI::DrawItemSortKey GetSortKey() const;
|
||||
void SetMeshLodConfiguration(RPI::Cullable::LodConfiguration meshLodConfig);
|
||||
RPI::Cullable::LodConfiguration GetMeshLodConfiguration() const;
|
||||
void UpdateDrawPackets(bool forceUpdate = false);
|
||||
void BuildCullable();
|
||||
void UpdateCullBounds(const TransformServiceFeatureProcessor* transformService);
|
||||
@@ -153,10 +153,10 @@ namespace AZ
|
||||
AZ::Aabb GetLocalAabb(const MeshHandle& meshHandle) const override;
|
||||
|
||||
void SetSortKey(const MeshHandle& meshHandle, RHI::DrawItemSortKey sortKey) override;
|
||||
RHI::DrawItemSortKey GetSortKey(const MeshHandle& meshHandle) override;
|
||||
RHI::DrawItemSortKey GetSortKey(const MeshHandle& meshHandle) const override;
|
||||
|
||||
void SetLodOverride(const MeshHandle& meshHandle, RPI::Cullable::LodOverride lodOverride) override;
|
||||
RPI::Cullable::LodOverride GetLodOverride(const MeshHandle& meshHandle) override;
|
||||
void SetMeshLodConfiguration(const MeshHandle& meshHandle, const RPI::Cullable::LodConfiguration& meshLodConfig) override;
|
||||
RPI::Cullable::LodConfiguration GetMeshLodConfiguration(const MeshHandle& meshHandle) const override;
|
||||
|
||||
void SetExcludeFromReflectionCubeMaps(const MeshHandle& meshHandle, bool excludeFromReflectionCubeMaps) override;
|
||||
void SetRayTracingEnabled(const MeshHandle& meshHandle, bool rayTracingEnabled) override;
|
||||
|
||||
@@ -93,11 +93,11 @@ namespace AZ
|
||||
//! Sets the sort key for a given mesh handle.
|
||||
virtual void SetSortKey(const MeshHandle& meshHandle, RHI::DrawItemSortKey sortKey) = 0;
|
||||
//! Gets the sort key for a given mesh handle.
|
||||
virtual RHI::DrawItemSortKey GetSortKey(const MeshHandle& meshHandle) = 0;
|
||||
//! Sets an LOD override for a given mesh handle. This LOD will always be rendered instead being automatically determined.
|
||||
virtual void SetLodOverride(const MeshHandle& meshHandle, RPI::Cullable::LodOverride lodOverride) = 0;
|
||||
//! Gets the LOD override for a given mesh handle.
|
||||
virtual RPI::Cullable::LodOverride GetLodOverride(const MeshHandle& meshHandle) = 0;
|
||||
virtual RHI::DrawItemSortKey GetSortKey(const MeshHandle& meshHandle) const = 0;
|
||||
//! Sets LOD mesh configurations to be used in the Mesh Feature Processor
|
||||
virtual void SetMeshLodConfiguration(const MeshHandle& meshHandle, const RPI::Cullable::LodConfiguration& meshLodConfig) = 0;
|
||||
//! Gets the LOD mesh configurations being used in the Mesh Feature Processor
|
||||
virtual RPI::Cullable::LodConfiguration GetMeshLodConfiguration(const MeshHandle& meshHandle) const = 0;
|
||||
//! Sets the option to exclude this mesh from baked reflection probe cubemaps
|
||||
virtual void SetExcludeFromReflectionCubeMaps(const MeshHandle& meshHandle, bool excludeFromReflectionCubeMaps) = 0;
|
||||
//! Sets the option to exclude this mesh from raytracing
|
||||
|
||||
@@ -32,9 +32,9 @@ namespace UnitTest
|
||||
MOCK_METHOD2(SetLocalAabb, void(const MeshHandle&, const AZ::Aabb&));
|
||||
MOCK_CONST_METHOD1(GetLocalAabb, AZ::Aabb(const MeshHandle&));
|
||||
MOCK_METHOD2(SetSortKey, void (const MeshHandle&, AZ::RHI::DrawItemSortKey));
|
||||
MOCK_METHOD1(GetSortKey, AZ::RHI::DrawItemSortKey(const MeshHandle&));
|
||||
MOCK_METHOD2(SetLodOverride, void(const MeshHandle&, AZ::RPI::Cullable::LodOverride));
|
||||
MOCK_METHOD1(GetLodOverride, AZ::RPI::Cullable::LodOverride(const MeshHandle&));
|
||||
MOCK_CONST_METHOD1(GetSortKey, AZ::RHI::DrawItemSortKey(const MeshHandle&));
|
||||
MOCK_METHOD2(SetMeshLodConfiguration, void(const MeshHandle&, const AZ::RPI::Cullable::LodConfiguration&));
|
||||
MOCK_CONST_METHOD1(GetMeshLodConfiguration, AZ::RPI::Cullable::LodConfiguration(const MeshHandle&));
|
||||
MOCK_METHOD2(AcquireMesh, MeshHandle (const AZ::Render::MeshHandleDescriptor&, const AZ::Render::MaterialAssignmentMap&));
|
||||
MOCK_METHOD2(AcquireMesh, MeshHandle (const AZ::Render::MeshHandleDescriptor&, const AZ::Data::Instance<AZ::RPI::Material>&));
|
||||
MOCK_METHOD2(SetRayTracingEnabled, void (const MeshHandle&, bool));
|
||||
|
||||
@@ -67,8 +67,8 @@ namespace AZ
|
||||
|
||||
ImGuiPass::ImGuiPass(const RPI::PassDescriptor& descriptor)
|
||||
: Base(descriptor)
|
||||
, AzFramework::InputChannelEventListener(AzFramework::InputChannelEventListener::GetPriorityUI())
|
||||
, AzFramework::InputTextEventListener(AzFramework::InputTextEventListener::GetPriorityUI())
|
||||
, AzFramework::InputChannelEventListener(AzFramework::InputChannelEventListener::GetPriorityDebugUI() - 1) // Give ImGui manager priority over the pass
|
||||
, AzFramework::InputTextEventListener(AzFramework::InputTextEventListener::GetPriorityDebugUI() - 1) // Give ImGui manager priority over the pass
|
||||
{
|
||||
|
||||
const ImGuiPassData* imguiPassData = RPI::PassUtils::GetPassData<ImGuiPassData>(descriptor);
|
||||
@@ -157,11 +157,6 @@ namespace AZ
|
||||
return io.WantTextInput;
|
||||
}
|
||||
|
||||
AZ::s32 ImGuiPass::GetPriority() const
|
||||
{
|
||||
return AzFramework::InputChannelEventListener::GetPriorityUI();
|
||||
}
|
||||
|
||||
bool ImGuiPass::OnInputChannelEventFiltered(const AzFramework::InputChannel& inputChannel)
|
||||
{
|
||||
if (!IsEnabled() || GetRenderPipeline()->GetScene() == nullptr)
|
||||
|
||||
@@ -84,7 +84,6 @@ namespace AZ
|
||||
|
||||
// AzFramework::InputChannelEventListener overrides...
|
||||
bool OnInputChannelEventFiltered(const AzFramework::InputChannel& inputChannel) override;
|
||||
AZ::s32 GetPriority() const override;
|
||||
|
||||
protected:
|
||||
explicit ImGuiPass(const RPI::PassDescriptor& descriptor);
|
||||
|
||||
@@ -12,12 +12,26 @@
|
||||
#include <AzCore/Math/Color.h>
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/Settings/SettingsRegistry.h>
|
||||
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
|
||||
|
||||
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Render
|
||||
{
|
||||
void MaterialConverterSettings::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<MaterialConverterSettings>()
|
||||
->Version(1)
|
||||
->Field("Enable", &MaterialConverterSettings::m_enable)
|
||||
->Field("DefaultMaterial", &MaterialConverterSettings::m_defaultMaterial);
|
||||
}
|
||||
}
|
||||
|
||||
void MaterialConverterSystemComponent::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (auto* serialize = azrtti_cast<SerializeContext*>(context))
|
||||
@@ -26,10 +40,22 @@ namespace AZ
|
||||
->Version(3)
|
||||
->Attribute(Edit::Attributes::SystemComponentTags, AZStd::vector<Crc32>({ AssetBuilderSDK::ComponentTags::AssetBuilder }));
|
||||
}
|
||||
|
||||
MaterialConverterSettings::Reflect(context);
|
||||
}
|
||||
|
||||
void MaterialConverterSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services)
|
||||
{
|
||||
services.emplace_back(AZ_CRC_CE("FingerprintModification"));
|
||||
}
|
||||
|
||||
void MaterialConverterSystemComponent::Activate()
|
||||
{
|
||||
if (auto* settingsRegistry = AZ::SettingsRegistry::Get())
|
||||
{
|
||||
settingsRegistry->GetObject(m_settings, "/O3DE/SceneAPI/MaterialConverter");
|
||||
}
|
||||
|
||||
RPI::MaterialConverterBus::Handler::BusConnect();
|
||||
}
|
||||
|
||||
@@ -37,11 +63,21 @@ namespace AZ
|
||||
{
|
||||
RPI::MaterialConverterBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
bool MaterialConverterSystemComponent::IsEnabled() const
|
||||
{
|
||||
return m_settings.m_enable;
|
||||
}
|
||||
|
||||
bool MaterialConverterSystemComponent::ConvertMaterial(
|
||||
const AZ::SceneAPI::DataTypes::IMaterialData& materialData, RPI::MaterialSourceData& sourceData)
|
||||
{
|
||||
using namespace AZ::RPI;
|
||||
|
||||
if (!m_settings.m_enable)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// The source data for generating material asset
|
||||
sourceData.m_materialType = GetMaterialTypePath();
|
||||
@@ -140,9 +176,20 @@ namespace AZ
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* MaterialConverterSystemComponent::GetMaterialTypePath() const
|
||||
AZStd::string MaterialConverterSystemComponent::GetMaterialTypePath() const
|
||||
{
|
||||
return "Materials/Types/StandardPBR.materialtype";
|
||||
}
|
||||
|
||||
AZStd::string MaterialConverterSystemComponent::GetDefaultMaterialPath() const
|
||||
{
|
||||
if (m_settings.m_defaultMaterial.empty())
|
||||
{
|
||||
AZ_Error("MaterialConverterSystemComponent", m_settings.m_enable,
|
||||
"Material conversion is disabled but a default material not specified in registry /O3DE/SceneAPI/MaterialConverter/DefaultMaterial");
|
||||
}
|
||||
|
||||
return m_settings.m_defaultMaterial;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,16 @@ namespace AZ
|
||||
{
|
||||
namespace Render
|
||||
{
|
||||
struct MaterialConverterSettings
|
||||
{
|
||||
AZ_TYPE_INFO(MaterialConverterSettings, "{8D91601D-570A-4557-99C8-631DB4928040}");
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
bool m_enable = true;
|
||||
AZStd::string m_defaultMaterial;
|
||||
};
|
||||
|
||||
//! Atom's implementation of converting SceneAPI data into Atom's default material: StandardPBR
|
||||
class MaterialConverterSystemComponent final
|
||||
: public AZ::Component
|
||||
@@ -27,13 +37,20 @@ namespace AZ
|
||||
AZ_COMPONENT(MaterialConverterSystemComponent, "{C2338D45-6456-4521-B469-B000A13F2493}");
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services);
|
||||
|
||||
void Activate() override;
|
||||
void Deactivate() override;
|
||||
|
||||
// MaterialConverterBus overrides ...
|
||||
bool IsEnabled() const override;
|
||||
bool ConvertMaterial(const AZ::SceneAPI::DataTypes::IMaterialData& materialData, RPI::MaterialSourceData& out) override;
|
||||
const char* GetMaterialTypePath() const override;
|
||||
AZStd::string GetMaterialTypePath() const override;
|
||||
AZStd::string GetDefaultMaterialPath() const override;
|
||||
|
||||
private:
|
||||
MaterialConverterSettings m_settings;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -346,7 +346,7 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
RHI::DrawItemSortKey MeshFeatureProcessor::GetSortKey(const MeshHandle& meshHandle)
|
||||
RHI::DrawItemSortKey MeshFeatureProcessor::GetSortKey(const MeshHandle& meshHandle) const
|
||||
{
|
||||
if (meshHandle.IsValid())
|
||||
{
|
||||
@@ -359,24 +359,24 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
void MeshFeatureProcessor::SetLodOverride(const MeshHandle& meshHandle, RPI::Cullable::LodOverride lodOverride)
|
||||
void MeshFeatureProcessor::SetMeshLodConfiguration(const MeshHandle& meshHandle, const RPI::Cullable::LodConfiguration& meshLodConfig)
|
||||
{
|
||||
if (meshHandle.IsValid())
|
||||
{
|
||||
meshHandle->SetLodOverride(lodOverride);
|
||||
meshHandle->SetMeshLodConfiguration(meshLodConfig);
|
||||
}
|
||||
}
|
||||
|
||||
RPI::Cullable::LodOverride MeshFeatureProcessor::GetLodOverride(const MeshHandle& meshHandle)
|
||||
RPI::Cullable::LodConfiguration MeshFeatureProcessor::GetMeshLodConfiguration(const MeshHandle& meshHandle) const
|
||||
{
|
||||
if (meshHandle.IsValid())
|
||||
{
|
||||
return meshHandle->GetLodOverride();
|
||||
return meshHandle->GetMeshLodConfiguration();
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Assert(false, "Invalid mesh handle");
|
||||
return 0;
|
||||
return {RPI::Cullable::LodType::Default, 0, 0.0f, 0.0f };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -968,19 +968,19 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
RHI::DrawItemSortKey MeshDataInstance::GetSortKey()
|
||||
RHI::DrawItemSortKey MeshDataInstance::GetSortKey() const
|
||||
{
|
||||
return m_sortKey;
|
||||
}
|
||||
|
||||
void MeshDataInstance::SetLodOverride(RPI::Cullable::LodOverride lodOverride)
|
||||
void MeshDataInstance::SetMeshLodConfiguration(RPI::Cullable::LodConfiguration meshLodConfig)
|
||||
{
|
||||
m_cullable.m_lodData.m_lodOverride = lodOverride;
|
||||
m_cullable.m_lodData.m_lodConfiguration = meshLodConfig;
|
||||
}
|
||||
|
||||
RPI::Cullable::LodOverride MeshDataInstance::GetLodOverride()
|
||||
RPI::Cullable::LodConfiguration MeshDataInstance::GetMeshLodConfiguration() const
|
||||
{
|
||||
return m_cullable.m_lodData.m_lodOverride;
|
||||
return m_cullable.m_lodData.m_lodConfiguration;
|
||||
}
|
||||
|
||||
void MeshDataInstance::UpdateDrawPackets(bool forceUpdate /*= false*/)
|
||||
@@ -1022,9 +1022,6 @@ namespace AZ
|
||||
{
|
||||
//initialize the lod
|
||||
RPI::Cullable::LodData::Lod& lod = lodData.m_lods[lodIndex];
|
||||
//[GFX TODO][ATOM-5562] - Level of detail: override lod distances and add global lod multiplier(s)
|
||||
static const float MinimumScreenCoverage = 1.0f/1080.0f; //mesh should cover at least a screen pixel at 1080p to be drawn
|
||||
static const float ReductionFactor = 0.5f;
|
||||
if (lodIndex == 0)
|
||||
{
|
||||
//first lod
|
||||
@@ -1033,17 +1030,18 @@ namespace AZ
|
||||
else
|
||||
{
|
||||
//every other lod: use the previous lod's min
|
||||
lod.m_screenCoverageMax = AZStd::GetMax(lodData.m_lods[lodIndex-1].m_screenCoverageMin, MinimumScreenCoverage);
|
||||
lod.m_screenCoverageMax = AZStd::GetMax(lodData.m_lods[lodIndex - 1].m_screenCoverageMin, lodData.m_lodConfiguration.m_minimumScreenCoverage);
|
||||
}
|
||||
|
||||
if (lodIndex < lodAssets.size() - 1)
|
||||
{
|
||||
//first and middle lods: compute a stepdown value for the min
|
||||
lod.m_screenCoverageMin = AZStd::GetMax(ReductionFactor * lod.m_screenCoverageMax, MinimumScreenCoverage);
|
||||
lod.m_screenCoverageMin = AZStd::GetMax(lodData.m_lodConfiguration.m_qualityDecayRate * lod.m_screenCoverageMax, lodData.m_lodConfiguration.m_minimumScreenCoverage);
|
||||
}
|
||||
else
|
||||
{
|
||||
//last lod: use MinimumScreenCoverage for the min
|
||||
lod.m_screenCoverageMin = MinimumScreenCoverage;
|
||||
lod.m_screenCoverageMin = lodData.m_lodConfiguration.m_minimumScreenCoverage;
|
||||
}
|
||||
|
||||
lod.m_drawPackets.clear();
|
||||
|
||||
@@ -173,13 +173,14 @@ namespace AZ
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
CpuProfilingStatisticsSerializerEntry() = default;
|
||||
CpuProfilingStatisticsSerializerEntry(const RHI::CachedTimeRegion& cachedTimeRegion);
|
||||
CpuProfilingStatisticsSerializerEntry(const RHI::CachedTimeRegion& cachedTimeRegion, AZStd::thread_id threadId);
|
||||
|
||||
Name m_groupName;
|
||||
Name m_regionName;
|
||||
uint16_t m_stackDepth;
|
||||
AZStd::sys_time_t m_startTick;
|
||||
AZStd::sys_time_t m_endTick;
|
||||
size_t m_threadId;
|
||||
};
|
||||
|
||||
AZ_TYPE_INFO(CpuProfilingStatisticsSerializer, "{D5B02946-0D27-474F-9A44-364C2706DD41}");
|
||||
|
||||
@@ -417,14 +417,14 @@ namespace AZ
|
||||
// Create serializable entries
|
||||
for (const auto& timeRegionMap : continuousData)
|
||||
{
|
||||
for (const auto& threadEntry : timeRegionMap)
|
||||
for (const auto& [threadId, regionMap] : timeRegionMap)
|
||||
{
|
||||
for (const auto& cachedRegionEntry : threadEntry.second)
|
||||
for (const auto& [regionName, regionVec] : regionMap)
|
||||
{
|
||||
m_cpuProfilingStatisticsSerializerEntries.insert(
|
||||
m_cpuProfilingStatisticsSerializerEntries.end(),
|
||||
cachedRegionEntry.second.begin(),
|
||||
cachedRegionEntry.second.end());
|
||||
for (const auto& region : regionVec)
|
||||
{
|
||||
m_cpuProfilingStatisticsSerializerEntries.emplace_back(region, threadId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -445,13 +445,15 @@ namespace AZ
|
||||
|
||||
// --- CpuProfilingStatisticsSerializerEntry ---
|
||||
|
||||
CpuProfilingStatisticsSerializer::CpuProfilingStatisticsSerializerEntry::CpuProfilingStatisticsSerializerEntry(const RHI::CachedTimeRegion& cachedTimeRegion)
|
||||
CpuProfilingStatisticsSerializer::CpuProfilingStatisticsSerializerEntry::CpuProfilingStatisticsSerializerEntry(
|
||||
const RHI::CachedTimeRegion& cachedTimeRegion, AZStd::thread_id threadId)
|
||||
{
|
||||
m_groupName = cachedTimeRegion.m_groupRegionName->m_groupName;
|
||||
m_regionName = cachedTimeRegion.m_groupRegionName->m_regionName;
|
||||
m_stackDepth = cachedTimeRegion.m_stackDepth;
|
||||
m_startTick = cachedTimeRegion.m_startTick;
|
||||
m_endTick = cachedTimeRegion.m_endTick;
|
||||
m_threadId = AZStd::hash<AZStd::thread_id>{}(threadId);
|
||||
}
|
||||
|
||||
void CpuProfilingStatisticsSerializer::CpuProfilingStatisticsSerializerEntry::Reflect(AZ::ReflectContext* context)
|
||||
@@ -465,6 +467,7 @@ namespace AZ
|
||||
->Field("stackDepth", &CpuProfilingStatisticsSerializerEntry::m_stackDepth)
|
||||
->Field("startTick", &CpuProfilingStatisticsSerializerEntry::m_startTick)
|
||||
->Field("endTick", &CpuProfilingStatisticsSerializerEntry::m_endTick)
|
||||
->Field("threadId", &CpuProfilingStatisticsSerializerEntry::m_threadId)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,10 +29,19 @@ namespace AZ
|
||||
: public AZ::EBusTraits
|
||||
{
|
||||
public:
|
||||
//! Returns true if the converion was successful
|
||||
|
||||
virtual bool IsEnabled() const = 0;
|
||||
|
||||
//! Converts data from a IMaterialData object to an Atom MaterialSourceData.
|
||||
//! Only works when IsEnabled() is true.
|
||||
//! @return true if the MaterialSourceData output was populated with converted material data.
|
||||
virtual bool ConvertMaterial(const AZ::SceneAPI::DataTypes::IMaterialData& materialData, MaterialSourceData& out) = 0;
|
||||
//! Returns the path to the .materialtype file that the materials are based on, such as StandardPBR.materialtype, etc.
|
||||
virtual const char* GetMaterialTypePath() const = 0;
|
||||
|
||||
//! Returns the path to the .materialtype file that the converted materials are based on, such as StandardPBR.materialtype, etc.
|
||||
virtual AZStd::string GetMaterialTypePath() const = 0;
|
||||
|
||||
//! Returns the path to a .material file to use as the default material when conversion is disabled.
|
||||
virtual AZStd::string GetDefaultMaterialPath() const = 0;
|
||||
};
|
||||
|
||||
using MaterialConverterBus = AZ::EBus<MaterialConverterRequests>;
|
||||
|
||||
@@ -70,8 +70,23 @@ namespace AZ
|
||||
};
|
||||
CullData m_cullData;
|
||||
|
||||
enum LodType : uint8_t
|
||||
{
|
||||
Default = 0,
|
||||
ScreenCoverage,
|
||||
SpecificLod,
|
||||
};
|
||||
using LodOverride = uint8_t;
|
||||
static constexpr uint8_t NoLodOverride = AZStd::numeric_limits<LodOverride>::max();
|
||||
|
||||
struct LodConfiguration
|
||||
{
|
||||
LodType m_lodType = LodType::Default;
|
||||
LodOverride m_lodOverride = 0;
|
||||
// the minimum possibe area a sphere enclosing a mesh projected onto the screen should have before it is culled.
|
||||
float m_minimumScreenCoverage = 1.0f / 1080.0f; // For default, mesh should cover at least a screen pixel at 1080p to be drawn;
|
||||
// The screen area decay between 0 and 1, i.e. closer to 1 -> lose quality immediately, closer to 0 -> never lose quality
|
||||
float m_qualityDecayRate = 0.5f;
|
||||
};
|
||||
|
||||
struct LodData
|
||||
{
|
||||
@@ -88,7 +103,7 @@ namespace AZ
|
||||
//! Suggest setting to: 0.5f*localAabb.GetExtents().GetMaxElement()
|
||||
float m_lodSelectionRadius = 1.0f;
|
||||
|
||||
LodOverride m_lodOverride = NoLodOverride;
|
||||
LodConfiguration m_lodConfiguration;
|
||||
};
|
||||
LodData m_lodData;
|
||||
|
||||
|
||||
@@ -25,6 +25,8 @@ namespace AZ
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
// Note that StableId is uint32_t for legacy reasons: we used to use AssetId::m_subId as the material slot ID. But actually the original MaterialUid
|
||||
// is 64 bit so we might want to switch this to be uint64_t at some point.
|
||||
using StableId = uint32_t;
|
||||
static const StableId InvalidStableId;
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
|
||||
#include <Atom/RPI.Edit/Material/MaterialSourceData.h>
|
||||
#include <Atom/RPI.Edit/Material/MaterialConverterBus.h>
|
||||
#include <Atom/RPI.Edit/Common/AssetUtils.h>
|
||||
|
||||
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
|
||||
#include <AzCore/Settings/SettingsRegistry.h>
|
||||
@@ -70,28 +71,74 @@ namespace AZ
|
||||
|
||||
void MaterialAssetDependenciesComponent::ReportJobDependencies(SceneAPI::JobDependencyList& jobDependencyList, const char* platformIdentifier)
|
||||
{
|
||||
AssetBuilderSDK::SourceFileDependency materialTypeSource;
|
||||
bool conversionEnabled = false;
|
||||
RPI::MaterialConverterBus::BroadcastResult(conversionEnabled, &RPI::MaterialConverterBus::Events::IsEnabled);
|
||||
|
||||
// Right now, scene file importing only supports a single material type, once that changes, this will have to be re-designed, see ATOM-3554
|
||||
RPI::MaterialConverterBus::BroadcastResult(materialTypeSource.m_sourceFileDependencyPath, &RPI::MaterialConverterBus::Events::GetMaterialTypePath);
|
||||
AZStd::string materialTypePath;
|
||||
RPI::MaterialConverterBus::BroadcastResult(materialTypePath, &RPI::MaterialConverterBus::Events::GetMaterialTypePath);
|
||||
|
||||
AssetBuilderSDK::JobDependency jobDependency;
|
||||
jobDependency.m_jobKey = "Atom Material Builder";
|
||||
jobDependency.m_sourceFile = materialTypeSource;
|
||||
jobDependency.m_platformIdentifier = platformIdentifier;
|
||||
jobDependency.m_type = AssetBuilderSDK::JobDependencyType::Order;
|
||||
|
||||
if (!materialTypeSource.m_sourceFileDependencyPath.empty())
|
||||
if (conversionEnabled && !materialTypePath.empty())
|
||||
{
|
||||
AssetBuilderSDK::SourceFileDependency materialTypeSource;
|
||||
materialTypeSource.m_sourceFileDependencyPath = materialTypePath;
|
||||
|
||||
AssetBuilderSDK::JobDependency jobDependency;
|
||||
jobDependency.m_jobKey = "Atom Material Builder";
|
||||
jobDependency.m_sourceFile = materialTypeSource;
|
||||
jobDependency.m_platformIdentifier = platformIdentifier;
|
||||
jobDependency.m_type = AssetBuilderSDK::JobDependencyType::Order;
|
||||
|
||||
jobDependencyList.push_back(jobDependency);
|
||||
}
|
||||
}
|
||||
|
||||
void MaterialAssetDependenciesComponent::AddFingerprintInfo(AZStd::set<AZStd::string>& fingerprintInfo)
|
||||
{
|
||||
// This will cause scene files to be reprocessed whenever the global MaterialConverter settings change.
|
||||
|
||||
bool conversionEnabled = false;
|
||||
RPI::MaterialConverterBus::BroadcastResult(conversionEnabled, &RPI::MaterialConverterBus::Events::IsEnabled);
|
||||
fingerprintInfo.insert(AZStd::string::format("[MaterialConverter enabled=%d]", conversionEnabled));
|
||||
|
||||
if (!conversionEnabled)
|
||||
{
|
||||
AZStd::string defaultMaterialPath;
|
||||
RPI::MaterialConverterBus::BroadcastResult(defaultMaterialPath, &RPI::MaterialConverterBus::Events::GetDefaultMaterialPath);
|
||||
fingerprintInfo.insert(AZStd::string::format("[MaterialConverter defaultMaterial=%s]", defaultMaterialPath.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
void MaterialAssetBuilderComponent::Reflect(ReflectContext* context)
|
||||
{
|
||||
if (auto* serialize = azrtti_cast<SerializeContext*>(context))
|
||||
{
|
||||
serialize->Class<MaterialAssetBuilderComponent, SceneAPI::SceneCore::ExportingComponent>()
|
||||
->Version(16); // Optional material conversion
|
||||
->Version(16); // Optional material conversion
|
||||
}
|
||||
}
|
||||
|
||||
Data::Asset<MaterialAsset> MaterialAssetBuilderComponent::GetDefaultMaterialAsset() const
|
||||
{
|
||||
AZStd::string defaultMaterialPath;
|
||||
RPI::MaterialConverterBus::BroadcastResult(defaultMaterialPath, &RPI::MaterialConverterBus::Events::GetDefaultMaterialPath);
|
||||
|
||||
if (defaultMaterialPath.empty())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
else
|
||||
{
|
||||
auto defaultMaterialAssetId = RPI::AssetUtils::MakeAssetId(defaultMaterialPath, 0);
|
||||
if (!defaultMaterialAssetId.IsSuccess())
|
||||
{
|
||||
AZ_Error("MaterialAssetBuilderComponent", false, "Could not find asset '%s'", defaultMaterialPath.c_str());
|
||||
return {};
|
||||
}
|
||||
else
|
||||
{
|
||||
return Data::AssetManager::Instance().CreateAsset<RPI::MaterialAsset>(defaultMaterialAssetId.GetValue(), Data::AssetLoadBehaviorNamespace::PreLoad);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,8 +166,8 @@ namespace AZ
|
||||
|
||||
BindToCall(&MaterialAssetBuilderComponent::BuildMaterials);
|
||||
}
|
||||
|
||||
SceneAPI::Events::ProcessingResult MaterialAssetBuilderComponent::BuildMaterials(MaterialAssetBuilderContext& context) const
|
||||
|
||||
SceneAPI::Events::ProcessingResult MaterialAssetBuilderComponent::ConvertMaterials(MaterialAssetBuilderContext& context) const
|
||||
{
|
||||
const auto& scene = context.m_scene;
|
||||
const Uuid sourceSceneUuid = scene.GetSourceGuid();
|
||||
@@ -193,6 +240,64 @@ namespace AZ
|
||||
|
||||
return SceneAPI::Events::ProcessingResult::Success;
|
||||
}
|
||||
|
||||
SceneAPI::Events::ProcessingResult MaterialAssetBuilderComponent::AssignDefaultMaterials(MaterialAssetBuilderContext& context) const
|
||||
{
|
||||
Data::Asset<MaterialAsset> defaultMaterialAsset = GetDefaultMaterialAsset();
|
||||
|
||||
if (!defaultMaterialAsset.GetId().IsValid())
|
||||
{
|
||||
AZ_Warning("MaterialAssetBuilderComponent", false, "Material conversion is disabled but no default material was provided. The model will likely be invisible by default.");
|
||||
// Return success because it's just a warning.
|
||||
return SceneAPI::Events::ProcessingResult::Success;
|
||||
}
|
||||
|
||||
const auto& scene = context.m_scene;
|
||||
const Uuid sourceSceneUuid = scene.GetSourceGuid();
|
||||
const auto& sceneGraph = scene.GetGraph();
|
||||
|
||||
auto names = sceneGraph.GetNameStorage();
|
||||
auto content = sceneGraph.GetContentStorage();
|
||||
auto pairView = SceneAPI::Containers::Views::MakePairView(names, content);
|
||||
|
||||
auto view = SceneAPI::Containers::Views::MakeSceneGraphDownwardsView<
|
||||
SceneAPI::Containers::Views::BreadthFirst>(
|
||||
sceneGraph, sceneGraph.GetRoot(), pairView.cbegin(), true);
|
||||
|
||||
for (const auto& viewIt : view)
|
||||
{
|
||||
if (viewIt.second == nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (azrtti_istypeof<SceneAPI::DataTypes::IMaterialData>(viewIt.second.get()))
|
||||
{
|
||||
auto materialData = AZStd::static_pointer_cast<const SceneAPI::DataTypes::IMaterialData>(viewIt.second);
|
||||
uint64_t materialUid = materialData->GetUniqueId();
|
||||
|
||||
context.m_outputMaterialsByUid[materialUid] = { defaultMaterialAsset, materialData->GetMaterialName() };
|
||||
}
|
||||
}
|
||||
|
||||
return SceneAPI::Events::ProcessingResult::Success;
|
||||
}
|
||||
|
||||
SceneAPI::Events::ProcessingResult MaterialAssetBuilderComponent::BuildMaterials(MaterialAssetBuilderContext& context) const
|
||||
{
|
||||
bool conversionEnabled = false;
|
||||
RPI::MaterialConverterBus::BroadcastResult(conversionEnabled, &RPI::MaterialConverterBus::Events::IsEnabled);
|
||||
|
||||
if (conversionEnabled)
|
||||
{
|
||||
return ConvertMaterials(context);
|
||||
}
|
||||
else
|
||||
{
|
||||
return AssignDefaultMaterials(context);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace RPI
|
||||
} // namespace AZ
|
||||
|
||||
|
||||
@@ -39,6 +39,13 @@ namespace AZ
|
||||
|
||||
// Required for ExportingComponent
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
private:
|
||||
|
||||
SceneAPI::Events::ProcessingResult ConvertMaterials(MaterialAssetBuilderContext& context) const;
|
||||
SceneAPI::Events::ProcessingResult AssignDefaultMaterials(MaterialAssetBuilderContext& context) const;
|
||||
|
||||
Data::Asset<MaterialAsset> GetDefaultMaterialAsset() const;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -65,6 +72,7 @@ namespace AZ
|
||||
|
||||
// SceneAPI::SceneBuilderDependencyBus::Handler overrides...
|
||||
void ReportJobDependencies(SceneAPI::JobDependencyList& jobDependencyList, const char* platformIdentifier) override;
|
||||
void AddFingerprintInfo(AZStd::set<AZStd::string>& fingerprintInfo) override;
|
||||
};
|
||||
} // namespace RPI
|
||||
} // namespace AZ
|
||||
|
||||
@@ -78,9 +78,17 @@ namespace AZ
|
||||
//Export MaterialAssets
|
||||
for (auto& materialPair : materialsByUid)
|
||||
{
|
||||
const Data::Asset<MaterialAsset>& asset = materialPair.second.m_asset;
|
||||
|
||||
// MaterialAssetBuilderContext could attach an independent material asset rather than
|
||||
// generate one using the scene data, so we must skip the export step in that case.
|
||||
if (asset.GetId().m_guid != exportEventContext.GetScene().GetSourceGuid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
uint64_t materialUid = materialPair.first;
|
||||
const AZStd::string& sceneName = exportEventContext.GetScene().GetName();
|
||||
const Data::Asset<MaterialAsset>& asset = materialPair.second.m_asset;
|
||||
|
||||
// escape the material name acceptable for a filename
|
||||
AZStd::string materialName = materialPair.second.m_name;
|
||||
|
||||
@@ -672,20 +672,25 @@ namespace AZ
|
||||
}
|
||||
};
|
||||
|
||||
if (lodData.m_lodOverride == Cullable::NoLodOverride)
|
||||
switch (lodData.m_lodConfiguration.m_lodType)
|
||||
{
|
||||
for (const Cullable::LodData::Lod& lod : lodData.m_lods)
|
||||
{
|
||||
//Note that this supports overlapping lod ranges (to suport cross-fading lods, for example)
|
||||
if (approxScreenPercentage >= lod.m_screenCoverageMin && approxScreenPercentage <= lod.m_screenCoverageMax)
|
||||
case Cullable::LodType::SpecificLod:
|
||||
if (lodData.m_lodConfiguration.m_lodOverride < lodData.m_lods.size())
|
||||
{
|
||||
addLodToDrawPacket(lod);
|
||||
addLodToDrawPacket(lodData.m_lods.at(lodData.m_lodConfiguration.m_lodOverride));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(lodData.m_lodOverride < lodData.m_lods.size())
|
||||
{
|
||||
addLodToDrawPacket(lodData.m_lods.at(lodData.m_lodOverride));
|
||||
break;
|
||||
case Cullable::LodType::ScreenCoverage:
|
||||
default:
|
||||
for (const Cullable::LodData::Lod& lod : lodData.m_lods)
|
||||
{
|
||||
// Note that this supports overlapping lod ranges (to suport cross-fading lods, for example)
|
||||
if (approxScreenPercentage >= lod.m_screenCoverageMin && approxScreenPercentage <= lod.m_screenCoverageMax)
|
||||
{
|
||||
addLodToDrawPacket(lod);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return numVisibleDrawPackets;
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <AtomToolsFramework/Document/AtomToolsDocumentNotificationBus.h>
|
||||
#include <AtomToolsFramework/Window/AtomToolsMainWindow.h>
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
#include <AzQtComponents/Components/Widgets/TabWidget.h>
|
||||
#endif
|
||||
|
||||
namespace AtomToolsFramework
|
||||
{
|
||||
//! AtomToolsDocumentMainWindow
|
||||
class AtomToolsDocumentMainWindow
|
||||
: public AtomToolsMainWindow
|
||||
, private AtomToolsDocumentNotificationBus::Handler
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(AtomToolsDocumentMainWindow, AZ::SystemAllocator, 0);
|
||||
|
||||
using Base = AtomToolsMainWindow;
|
||||
|
||||
AtomToolsDocumentMainWindow(QWidget* parent = 0);
|
||||
~AtomToolsDocumentMainWindow();
|
||||
|
||||
protected:
|
||||
void AddDocumentMenus();
|
||||
void AddDocumentTabBar();
|
||||
|
||||
QString GetDocumentPath(const AZ::Uuid& documentId) const;
|
||||
|
||||
AZ::Uuid GetDocumentTabId(const int tabIndex) const;
|
||||
|
||||
void AddDocumentTab(
|
||||
const AZ::Uuid& documentId,
|
||||
const AZStd::string& label,
|
||||
const AZStd::string& toolTip);
|
||||
|
||||
void RemoveDocumentTab(const AZ::Uuid& documentId);
|
||||
|
||||
void UpdateDocumentTab(
|
||||
const AZ::Uuid& documentId, const AZStd::string& label, const AZStd::string& toolTip, bool isModified);
|
||||
|
||||
void SelectPrevDocumentTab();
|
||||
void SelectNextDocumentTab();
|
||||
|
||||
virtual QWidget* CreateDocumentTabView(const AZ::Uuid& documentId);
|
||||
virtual void OpenDocumentTabContextMenu();
|
||||
virtual bool GetCreateDocumentParams(AZStd::string& openPath, AZStd::string& savePath);
|
||||
virtual bool GetOpenDocumentParams(AZStd::string& openPath);
|
||||
|
||||
// AtomToolsDocumentNotificationBus::Handler overrides...
|
||||
void OnDocumentOpened(const AZ::Uuid& documentId) override;
|
||||
void OnDocumentClosed(const AZ::Uuid& documentId) override;
|
||||
void OnDocumentModified(const AZ::Uuid& documentId) override;
|
||||
void OnDocumentUndoStateChanged(const AZ::Uuid& documentId) override;
|
||||
void OnDocumentSaved(const AZ::Uuid& documentId) override;
|
||||
|
||||
void closeEvent(QCloseEvent* closeEvent) override;
|
||||
|
||||
template<typename Functor>
|
||||
QAction* CreateAction(const QString& text, Functor functor, const QKeySequence& shortcut = 0);
|
||||
|
||||
QAction* m_actionNew = {};
|
||||
QAction* m_actionOpen = {};
|
||||
QAction* m_actionClose = {};
|
||||
QAction* m_actionCloseAll = {};
|
||||
QAction* m_actionCloseOthers = {};
|
||||
QAction* m_actionSave = {};
|
||||
QAction* m_actionSaveAsCopy = {};
|
||||
QAction* m_actionSaveAsChild = {};
|
||||
QAction* m_actionSaveAll = {};
|
||||
|
||||
QAction* m_actionUndo = {};
|
||||
QAction* m_actionRedo = {};
|
||||
|
||||
QAction* m_actionNextTab = {};
|
||||
QAction* m_actionPreviousTab = {};
|
||||
|
||||
AzQtComponents::TabWidget* m_tabWidget = {};
|
||||
};
|
||||
} // namespace AtomToolsFramework
|
||||
@@ -109,7 +109,6 @@ namespace AtomToolsFramework
|
||||
// AzToolsFramework::ViewportInteraction::ViewportMouseCursorRequestBus::Handler ...
|
||||
void BeginCursorCapture() override;
|
||||
void EndCursorCapture() override;
|
||||
AzFramework::ScreenPoint ViewportCursorScreenPosition() override;
|
||||
bool IsMouseOver() const override;
|
||||
|
||||
// AzFramework::WindowRequestBus::Handler ...
|
||||
|
||||
@@ -7,18 +7,14 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AtomToolsFramework/Window/AtomToolsMainWindowRequestBus.h>
|
||||
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
|
||||
#include <AzQtComponents/Components/DockMainWindow.h>
|
||||
#include <AzQtComponents/Components/FancyDocking.h>
|
||||
#include <AzQtComponents/Components/StyledDockWidget.h>
|
||||
#include <AzQtComponents/Components/Widgets/TabWidget.h>
|
||||
|
||||
#include <QLabel>
|
||||
#include <QMenuBar>
|
||||
#include <QToolBar>
|
||||
|
||||
namespace AtomToolsFramework
|
||||
{
|
||||
@@ -38,28 +34,26 @@ namespace AtomToolsFramework
|
||||
bool IsDockWidgetVisible(const AZStd::string& name) const override;
|
||||
AZStd::vector<AZStd::string> GetDockWidgetNames() const override;
|
||||
|
||||
virtual void CreateMenu();
|
||||
virtual void CreateTabBar();
|
||||
|
||||
virtual void AddTabForDocumentId(
|
||||
const AZ::Uuid& documentId, const AZStd::string& label, const AZStd::string& toolTip, AZStd::function<QWidget*()> widgetCreator);
|
||||
virtual void RemoveTabForDocumentId(const AZ::Uuid& documentId);
|
||||
virtual void UpdateTabForDocumentId(
|
||||
const AZ::Uuid& documentId, const AZStd::string& label, const AZStd::string& toolTip, bool isModified);
|
||||
virtual AZ::Uuid GetDocumentIdFromTab(const int tabIndex) const;
|
||||
|
||||
virtual void OpenTabContextMenu();
|
||||
virtual void SelectPreviousTab();
|
||||
virtual void SelectNextTab();
|
||||
|
||||
void SetStatusMessage(const QString& message);
|
||||
void SetStatusWarning(const QString& message);
|
||||
void SetStatusError(const QString& message);
|
||||
|
||||
AzQtComponents::FancyDocking* m_advancedDockManager = nullptr;
|
||||
AzQtComponents::TabWidget* m_tabWidget = nullptr;
|
||||
QLabel* m_statusMessage = nullptr;
|
||||
void AddCommonMenus();
|
||||
|
||||
virtual void OpenSettings();
|
||||
virtual void OpenHelp();
|
||||
virtual void OpenAbout();
|
||||
|
||||
AzQtComponents::FancyDocking* m_advancedDockManager = {};
|
||||
|
||||
QLabel* m_statusMessage = {};
|
||||
|
||||
QMenu* m_menuFile = {};
|
||||
QMenu* m_menuEdit = {};
|
||||
QMenu* m_menuView = {};
|
||||
QMenu* m_menuHelp = {};
|
||||
|
||||
AZStd::unordered_map<AZStd::string, AzQtComponents::StyledDockWidget*> m_dockWidgets;
|
||||
AZStd::unordered_map<AZStd::string, QAction*> m_dockActions;
|
||||
};
|
||||
} // namespace AtomToolsFramework
|
||||
|
||||
@@ -0,0 +1,493 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AtomToolsFramework/Document/AtomToolsDocumentMainWindow.h>
|
||||
#include <AtomToolsFramework/Document/AtomToolsDocumentRequestBus.h>
|
||||
#include <AtomToolsFramework/Document/AtomToolsDocumentSystemRequestBus.h>
|
||||
#include <AtomToolsFramework/Util/Util.h>
|
||||
#include <AtomToolsFramework/Window/AtomToolsMainWindowNotificationBus.h>
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
|
||||
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
|
||||
#include <QApplication>
|
||||
#include <QByteArray>
|
||||
#include <QCloseEvent>
|
||||
#include <QLayout>
|
||||
#include <QMenu>
|
||||
#include <QMenuBar>
|
||||
#include <QWindow>
|
||||
AZ_POP_DISABLE_WARNING
|
||||
|
||||
namespace AtomToolsFramework
|
||||
{
|
||||
AtomToolsDocumentMainWindow::AtomToolsDocumentMainWindow(QWidget* parent /* = 0 */)
|
||||
: AtomToolsMainWindow(parent)
|
||||
{
|
||||
setObjectName("AtomToolsDocumentMainWindow");
|
||||
AddDocumentMenus();
|
||||
AddDocumentTabBar();
|
||||
AtomToolsDocumentNotificationBus::Handler::BusConnect();
|
||||
}
|
||||
|
||||
AtomToolsDocumentMainWindow::~AtomToolsDocumentMainWindow()
|
||||
{
|
||||
AtomToolsDocumentNotificationBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
void AtomToolsDocumentMainWindow::AddDocumentMenus()
|
||||
{
|
||||
QAction* insertPostion = !m_menuFile->actions().empty() ? m_menuFile->actions().front() : nullptr;
|
||||
|
||||
// Generating the main menu manually because it's easier and we will have some dynamic or data driven entries
|
||||
m_actionNew = CreateAction("&New...", [this]() {
|
||||
AZStd::string openPath;
|
||||
AZStd::string savePath;
|
||||
if (GetCreateDocumentParams(openPath, savePath))
|
||||
{
|
||||
AtomToolsDocumentSystemRequestBus::Broadcast(
|
||||
&AtomToolsDocumentSystemRequestBus::Events::CreateDocumentFromFile, openPath, savePath);
|
||||
}
|
||||
}, QKeySequence::New);
|
||||
m_menuFile->insertAction(insertPostion, m_actionNew);
|
||||
|
||||
m_actionOpen = CreateAction("&Open...", [this]() {
|
||||
AZStd::string openPath;
|
||||
if (GetOpenDocumentParams(openPath))
|
||||
{
|
||||
AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsDocumentSystemRequestBus::Events::OpenDocument, openPath);
|
||||
}
|
||||
}, QKeySequence::Open);
|
||||
m_menuFile->insertAction(insertPostion, m_actionOpen);
|
||||
m_menuFile->insertSeparator(insertPostion);
|
||||
|
||||
m_actionSave = CreateAction("&Save", [this]() {
|
||||
const AZ::Uuid documentId = GetDocumentTabId(m_tabWidget->currentIndex());
|
||||
bool result = false;
|
||||
AtomToolsDocumentSystemRequestBus::BroadcastResult(result, &AtomToolsDocumentSystemRequestBus::Events::SaveDocument, documentId);
|
||||
if (!result)
|
||||
{
|
||||
SetStatusError(tr("Document save failed: %1").arg(GetDocumentPath(documentId)));
|
||||
}
|
||||
}, QKeySequence::Save);
|
||||
m_menuFile->insertAction(insertPostion, m_actionSave);
|
||||
|
||||
m_actionSaveAsCopy = CreateAction("Save &As...", [this]() {
|
||||
const AZ::Uuid documentId = GetDocumentTabId(m_tabWidget->currentIndex());
|
||||
const QString documentPath = GetDocumentPath(documentId);
|
||||
|
||||
bool result = false;
|
||||
AtomToolsDocumentSystemRequestBus::BroadcastResult(result, &AtomToolsDocumentSystemRequestBus::Events::SaveDocumentAsCopy,
|
||||
documentId, GetSaveFileInfo(documentPath).absoluteFilePath().toUtf8().constData());
|
||||
if (!result)
|
||||
{
|
||||
SetStatusError(tr("Document save failed: %1").arg(GetDocumentPath(documentId)));
|
||||
}
|
||||
}, QKeySequence::SaveAs);
|
||||
m_menuFile->insertAction(insertPostion, m_actionSaveAsCopy);
|
||||
|
||||
m_actionSaveAsChild = CreateAction("Save As &Child...", [this]() {
|
||||
const AZ::Uuid documentId = GetDocumentTabId(m_tabWidget->currentIndex());
|
||||
const QString documentPath = GetDocumentPath(documentId);
|
||||
|
||||
bool result = false;
|
||||
AtomToolsDocumentSystemRequestBus::BroadcastResult(result, &AtomToolsDocumentSystemRequestBus::Events::SaveDocumentAsChild,
|
||||
documentId, GetSaveFileInfo(documentPath).absoluteFilePath().toUtf8().constData());
|
||||
if (!result)
|
||||
{
|
||||
SetStatusError(tr("Document save failed: %1").arg(GetDocumentPath(documentId)));
|
||||
}
|
||||
});
|
||||
m_menuFile->insertAction(insertPostion, m_actionSaveAsChild);
|
||||
|
||||
m_actionSaveAll = CreateAction("Save A&ll", [this]() {
|
||||
bool result = false;
|
||||
AtomToolsDocumentSystemRequestBus::BroadcastResult(result, &AtomToolsDocumentSystemRequestBus::Events::SaveAllDocuments);
|
||||
if (!result)
|
||||
{
|
||||
SetStatusError(tr("Document save all failed"));
|
||||
}
|
||||
});
|
||||
m_menuFile->insertAction(insertPostion, m_actionSaveAll);
|
||||
m_menuFile->insertSeparator(insertPostion);
|
||||
|
||||
m_actionClose = CreateAction("&Close", [this]() {
|
||||
const AZ::Uuid documentId = GetDocumentTabId(m_tabWidget->currentIndex());
|
||||
AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsDocumentSystemRequestBus::Events::CloseDocument, documentId);
|
||||
}, QKeySequence::Close);
|
||||
m_menuFile->insertAction(insertPostion, m_actionClose);
|
||||
|
||||
m_actionCloseAll = CreateAction("Close All", [this]() {
|
||||
AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsDocumentSystemRequestBus::Events::CloseAllDocuments);
|
||||
});
|
||||
m_menuFile->insertAction(insertPostion, m_actionCloseAll);
|
||||
|
||||
m_actionCloseOthers = CreateAction("Close Others", [this]() {
|
||||
const AZ::Uuid documentId = GetDocumentTabId(m_tabWidget->currentIndex());
|
||||
AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsDocumentSystemRequestBus::Events::CloseAllDocumentsExcept, documentId);
|
||||
});
|
||||
m_menuFile->insertAction(insertPostion, m_actionCloseOthers);
|
||||
m_menuFile->insertSeparator(insertPostion);
|
||||
|
||||
insertPostion = !m_menuEdit->actions().empty() ? m_menuEdit->actions().front() : nullptr;
|
||||
|
||||
m_actionUndo = CreateAction("&Undo", [this]() {
|
||||
const AZ::Uuid documentId = GetDocumentTabId(m_tabWidget->currentIndex());
|
||||
bool result = false;
|
||||
AtomToolsDocumentRequestBus::EventResult(result, documentId, &AtomToolsDocumentRequestBus::Events::Undo);
|
||||
if (!result)
|
||||
{
|
||||
SetStatusError(tr("Document undo failed: %1").arg(GetDocumentPath(documentId)));
|
||||
}
|
||||
}, QKeySequence::Undo);
|
||||
m_menuEdit->insertAction(insertPostion, m_actionUndo);
|
||||
|
||||
m_actionRedo = CreateAction("&Redo", [this]() {
|
||||
const AZ::Uuid documentId = GetDocumentTabId(m_tabWidget->currentIndex());
|
||||
bool result = false;
|
||||
AtomToolsDocumentRequestBus::EventResult(result, documentId, &AtomToolsDocumentRequestBus::Events::Redo);
|
||||
if (!result)
|
||||
{
|
||||
SetStatusError(tr("Document redo failed: %1").arg(GetDocumentPath(documentId)));
|
||||
}
|
||||
}, QKeySequence::Redo);
|
||||
m_menuEdit->insertAction(insertPostion, m_actionRedo);
|
||||
m_menuEdit->insertSeparator(insertPostion);
|
||||
|
||||
insertPostion = !m_menuView->actions().empty() ? m_menuView->actions().front() : nullptr;
|
||||
|
||||
m_actionPreviousTab = CreateAction(
|
||||
"&Previous Tab",
|
||||
[this]()
|
||||
{
|
||||
SelectPrevDocumentTab();
|
||||
}, Qt::CTRL | Qt::SHIFT | Qt::Key_Tab); //QKeySequence::PreviousChild is mapped incorrectly in Qt
|
||||
m_menuView->insertAction(insertPostion, m_actionPreviousTab);
|
||||
|
||||
m_actionNextTab = CreateAction("&Next Tab", [this]() {
|
||||
SelectNextDocumentTab();
|
||||
}, Qt::CTRL | Qt::Key_Tab); //QKeySequence::NextChild works as expected but mirroring Previous
|
||||
m_menuView->insertAction(insertPostion, m_actionNextTab);
|
||||
m_menuView->insertSeparator(insertPostion);
|
||||
}
|
||||
|
||||
void AtomToolsDocumentMainWindow::AddDocumentTabBar()
|
||||
{
|
||||
m_tabWidget = new AzQtComponents::TabWidget(centralWidget());
|
||||
m_tabWidget->setObjectName("TabWidget");
|
||||
m_tabWidget->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred);
|
||||
m_tabWidget->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
// The tab bar should only be visible if it has active documents
|
||||
m_tabWidget->setVisible(false);
|
||||
m_tabWidget->setTabBarAutoHide(false);
|
||||
m_tabWidget->setMovable(true);
|
||||
m_tabWidget->setTabsClosable(true);
|
||||
m_tabWidget->setUsesScrollButtons(true);
|
||||
|
||||
// This signal will be triggered whenever a tab is added, removed, selected, clicked, dragged
|
||||
// When the last tab is removed tabIndex will be -1 and the document ID will be null
|
||||
// This should automatically clear the active document
|
||||
connect(m_tabWidget, &QTabWidget::currentChanged, this, [this](int tabIndex) {
|
||||
const AZ::Uuid documentId = GetDocumentTabId(tabIndex);
|
||||
AtomToolsDocumentNotificationBus::Broadcast(&AtomToolsDocumentNotificationBus::Events::OnDocumentOpened, documentId);
|
||||
});
|
||||
|
||||
connect(m_tabWidget, &QTabWidget::tabCloseRequested, this, [this](int tabIndex) {
|
||||
const AZ::Uuid documentId = GetDocumentTabId(tabIndex);
|
||||
AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsDocumentSystemRequestBus::Events::CloseDocument, documentId);
|
||||
});
|
||||
|
||||
// Add context menu for right-clicking on tabs
|
||||
m_tabWidget->setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu);
|
||||
connect(m_tabWidget, &QWidget::customContextMenuRequested, this, [this]() {
|
||||
OpenDocumentTabContextMenu();
|
||||
});
|
||||
|
||||
centralWidget()->layout()->addWidget(m_tabWidget);
|
||||
}
|
||||
|
||||
QString AtomToolsDocumentMainWindow::GetDocumentPath(const AZ::Uuid& documentId) const
|
||||
{
|
||||
AZStd::string absolutePath;
|
||||
AtomToolsDocumentRequestBus::EventResult(absolutePath, documentId, &AtomToolsDocumentRequestBus::Handler::GetAbsolutePath);
|
||||
return absolutePath.c_str();
|
||||
}
|
||||
|
||||
AZ::Uuid AtomToolsDocumentMainWindow::GetDocumentTabId(const int tabIndex) const
|
||||
{
|
||||
const QVariant tabData = m_tabWidget->tabBar()->tabData(tabIndex);
|
||||
if (!tabData.isNull())
|
||||
{
|
||||
// We need to be able to convert between a UUID and a string to store and retrieve a document ID from the tab bar
|
||||
const QString documentIdString = tabData.toString();
|
||||
const QByteArray documentIdBytes = documentIdString.toUtf8();
|
||||
const AZ::Uuid documentId(documentIdBytes.data(), documentIdBytes.size());
|
||||
return documentId;
|
||||
}
|
||||
return AZ::Uuid::CreateNull();
|
||||
}
|
||||
|
||||
void AtomToolsDocumentMainWindow::AddDocumentTab(
|
||||
const AZ::Uuid& documentId, const AZStd::string& label, const AZStd::string& toolTip)
|
||||
{
|
||||
// Blocking signals from the tab bar so the currentChanged signal is not sent while a document is already being opened.
|
||||
// This prevents the OnDocumentOpened notification from being sent recursively.
|
||||
const QSignalBlocker blocker(m_tabWidget);
|
||||
|
||||
// If a tab for this document already exists then select it instead of creating a new one
|
||||
for (int tabIndex = 0; tabIndex < m_tabWidget->count(); ++tabIndex)
|
||||
{
|
||||
if (documentId == GetDocumentTabId(tabIndex))
|
||||
{
|
||||
m_tabWidget->setCurrentIndex(tabIndex);
|
||||
m_tabWidget->repaint();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const int tabIndex = m_tabWidget->addTab(CreateDocumentTabView(documentId), label.c_str());
|
||||
|
||||
// The user can manually reorder tabs which will invalidate any association by index.
|
||||
// We need to store the document ID with the tab using the tab instead of a separate mapping.
|
||||
m_tabWidget->tabBar()->setTabData(tabIndex, QVariant(documentId.ToString<QString>()));
|
||||
m_tabWidget->setTabToolTip(tabIndex, toolTip.c_str());
|
||||
m_tabWidget->setCurrentIndex(tabIndex);
|
||||
m_tabWidget->setVisible(true);
|
||||
m_tabWidget->repaint();
|
||||
}
|
||||
|
||||
void AtomToolsDocumentMainWindow::RemoveDocumentTab(const AZ::Uuid& documentId)
|
||||
{
|
||||
// We are not blocking signals here because we want closing tabs to close the associated document
|
||||
// and automatically select the next document.
|
||||
for (int tabIndex = 0; tabIndex < m_tabWidget->count(); ++tabIndex)
|
||||
{
|
||||
if (documentId == GetDocumentTabId(tabIndex))
|
||||
{
|
||||
m_tabWidget->removeTab(tabIndex);
|
||||
m_tabWidget->setVisible(m_tabWidget->count() > 0);
|
||||
m_tabWidget->repaint();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AtomToolsDocumentMainWindow::UpdateDocumentTab(
|
||||
const AZ::Uuid& documentId, const AZStd::string& label, const AZStd::string& toolTip, bool isModified)
|
||||
{
|
||||
// Whenever a document is opened, saved, or modified we need to update the tab label
|
||||
if (!documentId.IsNull())
|
||||
{
|
||||
// Because tab order and indexes can change from user interactions, we cannot store a map
|
||||
// between a tab index and document ID.
|
||||
// We must iterate over all of the tabs to find the one associated with this document.
|
||||
for (int tabIndex = 0; tabIndex < m_tabWidget->count(); ++tabIndex)
|
||||
{
|
||||
if (documentId == GetDocumentTabId(tabIndex))
|
||||
{
|
||||
// We use an asterisk prepended to the file name to denote modified document
|
||||
// Appending is standard and preferred but the tabs elide from the
|
||||
// end (instead of middle) and cut it off
|
||||
const AZStd::string modifiedLabel = isModified ? "* " + label : label;
|
||||
m_tabWidget->setTabText(tabIndex, modifiedLabel.c_str());
|
||||
m_tabWidget->setTabToolTip(tabIndex, toolTip.c_str());
|
||||
m_tabWidget->repaint();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AtomToolsDocumentMainWindow::SelectPrevDocumentTab()
|
||||
{
|
||||
if (m_tabWidget->count() > 1)
|
||||
{
|
||||
// Adding count to wrap around when index <= 0
|
||||
m_tabWidget->setCurrentIndex((m_tabWidget->currentIndex() + m_tabWidget->count() - 1) % m_tabWidget->count());
|
||||
}
|
||||
}
|
||||
|
||||
void AtomToolsDocumentMainWindow::SelectNextDocumentTab()
|
||||
{
|
||||
if (m_tabWidget->count() > 1)
|
||||
{
|
||||
m_tabWidget->setCurrentIndex((m_tabWidget->currentIndex() + 1) % m_tabWidget->count());
|
||||
}
|
||||
}
|
||||
|
||||
QWidget* AtomToolsDocumentMainWindow::CreateDocumentTabView(const AZ::Uuid& documentId)
|
||||
{
|
||||
AZ_UNUSED(documentId);
|
||||
auto contentWidget = new QWidget(centralWidget());
|
||||
contentWidget->setContentsMargins(0, 0, 0, 0);
|
||||
contentWidget->setFixedSize(0, 0);
|
||||
return contentWidget;
|
||||
}
|
||||
|
||||
void AtomToolsDocumentMainWindow::OpenDocumentTabContextMenu()
|
||||
{
|
||||
const QTabBar* tabBar = m_tabWidget->tabBar();
|
||||
const QPoint position = tabBar->mapFromGlobal(QCursor::pos());
|
||||
const int clickedTabIndex = tabBar->tabAt(position);
|
||||
const int currentTabIndex = tabBar->currentIndex();
|
||||
if (clickedTabIndex >= 0)
|
||||
{
|
||||
QMenu tabMenu;
|
||||
const QString selectActionName = (currentTabIndex == clickedTabIndex) ? "Select in Browser" : "Select";
|
||||
tabMenu.addAction(selectActionName, [this, clickedTabIndex]() {
|
||||
const AZ::Uuid documentId = GetDocumentTabId(clickedTabIndex);
|
||||
AtomToolsDocumentNotificationBus::Broadcast(&AtomToolsDocumentNotificationBus::Events::OnDocumentOpened, documentId);
|
||||
});
|
||||
tabMenu.addAction("Close", [this, clickedTabIndex]() {
|
||||
const AZ::Uuid documentId = GetDocumentTabId(clickedTabIndex);
|
||||
AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsDocumentSystemRequestBus::Events::CloseDocument, documentId);
|
||||
});
|
||||
auto closeOthersAction = tabMenu.addAction("Close Others", [this, clickedTabIndex]() {
|
||||
const AZ::Uuid documentId = GetDocumentTabId(clickedTabIndex);
|
||||
AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsDocumentSystemRequestBus::Events::CloseAllDocumentsExcept, documentId);
|
||||
});
|
||||
closeOthersAction->setEnabled(tabBar->count() > 1);
|
||||
tabMenu.exec(QCursor::pos());
|
||||
}
|
||||
}
|
||||
|
||||
bool AtomToolsDocumentMainWindow::GetCreateDocumentParams(AZStd::string& openPath, AZStd::string& savePath)
|
||||
{
|
||||
AZ_UNUSED(openPath);
|
||||
AZ_UNUSED(savePath);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AtomToolsDocumentMainWindow::GetOpenDocumentParams(AZStd::string& openPath)
|
||||
{
|
||||
AZ_UNUSED(openPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
void AtomToolsDocumentMainWindow::OnDocumentOpened(const AZ::Uuid& documentId)
|
||||
{
|
||||
bool isOpen = false;
|
||||
AtomToolsDocumentRequestBus::EventResult(isOpen, documentId, &AtomToolsDocumentRequestBus::Events::IsOpen);
|
||||
bool isSavable = false;
|
||||
AtomToolsDocumentRequestBus::EventResult(isSavable, documentId, &AtomToolsDocumentRequestBus::Events::IsSavable);
|
||||
bool isModified = false;
|
||||
AtomToolsDocumentRequestBus::EventResult(isModified, documentId, &AtomToolsDocumentRequestBus::Events::IsModified);
|
||||
bool canUndo = false;
|
||||
AtomToolsDocumentRequestBus::EventResult(canUndo, documentId, &AtomToolsDocumentRequestBus::Events::CanUndo);
|
||||
bool canRedo = false;
|
||||
AtomToolsDocumentRequestBus::EventResult(canRedo, documentId, &AtomToolsDocumentRequestBus::Events::CanRedo);
|
||||
AZStd::string absolutePath;
|
||||
AtomToolsDocumentRequestBus::EventResult(absolutePath, documentId, &AtomToolsDocumentRequestBus::Events::GetAbsolutePath);
|
||||
AZStd::string filename;
|
||||
AzFramework::StringFunc::Path::GetFullFileName(absolutePath.c_str(), filename);
|
||||
|
||||
// Update UI to display the new document
|
||||
if (!documentId.IsNull() && isOpen)
|
||||
{
|
||||
// Create a new tab for the document ID and assign it's label to the file name of the document.
|
||||
AddDocumentTab(documentId, filename, absolutePath);
|
||||
UpdateDocumentTab(documentId, filename, absolutePath, isModified);
|
||||
}
|
||||
|
||||
const bool hasTabs = m_tabWidget->count() > 0;
|
||||
|
||||
// Update menu options
|
||||
m_actionNew->setEnabled(true);
|
||||
m_actionOpen->setEnabled(true);
|
||||
m_actionClose->setEnabled(hasTabs);
|
||||
m_actionCloseAll->setEnabled(hasTabs);
|
||||
m_actionCloseOthers->setEnabled(hasTabs);
|
||||
|
||||
m_actionSave->setEnabled(isOpen && isSavable);
|
||||
m_actionSaveAsCopy->setEnabled(isOpen && isSavable);
|
||||
m_actionSaveAsChild->setEnabled(isOpen);
|
||||
m_actionSaveAll->setEnabled(hasTabs);
|
||||
|
||||
m_actionUndo->setEnabled(canUndo);
|
||||
m_actionRedo->setEnabled(canRedo);
|
||||
|
||||
m_actionPreviousTab->setEnabled(m_tabWidget->count() > 1);
|
||||
m_actionNextTab->setEnabled(m_tabWidget->count() > 1);
|
||||
|
||||
activateWindow();
|
||||
raise();
|
||||
|
||||
const QString documentPath = GetDocumentPath(documentId);
|
||||
if (!documentPath.isEmpty())
|
||||
{
|
||||
SetStatusMessage(tr("Document opened: %1").arg(documentPath));
|
||||
}
|
||||
}
|
||||
|
||||
void AtomToolsDocumentMainWindow::OnDocumentClosed(const AZ::Uuid& documentId)
|
||||
{
|
||||
RemoveDocumentTab(documentId);
|
||||
SetStatusMessage(tr("Document closed: %1").arg(GetDocumentPath(documentId)));
|
||||
}
|
||||
|
||||
void AtomToolsDocumentMainWindow::OnDocumentModified(const AZ::Uuid& documentId)
|
||||
{
|
||||
bool isModified = false;
|
||||
AtomToolsDocumentRequestBus::EventResult(isModified, documentId, &AtomToolsDocumentRequestBus::Events::IsModified);
|
||||
AZStd::string absolutePath;
|
||||
AtomToolsDocumentRequestBus::EventResult(absolutePath, documentId, &AtomToolsDocumentRequestBus::Events::GetAbsolutePath);
|
||||
AZStd::string filename;
|
||||
AzFramework::StringFunc::Path::GetFullFileName(absolutePath.c_str(), filename);
|
||||
UpdateDocumentTab(documentId, filename, absolutePath, isModified);
|
||||
}
|
||||
|
||||
void AtomToolsDocumentMainWindow::OnDocumentUndoStateChanged(const AZ::Uuid& documentId)
|
||||
{
|
||||
if (documentId == GetDocumentTabId(m_tabWidget->currentIndex()))
|
||||
{
|
||||
bool canUndo = false;
|
||||
AtomToolsDocumentRequestBus::EventResult(canUndo, documentId, &AtomToolsDocumentRequestBus::Events::CanUndo);
|
||||
bool canRedo = false;
|
||||
AtomToolsDocumentRequestBus::EventResult(canRedo, documentId, &AtomToolsDocumentRequestBus::Events::CanRedo);
|
||||
m_actionUndo->setEnabled(canUndo);
|
||||
m_actionRedo->setEnabled(canRedo);
|
||||
}
|
||||
}
|
||||
|
||||
void AtomToolsDocumentMainWindow::OnDocumentSaved(const AZ::Uuid& documentId)
|
||||
{
|
||||
bool isModified = false;
|
||||
AtomToolsDocumentRequestBus::EventResult(isModified, documentId, &AtomToolsDocumentRequestBus::Events::IsModified);
|
||||
AZStd::string absolutePath;
|
||||
AtomToolsDocumentRequestBus::EventResult(absolutePath, documentId, &AtomToolsDocumentRequestBus::Events::GetAbsolutePath);
|
||||
AZStd::string filename;
|
||||
AzFramework::StringFunc::Path::GetFullFileName(absolutePath.c_str(), filename);
|
||||
UpdateDocumentTab(documentId, filename, absolutePath, isModified);
|
||||
SetStatusMessage(tr("Document saved: %1").arg(GetDocumentPath(documentId)));
|
||||
}
|
||||
|
||||
|
||||
void AtomToolsDocumentMainWindow::closeEvent(QCloseEvent* closeEvent)
|
||||
{
|
||||
bool didClose = true;
|
||||
AtomToolsDocumentSystemRequestBus::BroadcastResult(didClose, &AtomToolsDocumentSystemRequestBus::Events::CloseAllDocuments);
|
||||
if (!didClose)
|
||||
{
|
||||
closeEvent->ignore();
|
||||
return;
|
||||
}
|
||||
|
||||
AtomToolsMainWindowNotificationBus::Broadcast(&AtomToolsMainWindowNotifications::OnMainWindowClosing);
|
||||
}
|
||||
|
||||
template<typename Functor>
|
||||
QAction* AtomToolsDocumentMainWindow::CreateAction(const QString& text, Functor functor, const QKeySequence& shortcut)
|
||||
{
|
||||
QAction* action = new QAction(text, this);
|
||||
action->setShortcut(shortcut);
|
||||
connect(action, &QAction::triggered, this, functor);
|
||||
return action;
|
||||
}
|
||||
} // namespace AtomToolsFramework
|
||||
|
||||
//#include <Document/moc_AtomToolsDocumentMainWindow.cpp>
|
||||
@@ -403,11 +403,6 @@ namespace AtomToolsFramework
|
||||
return aznumeric_cast<float>(devicePixelRatioF());
|
||||
}
|
||||
|
||||
AzFramework::ScreenPoint RenderViewportWidget::ViewportCursorScreenPosition()
|
||||
{
|
||||
return AzToolsFramework::ViewportInteraction::ScreenPointFromQPoint(m_mousePosition.toPoint());
|
||||
}
|
||||
|
||||
bool RenderViewportWidget::IsMouseOver() const
|
||||
{
|
||||
return m_mouseOver;
|
||||
|
||||
@@ -7,6 +7,11 @@
|
||||
*/
|
||||
|
||||
#include <AtomToolsFramework/Window/AtomToolsMainWindow.h>
|
||||
#include <AzToolsFramework/API/EditorPythonRunnerRequestsBus.h>
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QMenu>
|
||||
#include <QMenuBar>
|
||||
#include <QStatusBar>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
@@ -23,6 +28,8 @@ namespace AtomToolsFramework
|
||||
setCorner(Qt::TopRightCorner, Qt::RightDockWidgetArea);
|
||||
setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea);
|
||||
|
||||
AddCommonMenus();
|
||||
|
||||
m_statusMessage = new QLabel(statusBar());
|
||||
statusBar()->addPermanentWidget(m_statusMessage, 1);
|
||||
|
||||
@@ -65,6 +72,10 @@ namespace AtomToolsFramework
|
||||
addDockWidget(aznumeric_cast<Qt::DockWidgetArea>(area), dockWidget);
|
||||
resizeDocks({ dockWidget }, { 400 }, aznumeric_cast<Qt::Orientation>(orientation));
|
||||
m_dockWidgets[name] = dockWidget;
|
||||
|
||||
m_dockActions[name] = m_menuView->addAction(name.c_str(), [this, name](){
|
||||
SetDockWidgetVisible(name, !IsDockWidgetVisible(name));
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -76,6 +87,12 @@ namespace AtomToolsFramework
|
||||
delete dockWidgetItr->second;
|
||||
m_dockWidgets.erase(dockWidgetItr);
|
||||
}
|
||||
auto dockActionItr = m_dockActions.find(name);
|
||||
if (dockActionItr != m_dockActions.end())
|
||||
{
|
||||
delete dockActionItr->second;
|
||||
m_dockActions.erase(dockActionItr);
|
||||
}
|
||||
}
|
||||
|
||||
void AtomToolsMainWindow::SetDockWidgetVisible(const AZStd::string& name, bool visible)
|
||||
@@ -108,145 +125,6 @@ namespace AtomToolsFramework
|
||||
return names;
|
||||
}
|
||||
|
||||
void AtomToolsMainWindow::CreateMenu()
|
||||
{
|
||||
auto menuBar = new QMenuBar(this);
|
||||
menuBar->setObjectName("MenuBar");
|
||||
setMenuBar(menuBar);
|
||||
}
|
||||
|
||||
void AtomToolsMainWindow::CreateTabBar()
|
||||
{
|
||||
m_tabWidget = new AzQtComponents::TabWidget(centralWidget());
|
||||
m_tabWidget->setObjectName("TabWidget");
|
||||
m_tabWidget->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred);
|
||||
m_tabWidget->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
// The tab bar should only be visible if it has active documents
|
||||
m_tabWidget->setVisible(false);
|
||||
m_tabWidget->setTabBarAutoHide(false);
|
||||
m_tabWidget->setMovable(true);
|
||||
m_tabWidget->setTabsClosable(true);
|
||||
m_tabWidget->setUsesScrollButtons(true);
|
||||
|
||||
// Add context menu for right-clicking on tabs
|
||||
m_tabWidget->setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu);
|
||||
connect(
|
||||
m_tabWidget, &QWidget::customContextMenuRequested, this,
|
||||
[this]()
|
||||
{
|
||||
OpenTabContextMenu();
|
||||
});
|
||||
|
||||
centralWidget()->layout()->addWidget(m_tabWidget);
|
||||
}
|
||||
|
||||
void AtomToolsMainWindow::AddTabForDocumentId(
|
||||
const AZ::Uuid& documentId, const AZStd::string& label, const AZStd::string& toolTip, AZStd::function<QWidget*()> widgetCreator)
|
||||
{
|
||||
// Blocking signals from the tab bar so the currentChanged signal is not sent while a document is already being opened.
|
||||
// This prevents the OnDocumentOpened notification from being sent recursively.
|
||||
const QSignalBlocker blocker(m_tabWidget);
|
||||
|
||||
// If a tab for this document already exists then select it instead of creating a new one
|
||||
for (int tabIndex = 0; tabIndex < m_tabWidget->count(); ++tabIndex)
|
||||
{
|
||||
if (documentId == GetDocumentIdFromTab(tabIndex))
|
||||
{
|
||||
m_tabWidget->setCurrentIndex(tabIndex);
|
||||
m_tabWidget->repaint();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const int tabIndex = m_tabWidget->addTab(widgetCreator(), label.c_str());
|
||||
|
||||
// The user can manually reorder tabs which will invalidate any association by index.
|
||||
// We need to store the document ID with the tab using the tab instead of a separate mapping.
|
||||
m_tabWidget->tabBar()->setTabData(tabIndex, QVariant(documentId.ToString<QString>()));
|
||||
m_tabWidget->setTabToolTip(tabIndex, toolTip.c_str());
|
||||
m_tabWidget->setCurrentIndex(tabIndex);
|
||||
m_tabWidget->setVisible(true);
|
||||
m_tabWidget->repaint();
|
||||
}
|
||||
|
||||
void AtomToolsMainWindow::RemoveTabForDocumentId(const AZ::Uuid& documentId)
|
||||
{
|
||||
// We are not blocking signals here because we want closing tabs to close the associated document
|
||||
// and automatically select the next document.
|
||||
for (int tabIndex = 0; tabIndex < m_tabWidget->count(); ++tabIndex)
|
||||
{
|
||||
if (documentId == GetDocumentIdFromTab(tabIndex))
|
||||
{
|
||||
m_tabWidget->removeTab(tabIndex);
|
||||
m_tabWidget->setVisible(m_tabWidget->count() > 0);
|
||||
m_tabWidget->repaint();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AtomToolsMainWindow::UpdateTabForDocumentId(
|
||||
const AZ::Uuid& documentId, const AZStd::string& label, const AZStd::string& toolTip, bool isModified)
|
||||
{
|
||||
// Whenever a document is opened, saved, or modified we need to update the tab label
|
||||
if (!documentId.IsNull())
|
||||
{
|
||||
// Because tab order and indexes can change from user interactions, we cannot store a map
|
||||
// between a tab index and document ID.
|
||||
// We must iterate over all of the tabs to find the one associated with this document.
|
||||
for (int tabIndex = 0; tabIndex < m_tabWidget->count(); ++tabIndex)
|
||||
{
|
||||
if (documentId == GetDocumentIdFromTab(tabIndex))
|
||||
{
|
||||
// We use an asterisk prepended to the file name to denote modified document
|
||||
// Appending is standard and preferred but the tabs elide from the
|
||||
// end (instead of middle) and cut it off
|
||||
const AZStd::string modifiedLabel = isModified ? "* " + label : label;
|
||||
m_tabWidget->setTabText(tabIndex, modifiedLabel.c_str());
|
||||
m_tabWidget->setTabToolTip(tabIndex, toolTip.c_str());
|
||||
m_tabWidget->repaint();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AZ::Uuid AtomToolsMainWindow::GetDocumentIdFromTab(const int tabIndex) const
|
||||
{
|
||||
const QVariant tabData = m_tabWidget->tabBar()->tabData(tabIndex);
|
||||
if (!tabData.isNull())
|
||||
{
|
||||
// We need to be able to convert between a UUID and a string to store and retrieve a document ID from the tab bar
|
||||
const QString documentIdString = tabData.toString();
|
||||
const QByteArray documentIdBytes = documentIdString.toUtf8();
|
||||
const AZ::Uuid documentId(documentIdBytes.data(), documentIdBytes.size());
|
||||
return documentId;
|
||||
}
|
||||
return AZ::Uuid::CreateNull();
|
||||
}
|
||||
|
||||
void AtomToolsMainWindow::OpenTabContextMenu()
|
||||
{
|
||||
}
|
||||
|
||||
void AtomToolsMainWindow::SelectPreviousTab()
|
||||
{
|
||||
if (m_tabWidget->count() > 1)
|
||||
{
|
||||
// Adding count to wrap around when index <= 0
|
||||
m_tabWidget->setCurrentIndex((m_tabWidget->currentIndex() + m_tabWidget->count() - 1) % m_tabWidget->count());
|
||||
}
|
||||
}
|
||||
|
||||
void AtomToolsMainWindow::SelectNextTab()
|
||||
{
|
||||
if (m_tabWidget->count() > 1)
|
||||
{
|
||||
m_tabWidget->setCurrentIndex((m_tabWidget->currentIndex() + 1) % m_tabWidget->count());
|
||||
}
|
||||
}
|
||||
|
||||
void AtomToolsMainWindow::SetStatusMessage(const QString& message)
|
||||
{
|
||||
m_statusMessage->setText(QString("<font color=\"White\">%1</font>").arg(message));
|
||||
@@ -261,4 +139,50 @@ namespace AtomToolsFramework
|
||||
{
|
||||
m_statusMessage->setText(QString("<font color=\"Red\">%1</font>").arg(message));
|
||||
}
|
||||
|
||||
void AtomToolsMainWindow::AddCommonMenus()
|
||||
{
|
||||
m_menuFile = menuBar()->addMenu("&File");
|
||||
m_menuEdit = menuBar()->addMenu("&Edit");
|
||||
m_menuView = menuBar()->addMenu("&View");
|
||||
m_menuHelp = menuBar()->addMenu("&Help");
|
||||
|
||||
m_menuFile->addAction("Run &Python...", [this]() {
|
||||
const QString script = QFileDialog::getOpenFileName(this, "Run Script", QString(), QString("*.py"));
|
||||
if (!script.isEmpty())
|
||||
{
|
||||
AzToolsFramework::EditorPythonRunnerRequestBus::Broadcast(&AzToolsFramework::EditorPythonRunnerRequestBus::Events::ExecuteByFilename, script.toUtf8().constData());
|
||||
}
|
||||
});
|
||||
|
||||
m_menuFile->addSeparator();
|
||||
|
||||
m_menuFile->addAction("E&xit", [this]() {
|
||||
close();
|
||||
}, QKeySequence::Quit);
|
||||
|
||||
m_menuEdit->addAction("&Settings...", [this]() {
|
||||
OpenSettings();
|
||||
}, QKeySequence::Preferences);
|
||||
|
||||
m_menuHelp->addAction("&Help...", [this]() {
|
||||
OpenHelp();
|
||||
});
|
||||
|
||||
m_menuHelp->addAction("&About...", [this]() {
|
||||
OpenAbout();
|
||||
});
|
||||
}
|
||||
|
||||
void AtomToolsMainWindow::OpenSettings()
|
||||
{
|
||||
}
|
||||
|
||||
void AtomToolsMainWindow::OpenHelp()
|
||||
{
|
||||
}
|
||||
|
||||
void AtomToolsMainWindow::OpenAbout()
|
||||
{
|
||||
}
|
||||
} // namespace AtomToolsFramework
|
||||
|
||||
@@ -12,6 +12,7 @@ set(FILES
|
||||
Include/AtomToolsFramework/Communication/LocalSocket.h
|
||||
Include/AtomToolsFramework/Debug/TraceRecorder.h
|
||||
Include/AtomToolsFramework/Document/AtomToolsDocument.h
|
||||
Include/AtomToolsFramework/Document/AtomToolsDocumentMainWindow.h
|
||||
Include/AtomToolsFramework/Document/AtomToolsDocumentSystemSettings.h
|
||||
Include/AtomToolsFramework/Document/AtomToolsDocumentSystemRequestBus.h
|
||||
Include/AtomToolsFramework/Document/AtomToolsDocumentNotificationBus.h
|
||||
@@ -38,6 +39,7 @@ set(FILES
|
||||
Source/Communication/LocalSocket.cpp
|
||||
Source/Debug/TraceRecorder.cpp
|
||||
Source/Document/AtomToolsDocument.cpp
|
||||
Source/Document/AtomToolsDocumentMainWindow.cpp
|
||||
Source/Document/AtomToolsDocumentSystemSettings.cpp
|
||||
Source/Document/AtomToolsDocumentSystemComponent.cpp
|
||||
Source/Document/AtomToolsDocumentSystemComponent.h
|
||||
|
||||
@@ -9,15 +9,9 @@
|
||||
#include <Atom/Document/MaterialDocumentRequestBus.h>
|
||||
#include <Atom/RHI/Factory.h>
|
||||
#include <Atom/Window/MaterialEditorWindowSettings.h>
|
||||
#include <AtomToolsFramework/Document/AtomToolsDocumentRequestBus.h>
|
||||
#include <AtomToolsFramework/Document/AtomToolsDocumentSystemRequestBus.h>
|
||||
#include <AtomToolsFramework/Util/Util.h>
|
||||
#include <AtomToolsFramework/Window/AtomToolsMainWindowNotificationBus.h>
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
#include <AzQtComponents/Components/StyleManager.h>
|
||||
#include <AzQtComponents/Components/WindowDecorationWrapper.h>
|
||||
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
|
||||
#include <AzToolsFramework/API/EditorPythonRunnerRequestsBus.h>
|
||||
#include <AzToolsFramework/PythonTerminal/ScriptTermDialog.h>
|
||||
#include <Viewport/MaterialViewportWidget.h>
|
||||
#include <Window/CreateMaterialDialog/CreateMaterialDialog.h>
|
||||
@@ -33,7 +27,6 @@ AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnin
|
||||
#include <QApplication>
|
||||
#include <QByteArray>
|
||||
#include <QCloseEvent>
|
||||
#include <QDesktopWidget>
|
||||
#include <QFileDialog>
|
||||
#include <QWindow>
|
||||
AZ_POP_DISABLE_WARNING
|
||||
@@ -41,7 +34,7 @@ AZ_POP_DISABLE_WARNING
|
||||
namespace MaterialEditor
|
||||
{
|
||||
MaterialEditorWindow::MaterialEditorWindow(QWidget* parent /* = 0 */)
|
||||
: AtomToolsFramework::AtomToolsMainWindow(parent)
|
||||
: AtomToolsFramework::AtomToolsDocumentMainWindow(parent)
|
||||
{
|
||||
resize(1280, 1024);
|
||||
|
||||
@@ -74,9 +67,6 @@ namespace MaterialEditor
|
||||
m_toolBar->setObjectName("ToolBar");
|
||||
addToolBar(m_toolBar);
|
||||
|
||||
CreateMenu();
|
||||
CreateTabBar();
|
||||
|
||||
m_materialViewport = new MaterialViewportWidget(centralWidget());
|
||||
m_materialViewport->setObjectName("Viewport");
|
||||
m_materialViewport->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
|
||||
@@ -105,16 +95,9 @@ namespace MaterialEditor
|
||||
m_advancedDockManager->restoreState(windowState);
|
||||
}
|
||||
|
||||
AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler::BusConnect();
|
||||
OnDocumentOpened(AZ::Uuid::CreateNull());
|
||||
}
|
||||
|
||||
MaterialEditorWindow::~MaterialEditorWindow()
|
||||
{
|
||||
AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
|
||||
void MaterialEditorWindow::ResizeViewportRenderTarget(uint32_t width, uint32_t height)
|
||||
{
|
||||
QSize requestedViewportSize = QSize(width, height) / devicePixelRatioF();
|
||||
@@ -146,16 +129,43 @@ namespace MaterialEditor
|
||||
m_materialViewport->UnlockRenderTargetSize();
|
||||
}
|
||||
|
||||
bool MaterialEditorWindow::GetCreateDocumentParams(AZStd::string& openPath, AZStd::string& savePath)
|
||||
{
|
||||
CreateMaterialDialog createDialog(this);
|
||||
createDialog.adjustSize();
|
||||
|
||||
if (createDialog.exec() == QDialog::Accepted &&
|
||||
!createDialog.m_materialFileInfo.absoluteFilePath().isEmpty() &&
|
||||
!createDialog.m_materialTypeFileInfo.absoluteFilePath().isEmpty())
|
||||
{
|
||||
savePath = createDialog.m_materialFileInfo.absoluteFilePath().toUtf8().constData();
|
||||
openPath = createDialog.m_materialTypeFileInfo.absoluteFilePath().toUtf8().constData();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MaterialEditorWindow::GetOpenDocumentParams(AZStd::string& openPath)
|
||||
{
|
||||
const AZStd::vector<AZ::Data::AssetType> assetTypes = { azrtti_typeid<AZ::RPI::MaterialAsset>() };
|
||||
openPath = AtomToolsFramework::GetOpenFileInfo(assetTypes).absoluteFilePath().toUtf8().constData();
|
||||
return !openPath.empty();
|
||||
}
|
||||
|
||||
void MaterialEditorWindow::OpenSettings()
|
||||
{
|
||||
SettingsDialog dialog(this);
|
||||
dialog.exec();
|
||||
}
|
||||
|
||||
void MaterialEditorWindow::OpenHelp()
|
||||
{
|
||||
HelpDialog dialog(this);
|
||||
dialog.exec();
|
||||
}
|
||||
|
||||
void MaterialEditorWindow::closeEvent(QCloseEvent* closeEvent)
|
||||
{
|
||||
bool didClose = true;
|
||||
AtomToolsFramework::AtomToolsDocumentSystemRequestBus::BroadcastResult(didClose, &AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::CloseAllDocuments);
|
||||
if (!didClose)
|
||||
{
|
||||
closeEvent->ignore();
|
||||
return;
|
||||
}
|
||||
|
||||
// Capture docking state before shutdown
|
||||
auto windowSettings = AZ::UserSettings::CreateFind<MaterialEditorWindowSettings>(
|
||||
AZ::Crc32("MaterialEditorWindowSettings"), AZ::UserSettings::CT_GLOBAL);
|
||||
@@ -163,368 +173,7 @@ namespace MaterialEditor
|
||||
QByteArray windowState = m_advancedDockManager->saveState();
|
||||
windowSettings->m_mainWindowState.assign(windowState.begin(), windowState.end());
|
||||
|
||||
AtomToolsFramework::AtomToolsMainWindowNotificationBus::Broadcast(
|
||||
&AtomToolsFramework::AtomToolsMainWindowNotifications::OnMainWindowClosing);
|
||||
}
|
||||
|
||||
void MaterialEditorWindow::OnDocumentOpened(const AZ::Uuid& documentId)
|
||||
{
|
||||
bool isOpen = false;
|
||||
AtomToolsFramework::AtomToolsDocumentRequestBus::EventResult(isOpen, documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::IsOpen);
|
||||
bool isSavable = false;
|
||||
AtomToolsFramework::AtomToolsDocumentRequestBus::EventResult(isSavable, documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::IsSavable);
|
||||
bool isModified = false;
|
||||
AtomToolsFramework::AtomToolsDocumentRequestBus::EventResult(isModified, documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::IsModified);
|
||||
bool canUndo = false;
|
||||
AtomToolsFramework::AtomToolsDocumentRequestBus::EventResult(canUndo, documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::CanUndo);
|
||||
bool canRedo = false;
|
||||
AtomToolsFramework::AtomToolsDocumentRequestBus::EventResult(canRedo, documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::CanRedo);
|
||||
AZStd::string absolutePath;
|
||||
AtomToolsFramework::AtomToolsDocumentRequestBus::EventResult(absolutePath, documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::GetAbsolutePath);
|
||||
AZStd::string filename;
|
||||
AzFramework::StringFunc::Path::GetFullFileName(absolutePath.c_str(), filename);
|
||||
|
||||
// Update UI to display the new document
|
||||
if (!documentId.IsNull() && isOpen)
|
||||
{
|
||||
// Create a new tab for the document ID and assign it's label to the file name of the document.
|
||||
AddTabForDocumentId(documentId, filename, absolutePath, [this]{
|
||||
// The tab widget requires a dummy page per tab
|
||||
auto contentWidget = new QWidget(centralWidget());
|
||||
contentWidget->setContentsMargins(0, 0, 0, 0);
|
||||
contentWidget->setFixedSize(0, 0);
|
||||
return contentWidget;
|
||||
});
|
||||
}
|
||||
|
||||
UpdateTabForDocumentId(documentId, filename, absolutePath, isModified);
|
||||
|
||||
const bool hasTabs = m_tabWidget->count() > 0;
|
||||
|
||||
// Update menu options
|
||||
m_actionNew->setEnabled(true);
|
||||
m_actionOpen->setEnabled(true);
|
||||
m_actionOpenRecent->setEnabled(false);
|
||||
m_actionClose->setEnabled(hasTabs);
|
||||
m_actionCloseAll->setEnabled(hasTabs);
|
||||
m_actionCloseOthers->setEnabled(hasTabs);
|
||||
|
||||
m_actionSave->setEnabled(isOpen && isSavable);
|
||||
m_actionSaveAsCopy->setEnabled(isOpen && isSavable);
|
||||
m_actionSaveAsChild->setEnabled(isOpen);
|
||||
m_actionSaveAll->setEnabled(hasTabs);
|
||||
|
||||
m_actionExit->setEnabled(true);
|
||||
|
||||
m_actionUndo->setEnabled(canUndo);
|
||||
m_actionRedo->setEnabled(canRedo);
|
||||
m_actionSettings->setEnabled(true);
|
||||
|
||||
m_actionAssetBrowser->setEnabled(true);
|
||||
m_actionInspector->setEnabled(true);
|
||||
m_actionConsole->setEnabled(false);
|
||||
m_actionPythonTerminal->setEnabled(true);
|
||||
m_actionPerfMonitor->setEnabled(true);
|
||||
m_actionViewportSettings->setEnabled(true);
|
||||
m_actionPreviousTab->setEnabled(m_tabWidget->count() > 1);
|
||||
m_actionNextTab->setEnabled(m_tabWidget->count() > 1);
|
||||
|
||||
m_actionAbout->setEnabled(false);
|
||||
|
||||
activateWindow();
|
||||
raise();
|
||||
|
||||
const QString documentPath = GetDocumentPath(documentId);
|
||||
if (!documentPath.isEmpty())
|
||||
{
|
||||
SetStatusMessage(tr("Document opened: %1").arg(documentPath));
|
||||
}
|
||||
}
|
||||
|
||||
void MaterialEditorWindow::OnDocumentClosed(const AZ::Uuid& documentId)
|
||||
{
|
||||
RemoveTabForDocumentId(documentId);
|
||||
SetStatusMessage(tr("Document closed: %1").arg(GetDocumentPath(documentId)));
|
||||
}
|
||||
|
||||
void MaterialEditorWindow::OnDocumentModified(const AZ::Uuid& documentId)
|
||||
{
|
||||
bool isModified = false;
|
||||
AtomToolsFramework::AtomToolsDocumentRequestBus::EventResult(isModified, documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::IsModified);
|
||||
AZStd::string absolutePath;
|
||||
AtomToolsFramework::AtomToolsDocumentRequestBus::EventResult(absolutePath, documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::GetAbsolutePath);
|
||||
AZStd::string filename;
|
||||
AzFramework::StringFunc::Path::GetFullFileName(absolutePath.c_str(), filename);
|
||||
UpdateTabForDocumentId(documentId, filename, absolutePath, isModified);
|
||||
}
|
||||
|
||||
void MaterialEditorWindow::OnDocumentUndoStateChanged(const AZ::Uuid& documentId)
|
||||
{
|
||||
if (documentId == GetDocumentIdFromTab(m_tabWidget->currentIndex()))
|
||||
{
|
||||
bool canUndo = false;
|
||||
AtomToolsFramework::AtomToolsDocumentRequestBus::EventResult(canUndo, documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::CanUndo);
|
||||
bool canRedo = false;
|
||||
AtomToolsFramework::AtomToolsDocumentRequestBus::EventResult(canRedo, documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::CanRedo);
|
||||
m_actionUndo->setEnabled(canUndo);
|
||||
m_actionRedo->setEnabled(canRedo);
|
||||
}
|
||||
}
|
||||
|
||||
void MaterialEditorWindow::OnDocumentSaved(const AZ::Uuid& documentId)
|
||||
{
|
||||
bool isModified = false;
|
||||
AtomToolsFramework::AtomToolsDocumentRequestBus::EventResult(isModified, documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::IsModified);
|
||||
AZStd::string absolutePath;
|
||||
AtomToolsFramework::AtomToolsDocumentRequestBus::EventResult(absolutePath, documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::GetAbsolutePath);
|
||||
AZStd::string filename;
|
||||
AzFramework::StringFunc::Path::GetFullFileName(absolutePath.c_str(), filename);
|
||||
UpdateTabForDocumentId(documentId, filename, absolutePath, isModified);
|
||||
SetStatusMessage(tr("Document saved: %1").arg(GetDocumentPath(documentId)));
|
||||
}
|
||||
|
||||
void MaterialEditorWindow::CreateMenu()
|
||||
{
|
||||
Base::CreateMenu();
|
||||
|
||||
// Generating the main menu manually because it's easier and we will have some dynamic or data driven entries
|
||||
m_menuFile = menuBar()->addMenu("&File");
|
||||
|
||||
m_actionNew = m_menuFile->addAction("&New...", [this]() {
|
||||
CreateMaterialDialog createDialog(this);
|
||||
createDialog.adjustSize();
|
||||
|
||||
if (createDialog.exec() == QDialog::Accepted &&
|
||||
!createDialog.m_materialFileInfo.absoluteFilePath().isEmpty() &&
|
||||
!createDialog.m_materialTypeFileInfo.absoluteFilePath().isEmpty())
|
||||
{
|
||||
AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::CreateDocumentFromFile,
|
||||
createDialog.m_materialTypeFileInfo.absoluteFilePath().toUtf8().constData(),
|
||||
createDialog.m_materialFileInfo.absoluteFilePath().toUtf8().constData());
|
||||
}
|
||||
}, QKeySequence::New);
|
||||
|
||||
m_actionOpen = m_menuFile->addAction("&Open...", [this]() {
|
||||
const AZStd::vector<AZ::Data::AssetType> assetTypes = { azrtti_typeid<AZ::RPI::MaterialAsset>() };
|
||||
const AZStd::string filePath = AtomToolsFramework::GetOpenFileInfo(assetTypes).absoluteFilePath().toUtf8().constData();
|
||||
if (!filePath.empty())
|
||||
{
|
||||
AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::OpenDocument, filePath);
|
||||
}
|
||||
}, QKeySequence::Open);
|
||||
|
||||
m_actionOpenRecent = m_menuFile->addAction("Open &Recent");
|
||||
|
||||
m_menuFile->addSeparator();
|
||||
|
||||
m_actionSave = m_menuFile->addAction("&Save", [this]() {
|
||||
const AZ::Uuid documentId = GetDocumentIdFromTab(m_tabWidget->currentIndex());
|
||||
bool result = false;
|
||||
AtomToolsFramework::AtomToolsDocumentSystemRequestBus::BroadcastResult(result, &AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::SaveDocument, documentId);
|
||||
if (!result)
|
||||
{
|
||||
SetStatusError(tr("Document save failed: %1").arg(GetDocumentPath(documentId)));
|
||||
}
|
||||
}, QKeySequence::Save);
|
||||
|
||||
m_actionSaveAsCopy = m_menuFile->addAction("Save &As...", [this]() {
|
||||
const AZ::Uuid documentId = GetDocumentIdFromTab(m_tabWidget->currentIndex());
|
||||
const QString documentPath = GetDocumentPath(documentId);
|
||||
|
||||
bool result = false;
|
||||
AtomToolsFramework::AtomToolsDocumentSystemRequestBus::BroadcastResult(result, &AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::SaveDocumentAsCopy,
|
||||
documentId, AtomToolsFramework::GetSaveFileInfo(documentPath).absoluteFilePath().toUtf8().constData());
|
||||
if (!result)
|
||||
{
|
||||
SetStatusError(tr("Document save failed: %1").arg(GetDocumentPath(documentId)));
|
||||
}
|
||||
}, QKeySequence::SaveAs);
|
||||
|
||||
m_actionSaveAsChild = m_menuFile->addAction("Save As &Child...", [this]() {
|
||||
const AZ::Uuid documentId = GetDocumentIdFromTab(m_tabWidget->currentIndex());
|
||||
const QString documentPath = GetDocumentPath(documentId);
|
||||
|
||||
bool result = false;
|
||||
AtomToolsFramework::AtomToolsDocumentSystemRequestBus::BroadcastResult(result, &AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::SaveDocumentAsChild,
|
||||
documentId, AtomToolsFramework::GetSaveFileInfo(documentPath).absoluteFilePath().toUtf8().constData());
|
||||
if (!result)
|
||||
{
|
||||
SetStatusError(tr("Document save failed: %1").arg(GetDocumentPath(documentId)));
|
||||
}
|
||||
});
|
||||
|
||||
m_actionSaveAll = m_menuFile->addAction("Save A&ll", [this]() {
|
||||
bool result = false;
|
||||
AtomToolsFramework::AtomToolsDocumentSystemRequestBus::BroadcastResult(result, &AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::SaveAllDocuments);
|
||||
if (!result)
|
||||
{
|
||||
SetStatusError(tr("Document save all failed"));
|
||||
}
|
||||
});
|
||||
|
||||
m_menuFile->addSeparator();
|
||||
|
||||
m_actionClose = m_menuFile->addAction("&Close", [this]() {
|
||||
const AZ::Uuid documentId = GetDocumentIdFromTab(m_tabWidget->currentIndex());
|
||||
AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::CloseDocument, documentId);
|
||||
}, QKeySequence::Close);
|
||||
|
||||
m_actionCloseAll = m_menuFile->addAction("Close All", [this]() {
|
||||
AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::CloseAllDocuments);
|
||||
});
|
||||
|
||||
m_actionCloseOthers = m_menuFile->addAction("Close Others", [this]() {
|
||||
const AZ::Uuid documentId = GetDocumentIdFromTab(m_tabWidget->currentIndex());
|
||||
AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::CloseAllDocumentsExcept, documentId);
|
||||
});
|
||||
|
||||
m_menuFile->addSeparator();
|
||||
|
||||
m_menuFile->addAction("Run &Python...", [this]() {
|
||||
const QString script = QFileDialog::getOpenFileName(this, "Run Script", QString(), QString("*.py"));
|
||||
if (!script.isEmpty())
|
||||
{
|
||||
AzToolsFramework::EditorPythonRunnerRequestBus::Broadcast(&AzToolsFramework::EditorPythonRunnerRequestBus::Events::ExecuteByFilename, script.toUtf8().constData());
|
||||
}
|
||||
});
|
||||
|
||||
m_menuFile->addSeparator();
|
||||
|
||||
m_actionExit = m_menuFile->addAction("E&xit", [this]() {
|
||||
close();
|
||||
}, QKeySequence::Quit);
|
||||
|
||||
m_menuEdit = menuBar()->addMenu("&Edit");
|
||||
|
||||
m_actionUndo = m_menuEdit->addAction("&Undo", [this]() {
|
||||
const AZ::Uuid documentId = GetDocumentIdFromTab(m_tabWidget->currentIndex());
|
||||
bool result = false;
|
||||
AtomToolsFramework::AtomToolsDocumentRequestBus::EventResult(result, documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::Undo);
|
||||
if (!result)
|
||||
{
|
||||
SetStatusError(tr("Document undo failed: %1").arg(GetDocumentPath(documentId)));
|
||||
}
|
||||
}, QKeySequence::Undo);
|
||||
|
||||
m_actionRedo = m_menuEdit->addAction("&Redo", [this]() {
|
||||
const AZ::Uuid documentId = GetDocumentIdFromTab(m_tabWidget->currentIndex());
|
||||
bool result = false;
|
||||
AtomToolsFramework::AtomToolsDocumentRequestBus::EventResult(result, documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::Redo);
|
||||
if (!result)
|
||||
{
|
||||
SetStatusError(tr("Document redo failed: %1").arg(GetDocumentPath(documentId)));
|
||||
}
|
||||
}, QKeySequence::Redo);
|
||||
|
||||
m_menuEdit->addSeparator();
|
||||
|
||||
m_actionSettings = m_menuEdit->addAction("&Settings...", [this]() {
|
||||
SettingsDialog dialog(this);
|
||||
dialog.exec();
|
||||
}, QKeySequence::Preferences);
|
||||
m_actionSettings->setEnabled(true);
|
||||
|
||||
m_menuView = menuBar()->addMenu("&View");
|
||||
|
||||
m_actionAssetBrowser = m_menuView->addAction("&Asset Browser", [this]() {
|
||||
const AZStd::string label = "Asset Browser";
|
||||
SetDockWidgetVisible(label, !IsDockWidgetVisible(label));
|
||||
});
|
||||
|
||||
m_actionInspector = m_menuView->addAction("&Inspector", [this]() {
|
||||
const AZStd::string label = "Inspector";
|
||||
SetDockWidgetVisible(label, !IsDockWidgetVisible(label));
|
||||
});
|
||||
|
||||
m_actionConsole = m_menuView->addAction("&Console", [this]() {
|
||||
});
|
||||
|
||||
m_actionPythonTerminal = m_menuView->addAction("Python &Terminal", [this]() {
|
||||
const AZStd::string label = "Python Terminal";
|
||||
SetDockWidgetVisible(label, !IsDockWidgetVisible(label));
|
||||
});
|
||||
|
||||
m_actionPerfMonitor = m_menuView->addAction("Performance &Monitor", [this]() {
|
||||
const AZStd::string label = "Performance Monitor";
|
||||
SetDockWidgetVisible(label, !IsDockWidgetVisible(label));
|
||||
});
|
||||
|
||||
m_actionViewportSettings = m_menuView->addAction("Viewport Settings", [this]() {
|
||||
const AZStd::string label = "Viewport Settings";
|
||||
SetDockWidgetVisible(label, !IsDockWidgetVisible(label));
|
||||
});
|
||||
|
||||
m_menuView->addSeparator();
|
||||
|
||||
m_actionPreviousTab = m_menuView->addAction("&Previous Tab", [this]() {
|
||||
SelectPreviousTab();
|
||||
}, Qt::CTRL | Qt::SHIFT | Qt::Key_Tab); //QKeySequence::PreviousChild is mapped incorrectly in Qt
|
||||
|
||||
m_actionNextTab = m_menuView->addAction("&Next Tab", [this]() {
|
||||
SelectNextTab();
|
||||
}, Qt::CTRL | Qt::Key_Tab); //QKeySequence::NextChild works as expected but mirroring Previous
|
||||
|
||||
m_menuHelp = menuBar()->addMenu("&Help");
|
||||
|
||||
m_actionHelp = m_menuHelp->addAction("&Help...", [this]() {
|
||||
HelpDialog dialog(this);
|
||||
dialog.exec();
|
||||
});
|
||||
|
||||
m_actionAbout = m_menuHelp->addAction("&About...", [this]() {
|
||||
});
|
||||
}
|
||||
|
||||
void MaterialEditorWindow::CreateTabBar()
|
||||
{
|
||||
Base::CreateTabBar();
|
||||
|
||||
// This signal will be triggered whenever a tab is added, removed, selected, clicked, dragged
|
||||
// When the last tab is removed tabIndex will be -1 and the document ID will be null
|
||||
// This should automatically clear the active document
|
||||
connect(m_tabWidget, &QTabWidget::currentChanged, this, [this](int tabIndex) {
|
||||
const AZ::Uuid documentId = GetDocumentIdFromTab(tabIndex);
|
||||
AtomToolsFramework::AtomToolsDocumentNotificationBus::Broadcast(&AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentOpened, documentId);
|
||||
});
|
||||
|
||||
connect(m_tabWidget, &QTabWidget::tabCloseRequested, this, [this](int tabIndex) {
|
||||
const AZ::Uuid documentId = GetDocumentIdFromTab(tabIndex);
|
||||
AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::CloseDocument, documentId);
|
||||
});
|
||||
}
|
||||
|
||||
QString MaterialEditorWindow::GetDocumentPath(const AZ::Uuid& documentId) const
|
||||
{
|
||||
AZStd::string absolutePath;
|
||||
AtomToolsFramework::AtomToolsDocumentRequestBus::EventResult(absolutePath, documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Handler::GetAbsolutePath);
|
||||
return absolutePath.c_str();
|
||||
}
|
||||
|
||||
void MaterialEditorWindow::OpenTabContextMenu()
|
||||
{
|
||||
const QTabBar* tabBar = m_tabWidget->tabBar();
|
||||
const QPoint position = tabBar->mapFromGlobal(QCursor::pos());
|
||||
const int clickedTabIndex = tabBar->tabAt(position);
|
||||
const int currentTabIndex = tabBar->currentIndex();
|
||||
if (clickedTabIndex >= 0)
|
||||
{
|
||||
QMenu tabMenu;
|
||||
const QString selectActionName = (currentTabIndex == clickedTabIndex) ? "Select in Browser" : "Select";
|
||||
tabMenu.addAction(selectActionName, [this, clickedTabIndex]() {
|
||||
const AZ::Uuid documentId = GetDocumentIdFromTab(clickedTabIndex);
|
||||
AtomToolsFramework::AtomToolsDocumentNotificationBus::Broadcast(&AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentOpened, documentId);
|
||||
});
|
||||
tabMenu.addAction("Close", [this, clickedTabIndex]() {
|
||||
const AZ::Uuid documentId = GetDocumentIdFromTab(clickedTabIndex);
|
||||
AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::CloseDocument, documentId);
|
||||
});
|
||||
auto closeOthersAction = tabMenu.addAction("Close Others", [this, clickedTabIndex]() {
|
||||
const AZ::Uuid documentId = GetDocumentIdFromTab(clickedTabIndex);
|
||||
AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::CloseAllDocumentsExcept, documentId);
|
||||
});
|
||||
closeOthersAction->setEnabled(tabBar->count() > 1);
|
||||
tabMenu.exec(QCursor::pos());
|
||||
}
|
||||
Base::closeEvent(closeEvent);
|
||||
}
|
||||
} // namespace MaterialEditor
|
||||
|
||||
|
||||
@@ -9,9 +9,7 @@
|
||||
#pragma once
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <AtomToolsFramework/Document/AtomToolsDocumentNotificationBus.h>
|
||||
#include <AtomToolsFramework/Window/AtomToolsMainWindow.h>
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
#include <AtomToolsFramework/Document/AtomToolsDocumentMainWindow.h>
|
||||
|
||||
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
|
||||
#include <Viewport/MaterialViewportWidget.h>
|
||||
@@ -21,80 +19,36 @@ AZ_POP_DISABLE_WARNING
|
||||
|
||||
namespace MaterialEditor
|
||||
{
|
||||
/**
|
||||
* MaterialEditorWindow is the main class. Its responsibility is limited to initializing and connecting
|
||||
* its panels, managing selection of assets, and performing high-level actions like saving. It contains...
|
||||
* 1) MaterialBrowser - The user browses for Material (.material) assets.
|
||||
* 2) MaterialViewport - The user can see the selected Material applied to a model.
|
||||
* 3) MaterialPropertyInspector - The user edits the properties of the selected Material.
|
||||
*/
|
||||
//! MaterialEditorWindow is the main class. Its responsibility is limited to initializing and connecting
|
||||
//! its panels, managing selection of assets, and performing high-level actions like saving. It contains...
|
||||
//! 1) MaterialBrowser - The user browses for Material (.material) assets.
|
||||
//! 2) MaterialViewport - The user can see the selected Material applied to a model.
|
||||
//! 3) MaterialPropertyInspector - The user edits the properties of the selected Material.
|
||||
class MaterialEditorWindow
|
||||
: public AtomToolsFramework::AtomToolsMainWindow
|
||||
, private AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler
|
||||
: public AtomToolsFramework::AtomToolsDocumentMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(MaterialEditorWindow, AZ::SystemAllocator, 0);
|
||||
|
||||
using Base = AtomToolsFramework::AtomToolsMainWindow;
|
||||
using Base = AtomToolsFramework::AtomToolsDocumentMainWindow;
|
||||
|
||||
MaterialEditorWindow(QWidget* parent = 0);
|
||||
~MaterialEditorWindow();
|
||||
~MaterialEditorWindow() = default;
|
||||
|
||||
private:
|
||||
protected:
|
||||
void ResizeViewportRenderTarget(uint32_t width, uint32_t height) override;
|
||||
void LockViewportRenderTargetSize(uint32_t width, uint32_t height) override;
|
||||
void UnlockViewportRenderTargetSize() override;
|
||||
|
||||
// AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler overrides...
|
||||
void OnDocumentOpened(const AZ::Uuid& documentId) override;
|
||||
void OnDocumentClosed(const AZ::Uuid& documentId) override;
|
||||
void OnDocumentModified(const AZ::Uuid& documentId) override;
|
||||
void OnDocumentUndoStateChanged(const AZ::Uuid& documentId) override;
|
||||
void OnDocumentSaved(const AZ::Uuid& documentId) override;
|
||||
|
||||
void CreateMenu() override;
|
||||
void CreateTabBar() override;
|
||||
|
||||
QString GetDocumentPath(const AZ::Uuid& documentId) const;
|
||||
|
||||
void OpenTabContextMenu() override;
|
||||
bool GetCreateDocumentParams(AZStd::string& openPath, AZStd::string& savePath) override;
|
||||
bool GetOpenDocumentParams(AZStd::string& openPath) override;
|
||||
void OpenSettings() override;
|
||||
void OpenHelp() override;
|
||||
|
||||
void closeEvent(QCloseEvent* closeEvent) override;
|
||||
|
||||
MaterialViewportWidget* m_materialViewport = nullptr;
|
||||
MaterialEditorToolBar* m_toolBar = nullptr;
|
||||
|
||||
QMenu* m_menuFile = {};
|
||||
QAction* m_actionNew = {};
|
||||
QAction* m_actionOpen = {};
|
||||
QAction* m_actionOpenRecent = {};
|
||||
QAction* m_actionClose = {};
|
||||
QAction* m_actionCloseAll = {};
|
||||
QAction* m_actionCloseOthers = {};
|
||||
QAction* m_actionSave = {};
|
||||
QAction* m_actionSaveAsCopy = {};
|
||||
QAction* m_actionSaveAsChild = {};
|
||||
QAction* m_actionSaveAll = {};
|
||||
QAction* m_actionExit = {};
|
||||
|
||||
QMenu* m_menuEdit = {};
|
||||
QAction* m_actionUndo = {};
|
||||
QAction* m_actionRedo = {};
|
||||
QAction* m_actionSettings = {};
|
||||
|
||||
QMenu* m_menuView = {};
|
||||
QAction* m_actionAssetBrowser = {};
|
||||
QAction* m_actionInspector = {};
|
||||
QAction* m_actionConsole = {};
|
||||
QAction* m_actionPythonTerminal = {};
|
||||
QAction* m_actionPerfMonitor = {};
|
||||
QAction* m_actionViewportSettings = {};
|
||||
QAction* m_actionNextTab = {};
|
||||
QAction* m_actionPreviousTab = {};
|
||||
|
||||
QMenu* m_menuHelp = {};
|
||||
QAction* m_actionHelp = {};
|
||||
QAction* m_actionAbout = {};
|
||||
};
|
||||
} // namespace MaterialEditor
|
||||
|
||||
@@ -5,24 +5,15 @@
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Name/Name.h>
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
#include <AzQtComponents/Components/StyleManager.h>
|
||||
#include <AzQtComponents/Components/WindowDecorationWrapper.h>
|
||||
#include <AzToolsFramework/API/EditorPythonRunnerRequestsBus.h>
|
||||
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
|
||||
#include <AzToolsFramework/PythonTerminal/ScriptTermDialog.h>
|
||||
#include <AtomToolsFramework/Util/Util.h>
|
||||
#include <AtomToolsFramework/Window/AtomToolsMainWindowNotificationBus.h>
|
||||
|
||||
#include <Atom/Document/ShaderManagementConsoleDocumentRequestBus.h>
|
||||
#include <AtomToolsFramework/Document/AtomToolsDocumentRequestBus.h>
|
||||
#include <AtomToolsFramework/Document/AtomToolsDocumentSystemRequestBus.h>
|
||||
#include <AtomToolsFramework/Util/Util.h>
|
||||
#include <AzCore/Name/Name.h>
|
||||
#include <AzQtComponents/Components/WindowDecorationWrapper.h>
|
||||
#include <AzToolsFramework/PythonTerminal/ScriptTermDialog.h>
|
||||
#include <Window/ShaderManagementConsoleWindow.h>
|
||||
|
||||
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
|
||||
#include <QCloseEvent>
|
||||
#include <QFileDialog>
|
||||
#include <QHeaderView>
|
||||
#include <QStandardItemModel>
|
||||
#include <QTableView>
|
||||
@@ -32,7 +23,7 @@ AZ_POP_DISABLE_WARNING
|
||||
namespace ShaderManagementConsole
|
||||
{
|
||||
ShaderManagementConsoleWindow::ShaderManagementConsoleWindow(QWidget* parent /* = 0 */)
|
||||
: AtomToolsFramework::AtomToolsMainWindow(parent)
|
||||
: AtomToolsFramework::AtomToolsDocumentMainWindow(parent)
|
||||
{
|
||||
resize(1280, 1024);
|
||||
|
||||
@@ -50,9 +41,6 @@ namespace ShaderManagementConsole
|
||||
m_toolBar->setObjectName("ToolBar");
|
||||
addToolBar(m_toolBar);
|
||||
|
||||
CreateMenu();
|
||||
CreateTabBar();
|
||||
|
||||
AddDockWidget("Asset Browser", new ShaderManagementConsoleBrowserWidget, Qt::BottomDockWidgetArea, Qt::Vertical);
|
||||
AddDockWidget("Python Terminal", new AzToolsFramework::CScriptTermDialog, Qt::BottomDockWidgetArea, Qt::Horizontal);
|
||||
|
||||
@@ -61,356 +49,36 @@ namespace ShaderManagementConsole
|
||||
// Restore geometry and show the window
|
||||
mainWindowWrapper->showFromSettings();
|
||||
|
||||
AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler::BusConnect();
|
||||
// Disable unused actions
|
||||
m_actionNew->setVisible(false);
|
||||
m_actionNew->setEnabled(false);
|
||||
m_actionSaveAsChild->setVisible(false);
|
||||
m_actionSaveAsChild->setEnabled(false);
|
||||
|
||||
OnDocumentOpened(AZ::Uuid::CreateNull());
|
||||
}
|
||||
|
||||
ShaderManagementConsoleWindow::~ShaderManagementConsoleWindow()
|
||||
{
|
||||
AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
void ShaderManagementConsoleWindow::closeEvent(QCloseEvent* closeEvent)
|
||||
{
|
||||
bool didClose = true;
|
||||
AtomToolsFramework::AtomToolsDocumentSystemRequestBus::BroadcastResult(didClose, &AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::CloseAllDocuments);
|
||||
if (!didClose)
|
||||
{
|
||||
closeEvent->ignore();
|
||||
return;
|
||||
}
|
||||
|
||||
AtomToolsFramework::AtomToolsMainWindowNotificationBus::Broadcast(
|
||||
&AtomToolsFramework::AtomToolsMainWindowNotifications::OnMainWindowClosing);
|
||||
}
|
||||
|
||||
void ShaderManagementConsoleWindow::OnDocumentOpened(const AZ::Uuid& documentId)
|
||||
{
|
||||
bool isOpen = false;
|
||||
AtomToolsFramework::AtomToolsDocumentRequestBus::EventResult(isOpen, documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::IsOpen);
|
||||
bool isSavable = false;
|
||||
AtomToolsFramework::AtomToolsDocumentRequestBus::EventResult(isSavable, documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::IsSavable);
|
||||
bool isModified = false;
|
||||
AtomToolsFramework::AtomToolsDocumentRequestBus::EventResult(isModified, documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::IsModified);
|
||||
bool canUndo = false;
|
||||
AtomToolsFramework::AtomToolsDocumentRequestBus::EventResult(canUndo, documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::CanUndo);
|
||||
bool canRedo = false;
|
||||
AtomToolsFramework::AtomToolsDocumentRequestBus::EventResult(canRedo, documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::CanRedo);
|
||||
AZStd::string absolutePath;
|
||||
AtomToolsFramework::AtomToolsDocumentRequestBus::EventResult(absolutePath, documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::GetAbsolutePath);
|
||||
AZStd::string filename;
|
||||
AzFramework::StringFunc::Path::GetFullFileName(absolutePath.c_str(), filename);
|
||||
|
||||
// Update UI to display the new document
|
||||
if (!documentId.IsNull() && isOpen)
|
||||
{
|
||||
// Create a new tab for the document ID and assign it's label to the file name of the document.
|
||||
AddTabForDocumentId(documentId, filename, absolutePath, [this, documentId]{
|
||||
// The document tab contains a table view.
|
||||
auto contentWidget = new QTableView(centralWidget());
|
||||
contentWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
contentWidget->setModel(CreateDocumentContent(documentId));
|
||||
return contentWidget;
|
||||
});
|
||||
}
|
||||
|
||||
UpdateTabForDocumentId(documentId, filename, absolutePath, isModified);
|
||||
|
||||
const bool hasTabs = m_tabWidget->count() > 0;
|
||||
|
||||
// Update menu options
|
||||
m_actionOpen->setEnabled(true);
|
||||
m_actionOpenRecent->setEnabled(false);
|
||||
m_actionClose->setEnabled(hasTabs);
|
||||
m_actionCloseAll->setEnabled(hasTabs);
|
||||
m_actionCloseOthers->setEnabled(hasTabs);
|
||||
|
||||
m_actionSave->setEnabled(isOpen && isSavable);
|
||||
m_actionSaveAsCopy->setEnabled(isOpen && isSavable);
|
||||
m_actionSaveAll->setEnabled(hasTabs);
|
||||
|
||||
m_actionExit->setEnabled(true);
|
||||
|
||||
m_actionUndo->setEnabled(canUndo);
|
||||
m_actionRedo->setEnabled(canRedo);
|
||||
m_actionSettings->setEnabled(false);
|
||||
|
||||
m_actionAssetBrowser->setEnabled(true);
|
||||
m_actionPythonTerminal->setEnabled(true);
|
||||
m_actionPreviousTab->setEnabled(m_tabWidget->count() > 1);
|
||||
m_actionNextTab->setEnabled(m_tabWidget->count() > 1);
|
||||
|
||||
m_actionHelp->setEnabled(false);
|
||||
m_actionAbout->setEnabled(false);
|
||||
|
||||
activateWindow();
|
||||
raise();
|
||||
|
||||
const QString documentPath = GetDocumentPath(documentId);
|
||||
if (!documentPath.isEmpty())
|
||||
{
|
||||
SetStatusMessage(tr("Document opened: %1").arg(documentPath));
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderManagementConsoleWindow::OnDocumentClosed(const AZ::Uuid& documentId)
|
||||
{
|
||||
RemoveTabForDocumentId(documentId);
|
||||
SetStatusMessage(tr("Document closed: %1").arg(GetDocumentPath(documentId)));
|
||||
}
|
||||
|
||||
void ShaderManagementConsoleWindow::OnDocumentModified(const AZ::Uuid& documentId)
|
||||
{
|
||||
bool isModified = false;
|
||||
AtomToolsFramework::AtomToolsDocumentRequestBus::EventResult(isModified, documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::IsModified);
|
||||
AZStd::string absolutePath;
|
||||
AtomToolsFramework::AtomToolsDocumentRequestBus::EventResult(absolutePath, documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::GetAbsolutePath);
|
||||
AZStd::string filename;
|
||||
AzFramework::StringFunc::Path::GetFullFileName(absolutePath.c_str(), filename);
|
||||
UpdateTabForDocumentId(documentId, filename, absolutePath, isModified);
|
||||
}
|
||||
|
||||
void ShaderManagementConsoleWindow::OnDocumentUndoStateChanged(const AZ::Uuid& documentId)
|
||||
{
|
||||
if (documentId == GetDocumentIdFromTab(m_tabWidget->currentIndex()))
|
||||
{
|
||||
bool canUndo = false;
|
||||
AtomToolsFramework::AtomToolsDocumentRequestBus::EventResult(canUndo, documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::CanUndo);
|
||||
bool canRedo = false;
|
||||
AtomToolsFramework::AtomToolsDocumentRequestBus::EventResult(canRedo, documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::CanRedo);
|
||||
m_actionUndo->setEnabled(canUndo);
|
||||
m_actionRedo->setEnabled(canRedo);
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderManagementConsoleWindow::OnDocumentSaved(const AZ::Uuid& documentId)
|
||||
{
|
||||
bool isModified = false;
|
||||
AtomToolsFramework::AtomToolsDocumentRequestBus::EventResult(isModified, documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::IsModified);
|
||||
AZStd::string absolutePath;
|
||||
AtomToolsFramework::AtomToolsDocumentRequestBus::EventResult(absolutePath, documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::GetAbsolutePath);
|
||||
AZStd::string filename;
|
||||
AzFramework::StringFunc::Path::GetFullFileName(absolutePath.c_str(), filename);
|
||||
UpdateTabForDocumentId(documentId, filename, absolutePath, isModified);
|
||||
SetStatusMessage(tr("Document saved: %1").arg(GetDocumentPath(documentId)));
|
||||
}
|
||||
|
||||
void ShaderManagementConsoleWindow::CreateMenu()
|
||||
{
|
||||
Base::CreateMenu();
|
||||
|
||||
// Generating the main menu manually because it's easier and we will have some dynamic or data driven entries
|
||||
m_menuFile = menuBar()->addMenu("&File");
|
||||
|
||||
m_actionOpen = m_menuFile->addAction("&Open...", [this]() {
|
||||
const AZStd::vector<AZ::Data::AssetType> assetTypes = {
|
||||
};
|
||||
|
||||
const AZStd::string filePath = AtomToolsFramework::GetOpenFileInfo(assetTypes).absoluteFilePath().toUtf8().constData();
|
||||
if (!filePath.empty())
|
||||
{
|
||||
AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::OpenDocument, filePath);
|
||||
}
|
||||
}, QKeySequence::Open);
|
||||
|
||||
m_actionOpenRecent = m_menuFile->addAction("Open &Recent");
|
||||
|
||||
m_menuFile->addSeparator();
|
||||
|
||||
m_actionSave = m_menuFile->addAction("&Save", [this]() {
|
||||
const AZ::Uuid documentId = GetDocumentIdFromTab(m_tabWidget->currentIndex());
|
||||
bool result = false;
|
||||
AtomToolsFramework::AtomToolsDocumentSystemRequestBus::BroadcastResult(result, &AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::SaveDocument, documentId);
|
||||
if (!result)
|
||||
{
|
||||
SetStatusError(tr("Document save failed: %1").arg(GetDocumentPath(documentId)));
|
||||
}
|
||||
}, QKeySequence::Save);
|
||||
|
||||
m_actionSaveAsCopy = m_menuFile->addAction("Save &As...", [this]() {
|
||||
const AZ::Uuid documentId = GetDocumentIdFromTab(m_tabWidget->currentIndex());
|
||||
const QString documentPath = GetDocumentPath(documentId);
|
||||
|
||||
bool result = false;
|
||||
AtomToolsFramework::AtomToolsDocumentSystemRequestBus::BroadcastResult(result, &AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::SaveDocumentAsCopy,
|
||||
documentId, AtomToolsFramework::GetSaveFileInfo(documentPath).absoluteFilePath().toUtf8().constData());
|
||||
if (!result)
|
||||
{
|
||||
SetStatusError(tr("Document save failed: %1").arg(GetDocumentPath(documentId)));
|
||||
}
|
||||
}, QKeySequence::SaveAs);
|
||||
|
||||
m_actionSaveAll = m_menuFile->addAction("Save A&ll", [this]() {
|
||||
bool result = false;
|
||||
AtomToolsFramework::AtomToolsDocumentSystemRequestBus::BroadcastResult(result, &AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::SaveAllDocuments);
|
||||
if (!result)
|
||||
{
|
||||
SetStatusError(tr("Document save all failed"));
|
||||
}
|
||||
});
|
||||
|
||||
m_menuFile->addSeparator();
|
||||
|
||||
m_actionClose = m_menuFile->addAction("&Close", [this]() {
|
||||
const AZ::Uuid documentId = GetDocumentIdFromTab(m_tabWidget->currentIndex());
|
||||
AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::CloseDocument, documentId);
|
||||
}, QKeySequence::Close);
|
||||
|
||||
m_actionCloseAll = m_menuFile->addAction("Close All", [this]() {
|
||||
AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::CloseAllDocuments);
|
||||
});
|
||||
|
||||
m_actionCloseOthers = m_menuFile->addAction("Close Others", [this]() {
|
||||
const AZ::Uuid documentId = GetDocumentIdFromTab(m_tabWidget->currentIndex());
|
||||
AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::CloseAllDocumentsExcept, documentId);
|
||||
});
|
||||
|
||||
m_menuFile->addSeparator();
|
||||
|
||||
m_menuFile->addAction("Run &Python...", [this]() {
|
||||
const QString script = QFileDialog::getOpenFileName(this, "Run Script", QString(), QString("*.py"));
|
||||
if (!script.isEmpty())
|
||||
{
|
||||
AzToolsFramework::EditorPythonRunnerRequestBus::Broadcast(&AzToolsFramework::EditorPythonRunnerRequestBus::Events::ExecuteByFilename, script.toUtf8().constData());
|
||||
}
|
||||
});
|
||||
|
||||
m_menuFile->addSeparator();
|
||||
|
||||
m_actionExit = m_menuFile->addAction("E&xit", [this]() {
|
||||
close();
|
||||
}, QKeySequence::Quit);
|
||||
|
||||
m_menuEdit = menuBar()->addMenu("&Edit");
|
||||
|
||||
m_actionUndo = m_menuEdit->addAction("&Undo", [this]() {
|
||||
const AZ::Uuid documentId = GetDocumentIdFromTab(m_tabWidget->currentIndex());
|
||||
bool result = false;
|
||||
AtomToolsFramework::AtomToolsDocumentRequestBus::EventResult(result, documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::Undo);
|
||||
if (!result)
|
||||
{
|
||||
SetStatusError(tr("Document undo failed: %1").arg(GetDocumentPath(documentId)));
|
||||
}
|
||||
}, QKeySequence::Undo);
|
||||
|
||||
m_actionRedo = m_menuEdit->addAction("&Redo", [this]() {
|
||||
const AZ::Uuid documentId = GetDocumentIdFromTab(m_tabWidget->currentIndex());
|
||||
bool result = false;
|
||||
AtomToolsFramework::AtomToolsDocumentRequestBus::EventResult(result, documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::Redo);
|
||||
if (!result)
|
||||
{
|
||||
SetStatusError(tr("Document redo failed: %1").arg(GetDocumentPath(documentId)));
|
||||
}
|
||||
}, QKeySequence::Redo);
|
||||
|
||||
m_menuEdit->addSeparator();
|
||||
|
||||
m_actionSettings = m_menuEdit->addAction("&Settings...", [this]() {
|
||||
}, QKeySequence::Preferences);
|
||||
m_actionSettings->setEnabled(false);
|
||||
|
||||
m_menuView = menuBar()->addMenu("&View");
|
||||
|
||||
m_actionAssetBrowser = m_menuView->addAction("&Asset Browser", [this]() {
|
||||
const AZStd::string label = "Asset Browser";
|
||||
SetDockWidgetVisible(label, !IsDockWidgetVisible(label));
|
||||
});
|
||||
|
||||
m_actionPythonTerminal = m_menuView->addAction("Python &Terminal", [this]() {
|
||||
const AZStd::string label = "Python Terminal";
|
||||
SetDockWidgetVisible(label, !IsDockWidgetVisible(label));
|
||||
});
|
||||
|
||||
|
||||
m_menuView->addSeparator();
|
||||
|
||||
m_actionPreviousTab = m_menuView->addAction("&Previous Tab", [this]() {
|
||||
SelectPreviousTab();
|
||||
}, Qt::CTRL | Qt::SHIFT | Qt::Key_Tab); //QKeySequence::PreviousChild is mapped incorrectly in Qt
|
||||
|
||||
m_actionNextTab = m_menuView->addAction("&Next Tab", [this]() {
|
||||
SelectNextTab();
|
||||
}, Qt::CTRL | Qt::Key_Tab); //QKeySequence::NextChild works as expected but mirroring Previous
|
||||
|
||||
m_menuHelp = menuBar()->addMenu("&Help");
|
||||
|
||||
m_actionHelp = m_menuHelp->addAction("&Help...", [this]() {
|
||||
});
|
||||
|
||||
m_actionAbout = m_menuHelp->addAction("&About...", [this]() {
|
||||
});
|
||||
}
|
||||
|
||||
void ShaderManagementConsoleWindow::CreateTabBar()
|
||||
{
|
||||
Base::CreateTabBar();
|
||||
|
||||
// This signal will be triggered whenever a tab is added, removed, selected, clicked, dragged
|
||||
// When the last tab is removed tabIndex will be -1 and the document ID will be null
|
||||
// This should automatically clear the active document
|
||||
connect(m_tabWidget, &QTabWidget::currentChanged, this, [this](int tabIndex) {
|
||||
const AZ::Uuid documentId = GetDocumentIdFromTab(tabIndex);
|
||||
AtomToolsFramework::AtomToolsDocumentNotificationBus::Broadcast(&AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentOpened, documentId);
|
||||
});
|
||||
|
||||
connect(m_tabWidget, &QTabWidget::tabCloseRequested, this, [this](int tabIndex) {
|
||||
const AZ::Uuid documentId = GetDocumentIdFromTab(tabIndex);
|
||||
AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::CloseDocument, documentId);
|
||||
});
|
||||
}
|
||||
|
||||
QString ShaderManagementConsoleWindow::GetDocumentPath(const AZ::Uuid& documentId) const
|
||||
{
|
||||
AZStd::string absolutePath;
|
||||
AtomToolsFramework::AtomToolsDocumentRequestBus::EventResult(absolutePath, documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Handler::GetAbsolutePath);
|
||||
return absolutePath.c_str();
|
||||
}
|
||||
|
||||
void ShaderManagementConsoleWindow::OpenTabContextMenu()
|
||||
{
|
||||
const QTabBar* tabBar = m_tabWidget->tabBar();
|
||||
const QPoint position = tabBar->mapFromGlobal(QCursor::pos());
|
||||
const int clickedTabIndex = tabBar->tabAt(position);
|
||||
const int currentTabIndex = tabBar->currentIndex();
|
||||
if (clickedTabIndex >= 0)
|
||||
{
|
||||
QMenu tabMenu;
|
||||
const QString selectActionName = (currentTabIndex == clickedTabIndex) ? "Select in Browser" : "Select";
|
||||
tabMenu.addAction(selectActionName, [this, clickedTabIndex]() {
|
||||
const AZ::Uuid documentId = GetDocumentIdFromTab(clickedTabIndex);
|
||||
AtomToolsFramework::AtomToolsDocumentNotificationBus::Broadcast(&AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentOpened, documentId);
|
||||
});
|
||||
tabMenu.addAction("Close", [this, clickedTabIndex]() {
|
||||
const AZ::Uuid documentId = GetDocumentIdFromTab(clickedTabIndex);
|
||||
AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::CloseDocument, documentId);
|
||||
});
|
||||
auto closeOthersAction = tabMenu.addAction("Close Others", [this, clickedTabIndex]() {
|
||||
const AZ::Uuid documentId = GetDocumentIdFromTab(clickedTabIndex);
|
||||
AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::CloseAllDocumentsExcept, documentId);
|
||||
});
|
||||
closeOthersAction->setEnabled(tabBar->count() > 1);
|
||||
tabMenu.exec(QCursor::pos());
|
||||
}
|
||||
}
|
||||
|
||||
QStandardItemModel* ShaderManagementConsoleWindow::CreateDocumentContent(const AZ::Uuid& documentId)
|
||||
QWidget* ShaderManagementConsoleWindow::CreateDocumentTabView(const AZ::Uuid& documentId)
|
||||
{
|
||||
AZStd::unordered_set<AZStd::string> optionNames;
|
||||
|
||||
size_t shaderOptionCount = 0;
|
||||
ShaderManagementConsoleDocumentRequestBus::EventResult(shaderOptionCount, documentId, &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderOptionCount);
|
||||
ShaderManagementConsoleDocumentRequestBus::EventResult(
|
||||
shaderOptionCount, documentId, &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderOptionCount);
|
||||
|
||||
for (size_t optionIndex = 0; optionIndex < shaderOptionCount; ++optionIndex)
|
||||
{
|
||||
AZ::RPI::ShaderOptionDescriptor shaderOptionDesc;
|
||||
ShaderManagementConsoleDocumentRequestBus::EventResult(shaderOptionDesc, documentId, &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderOptionDescriptor, optionIndex);
|
||||
ShaderManagementConsoleDocumentRequestBus::EventResult(
|
||||
shaderOptionDesc, documentId, &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderOptionDescriptor, optionIndex);
|
||||
|
||||
const char* optionName = shaderOptionDesc.GetName().GetCStr();
|
||||
optionNames.insert(optionName);
|
||||
}
|
||||
|
||||
size_t shaderVariantCount = 0;
|
||||
ShaderManagementConsoleDocumentRequestBus::EventResult(shaderVariantCount, documentId, &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderVariantCount);
|
||||
ShaderManagementConsoleDocumentRequestBus::EventResult(
|
||||
shaderVariantCount, documentId, &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderVariantCount);
|
||||
|
||||
auto model = new QStandardItemModel();
|
||||
model->setRowCount(static_cast<int>(shaderVariantCount));
|
||||
@@ -425,7 +93,8 @@ namespace ShaderManagementConsole
|
||||
for (int variantIndex = 0; variantIndex < shaderVariantCount; ++variantIndex)
|
||||
{
|
||||
AZ::RPI::ShaderVariantListSourceData::VariantInfo shaderVariantInfo;
|
||||
ShaderManagementConsoleDocumentRequestBus::EventResult(shaderVariantInfo, documentId, &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderVariantInfo, variantIndex);
|
||||
ShaderManagementConsoleDocumentRequestBus::EventResult(
|
||||
shaderVariantInfo, documentId, &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderVariantInfo, variantIndex);
|
||||
|
||||
model->setHeaderData(variantIndex, Qt::Vertical, QString::number(variantIndex));
|
||||
|
||||
@@ -442,7 +111,11 @@ namespace ShaderManagementConsole
|
||||
}
|
||||
}
|
||||
|
||||
return model;
|
||||
// The document tab contains a table view.
|
||||
auto contentWidget = new QTableView(centralWidget());
|
||||
contentWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
contentWidget->setModel(model);
|
||||
return contentWidget;
|
||||
}
|
||||
} // namespace ShaderManagementConsole
|
||||
|
||||
|
||||
@@ -11,9 +11,7 @@
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <Atom/RPI.Edit/Shader/ShaderVariantListSourceData.h>
|
||||
#include <Atom/RPI.Public/Shader/Shader.h>
|
||||
#include <AtomToolsFramework/Document/AtomToolsDocumentNotificationBus.h>
|
||||
#include <AtomToolsFramework/Window/AtomToolsMainWindow.h>
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
#include <AtomToolsFramework/Document/AtomToolsDocumentMainWindow.h>
|
||||
|
||||
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
|
||||
#include <Window/ShaderManagementConsoleBrowserWidget.h>
|
||||
@@ -25,69 +23,23 @@ AZ_POP_DISABLE_WARNING
|
||||
|
||||
namespace ShaderManagementConsole
|
||||
{
|
||||
/**
|
||||
* ShaderManagementConsoleWindow is the main class. Its responsibility is limited to initializing and connecting
|
||||
* its panels, managing selection of assets, and performing high-level actions like saving. It contains...
|
||||
*/
|
||||
//! ShaderManagementConsoleWindow is the main class. Its responsibility is limited to initializing and connecting
|
||||
//! its panels, managing selection of assets, and performing high-level actions like saving. It contains...
|
||||
class ShaderManagementConsoleWindow
|
||||
: public AtomToolsFramework::AtomToolsMainWindow
|
||||
, private AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler
|
||||
: public AtomToolsFramework::AtomToolsDocumentMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(ShaderManagementConsoleWindow, AZ::SystemAllocator, 0);
|
||||
|
||||
using Base = AtomToolsFramework::AtomToolsMainWindow;
|
||||
using Base = AtomToolsFramework::AtomToolsDocumentMainWindow;
|
||||
|
||||
ShaderManagementConsoleWindow(QWidget* parent = 0);
|
||||
~ShaderManagementConsoleWindow();
|
||||
~ShaderManagementConsoleWindow() = default;
|
||||
|
||||
private:
|
||||
// AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler overrides...
|
||||
void OnDocumentOpened(const AZ::Uuid& documentId) override;
|
||||
void OnDocumentClosed(const AZ::Uuid& documentId) override;
|
||||
void OnDocumentModified(const AZ::Uuid& documentId) override;
|
||||
void OnDocumentUndoStateChanged(const AZ::Uuid& documentId) override;
|
||||
void OnDocumentSaved(const AZ::Uuid& documentId) override;
|
||||
|
||||
void CreateMenu() override;
|
||||
void CreateTabBar() override;
|
||||
|
||||
QString GetDocumentPath(const AZ::Uuid& documentId) const;
|
||||
|
||||
void OpenTabContextMenu() override;
|
||||
|
||||
void closeEvent(QCloseEvent* closeEvent) override;
|
||||
|
||||
QStandardItemModel* CreateDocumentContent(const AZ::Uuid& documentId);
|
||||
protected:
|
||||
QWidget* CreateDocumentTabView(const AZ::Uuid& documentId) override;
|
||||
|
||||
ShaderManagementConsoleToolBar* m_toolBar = nullptr;
|
||||
|
||||
QMenu* m_menuFile = {};
|
||||
QMenu* m_menuNew = {};
|
||||
QAction* m_actionOpen = {};
|
||||
QAction* m_actionOpenRecent = {};
|
||||
QAction* m_actionClose = {};
|
||||
QAction* m_actionCloseAll = {};
|
||||
QAction* m_actionCloseOthers = {};
|
||||
QAction* m_actionSave = {};
|
||||
QAction* m_actionSaveAsCopy = {};
|
||||
QAction* m_actionSaveAll = {};
|
||||
QAction* m_actionExit = {};
|
||||
|
||||
QMenu* m_menuEdit = {};
|
||||
QAction* m_actionUndo = {};
|
||||
QAction* m_actionRedo = {};
|
||||
QAction* m_actionSettings = {};
|
||||
|
||||
QMenu* m_menuView = {};
|
||||
QAction* m_actionAssetBrowser = {};
|
||||
QAction* m_actionPythonTerminal = {};
|
||||
QAction* m_actionNextTab = {};
|
||||
QAction* m_actionPreviousTab = {};
|
||||
|
||||
QMenu* m_menuHelp = {};
|
||||
QAction* m_actionHelp = {};
|
||||
QAction* m_actionAbout = {};
|
||||
};
|
||||
} // namespace ShaderManagementConsole
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace AZ
|
||||
};
|
||||
|
||||
// Update running statistics with new region data
|
||||
void RecordRegion(const AZ::RHI::CachedTimeRegion& region, AZStd::thread_id threadId);
|
||||
void RecordRegion(const AZ::RHI::CachedTimeRegion& region, size_t threadId);
|
||||
|
||||
void ResetPerFrameStatistics();
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace AZ
|
||||
u64 m_invocationsLastFrame = 0;
|
||||
|
||||
// NOTE: set over unordered_set so the threads can be shown in increasing order in tooltip.
|
||||
AZStd::set<AZStd::thread_id> m_executingThreads;
|
||||
AZStd::set<size_t> m_executingThreads;
|
||||
|
||||
AZStd::sys_time_t m_lastFrameTotalTicks = 0;
|
||||
|
||||
@@ -95,7 +95,7 @@ namespace AZ
|
||||
void Draw(bool& keepDrawing, const AZ::RHI::CpuTimingStatistics& cpuTimingStatistics);
|
||||
|
||||
private:
|
||||
static constexpr float RowHeight = 50.0;
|
||||
static constexpr float RowHeight = 35.0;
|
||||
static constexpr int DefaultFramesToCollect = 50;
|
||||
static constexpr float MediumFrameTimeLimit = 16.6; // 60 fps
|
||||
static constexpr float HighFrameTimeLimit = 33.3; // 30 fps
|
||||
@@ -134,7 +134,7 @@ namespace AZ
|
||||
void DrawThreadSeparator(u64 threadBoundary, u64 maxDepth);
|
||||
|
||||
// Draw the "Thread XXXXX" label onto the viewport
|
||||
void DrawThreadLabel(u64 baseRow, AZStd::thread_id threadId);
|
||||
void DrawThreadLabel(u64 baseRow, size_t threadId);
|
||||
|
||||
// Draw the vertical lines separating frames in the timeline
|
||||
void DrawFrameBoundaries();
|
||||
@@ -169,7 +169,9 @@ namespace AZ
|
||||
AZStd::sys_time_t m_viewportEndTick;
|
||||
|
||||
// Map to store each thread's TimeRegions, individual vectors are sorted by start tick
|
||||
AZStd::unordered_map<AZStd::thread_id, AZStd::vector<TimeRegion>> m_savedData;
|
||||
// note: we use size_t as a proxy for thread_id because native_thread_id_type differs differs from
|
||||
// platform to platform, which causes problems when deserializing saved captures.
|
||||
AZStd::unordered_map<size_t, AZStd::vector<TimeRegion>> m_savedData;
|
||||
|
||||
// Region color cache
|
||||
AZStd::unordered_map<const GroupRegionName*, ImVec4> m_regionColorMap;
|
||||
@@ -213,6 +215,11 @@ namespace AZ
|
||||
|
||||
// Index into the file picker, used to determine which file to load when "Load File" is pressed.
|
||||
int m_currentFileIndex = 0;
|
||||
|
||||
|
||||
// --- Loading capture state ---
|
||||
AZStd::unordered_set<AZStd::string> m_deserializedStringPool;
|
||||
AZStd::unordered_set<RHI::CachedTimeRegion::GroupRegionName, RHI::CachedTimeRegion::GroupRegionName::Hash> m_deserializedGroupRegionNamePool;
|
||||
};
|
||||
} // namespace Render
|
||||
} // namespace AZ
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <Atom/RPI.Edit/Common/JsonUtils.h>
|
||||
#include <Atom/RPI.Public/RPISystemInterface.h>
|
||||
|
||||
#include <AzCore/Casting/lossy_cast.h>
|
||||
#include <AzCore/IO/FileIO.h>
|
||||
#include <AzCore/JSON/filereadstream.h>
|
||||
#include <AzCore/Serialization/Json/JsonSerialization.h>
|
||||
@@ -30,19 +31,6 @@ namespace AZ
|
||||
{
|
||||
namespace CpuProfilerImGuiHelper
|
||||
{
|
||||
// NOTE: Fix build error in case AZStd::thread_id is not of an arithmetic type, and instead a pointer
|
||||
template<typename ThreadId, typename AZStd::enable_if<AZStd::is_pointer<ThreadId>::value>::type* = nullptr>
|
||||
AZStd::string TextThreadId(ThreadId threadId)
|
||||
{
|
||||
return AZStd::string::format("Thread: %p", threadId);
|
||||
}
|
||||
|
||||
template<typename ThreadId, typename AZStd::enable_if<!AZStd::is_pointer<ThreadId>::value>::type* = nullptr>
|
||||
AZStd::string TextThreadId(ThreadId threadId)
|
||||
{
|
||||
return AZStd::string::format("Thread: %zu", static_cast<size_t>(threadId));
|
||||
}
|
||||
|
||||
inline float TicksToMs(AZStd::sys_time_t ticks)
|
||||
{
|
||||
// Note: converting to microseconds integer before converting to milliseconds float
|
||||
@@ -231,6 +219,7 @@ namespace AZ
|
||||
m_lastCapturedFilePath = resolvedPath;
|
||||
AZ::Render::ProfilingCaptureRequestBus::Broadcast(
|
||||
&AZ::Render::ProfilingCaptureRequestBus::Events::EndContinuousCpuProfilingCapture, frameDataFilePath);
|
||||
m_paused = true;
|
||||
}
|
||||
|
||||
else
|
||||
@@ -447,13 +436,65 @@ namespace AZ
|
||||
inline void ImGuiCpuProfiler::LoadFile()
|
||||
{
|
||||
const IO::Path& pathToLoad = m_cachedCapturePaths[m_currentFileIndex];
|
||||
auto res = CpuProfilerImGuiHelper::LoadSavedCpuProfilingStatistics(pathToLoad.String());
|
||||
if (!res.IsSuccess())
|
||||
auto loadResult = CpuProfilerImGuiHelper::LoadSavedCpuProfilingStatistics(pathToLoad.String());
|
||||
if (!loadResult.IsSuccess())
|
||||
{
|
||||
AZ_TracePrintf("ImGuiCpuProfiler", "%s", res.GetError().c_str());
|
||||
AZ_TracePrintf("ImGuiCpuProfiler", "%s", loadResult.GetError().c_str());
|
||||
return;
|
||||
}
|
||||
// TODO ATOM-16022 Parse this data and display it in the visualizer widget.
|
||||
|
||||
AZStd::vector<RHI::CpuProfilingStatisticsSerializer::CpuProfilingStatisticsSerializerEntry> deserializedData = loadResult.TakeValue();
|
||||
|
||||
// Clear visualizer and statistics view state
|
||||
m_savedRegionCount = deserializedData.size();
|
||||
m_savedData.clear();
|
||||
m_paused = true;
|
||||
AZ::RHI::CpuProfiler::Get()->SetProfilerEnabled(false);
|
||||
m_frameEndTicks.clear();
|
||||
|
||||
m_tableData.clear();
|
||||
m_groupRegionMap.clear();
|
||||
|
||||
for (const auto& entry : deserializedData)
|
||||
{
|
||||
const auto [groupNameItr, wasGroupNameInserted] = m_deserializedStringPool.emplace(entry.m_groupName.GetCStr());
|
||||
const auto [regionNameItr, wasRegionNameInserted] = m_deserializedStringPool.emplace(entry.m_regionName.GetCStr());
|
||||
const auto [groupRegionNameItr, wasGroupRegionNameInserted] =
|
||||
m_deserializedGroupRegionNamePool.emplace(groupNameItr->c_str(), regionNameItr->c_str());
|
||||
|
||||
const RHI::CachedTimeRegion newRegion(&(*groupRegionNameItr), entry.m_stackDepth, entry.m_startTick, entry.m_endTick);
|
||||
m_savedData[entry.m_threadId].push_back(newRegion);
|
||||
|
||||
// Since we don't serialize the frame boundaries, we need to use the RPI's OnSystemTick event as a heuristic.
|
||||
const static Name frameBoundaryName = Name("RPISystem: OnSystemTick");
|
||||
if (entry.m_regionName == frameBoundaryName)
|
||||
{
|
||||
m_frameEndTicks.push_back(entry.m_endTick);
|
||||
}
|
||||
|
||||
// Update running statistics
|
||||
if (!m_groupRegionMap[*groupNameItr].contains(*regionNameItr))
|
||||
{
|
||||
m_groupRegionMap[*groupNameItr][*regionNameItr].m_groupName = *groupNameItr;
|
||||
m_groupRegionMap[*groupNameItr][*regionNameItr].m_regionName = *regionNameItr;
|
||||
m_tableData.push_back(&m_groupRegionMap[*groupNameItr][*regionNameItr]);
|
||||
}
|
||||
m_groupRegionMap[*groupNameItr][*regionNameItr].RecordRegion(newRegion, entry.m_threadId);
|
||||
}
|
||||
|
||||
// Update viewport bounds with some added UX fudge factor
|
||||
m_viewportStartTick = deserializedData.back().m_startTick - 1000;
|
||||
m_viewportEndTick = deserializedData.back().m_endTick + 1000;
|
||||
|
||||
// Invariant: each vector in m_savedData must be sorted so that we can efficiently cull region data.
|
||||
for (auto& [threadId, singleThreadData] : m_savedData)
|
||||
{
|
||||
AZStd::sort(singleThreadData.begin(), singleThreadData.end(),
|
||||
[](const TimeRegion& lhs, const TimeRegion& rhs)
|
||||
{
|
||||
return lhs.m_startTick < rhs.m_startTick;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// -- CPU Visualizer --
|
||||
@@ -465,7 +506,7 @@ namespace AZ
|
||||
if (ImGui::BeginChild("Options and Statistics", { 0, 0 }, true))
|
||||
{
|
||||
ImGui::Columns(3, "Options", true);
|
||||
ImGui::SliderInt("Saved Frames", &m_framesToCollect, 10, 10000, "%d", ImGuiSliderFlags_AlwaysClamp | ImGuiSliderFlags_Logarithmic);
|
||||
ImGui::SliderInt("Saved Frames", &m_framesToCollect, 10, 20000, "%d", ImGuiSliderFlags_AlwaysClamp | ImGuiSliderFlags_Logarithmic);
|
||||
m_visualizerHighlightFilter.Draw("Find Region");
|
||||
|
||||
ImGui::NextColumn();
|
||||
@@ -631,6 +672,7 @@ namespace AZ
|
||||
// Iterate through the entire TimeRegionMap and copy the data since it will get deleted on the next frame
|
||||
for (const auto& [threadId, singleThreadRegionMap] : timeRegionMap)
|
||||
{
|
||||
const size_t threadIdHashed = AZStd::hash<AZStd::thread_id>{}(threadId);
|
||||
// The profiler can sometime return threads without any profiling events when dropping threads, FIXME(ATOM-15949)
|
||||
if (singleThreadRegionMap.size() == 0)
|
||||
{
|
||||
@@ -656,7 +698,7 @@ namespace AZ
|
||||
m_tableData.push_back(&m_groupRegionMap[groupName][regionName]);
|
||||
}
|
||||
|
||||
m_groupRegionMap[groupName][regionName].RecordRegion(region, threadId);
|
||||
m_groupRegionMap[groupName][regionName].RecordRegion(region, threadIdHashed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -676,7 +718,7 @@ namespace AZ
|
||||
m_savedRegionCount += newVisualizerData.size();
|
||||
|
||||
// Move onto the end of the current thread's saved data, sorted order maintained
|
||||
AZStd::vector<TimeRegion>& savedDataVec = m_savedData[threadId];
|
||||
AZStd::vector<TimeRegion>& savedDataVec = m_savedData[threadIdHashed];
|
||||
savedDataVec.insert(
|
||||
savedDataVec.end(), AZStd::make_move_iterator(newVisualizerData.begin()), AZStd::make_move_iterator(newVisualizerData.end()));
|
||||
}
|
||||
@@ -696,6 +738,12 @@ namespace AZ
|
||||
{
|
||||
AZStd::size_t sizeBeforeRemove = savedRegions.size();
|
||||
|
||||
// Early out to avoid the linear erase_if call
|
||||
if (savedRegions.size() >= 1 && savedRegions.at(0).m_startTick > deleteBeforeTick)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Use erase_if over plain upper_bound + erase to avoid repeated shifts. erase requires a shift of all elements to the right
|
||||
// for each element that is erased, while erase_if squashes all removes into a single shift which significantly improves perf.
|
||||
AZStd::erase_if(
|
||||
@@ -732,12 +780,19 @@ namespace AZ
|
||||
const float startPixel = ConvertTickToPixelSpace(block.m_startTick, m_viewportStartTick, m_viewportEndTick);
|
||||
const float endPixel = ConvertTickToPixelSpace(block.m_endTick, m_viewportStartTick, m_viewportEndTick);
|
||||
|
||||
const ImVec2 startPoint = { startPixel, wy + targetRow * RowHeight };
|
||||
const ImVec2 endPoint = { endPixel, wy + targetRow * RowHeight + 40 };
|
||||
if (endPixel - startPixel < 0.5f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const ImVec2 startPoint = { startPixel, wy + targetRow * RowHeight + 1};
|
||||
const ImVec2 endPoint = { endPixel, wy + (targetRow + 1) * RowHeight };
|
||||
|
||||
const ImU32 blockColor = GetBlockColor(block);
|
||||
|
||||
drawList->AddRectFilled(startPoint, endPoint, blockColor, 0);
|
||||
drawList->AddLine(startPoint, { endPixel, startPoint.y }, IM_COL32_BLACK, 0.5f);
|
||||
drawList->AddLine({ startPixel, endPoint.y }, endPoint, IM_COL32_BLACK, 0.5f);
|
||||
|
||||
// Draw the region name if possible
|
||||
// If the block's current width is too small, we skip drawing the label.
|
||||
@@ -751,11 +806,13 @@ namespace AZ
|
||||
|
||||
if (regionPixelWidth < textWidth) // Not enough space in the block to draw the whole name, draw clipped text.
|
||||
{
|
||||
// clipRect appears to only clip when a character is fully outside of its bounds which can lead to overflow
|
||||
// for now subtract the width of a character
|
||||
const ImVec4 clipRect = { startPoint.x, startPoint.y, endPoint.x - maxCharWidth, endPoint.y };
|
||||
const float fontSize = ImGui::GetFont()->FontSize;
|
||||
|
||||
// NOTE: RenderText calls do not automatically account for the global scale (which is modified at high DPI)
|
||||
// so we must adjust for the scale manually.
|
||||
const float scaleFactor = ImGui::GetIO().FontGlobalScale;
|
||||
const float fontSize = ImGui::GetFont()->FontSize * scaleFactor;
|
||||
|
||||
ImGui::GetFont()->RenderText(drawList, fontSize, startPoint, IM_COL32_WHITE, clipRect, label.c_str(), 0);
|
||||
}
|
||||
else // We have enough space to draw the entire label, draw and center text.
|
||||
@@ -815,18 +872,18 @@ namespace AZ
|
||||
auto [wx, wy] = ImGui::GetWindowPos();
|
||||
wy -= ImGui::GetScrollY();
|
||||
const float windowWidth = ImGui::GetWindowWidth();
|
||||
const float boundaryY = wy + (baseRow + maxDepth + 1) * RowHeight - 5;
|
||||
const float boundaryY = wy + (baseRow + maxDepth + 1) * RowHeight;
|
||||
|
||||
ImGui::GetWindowDrawList()->AddLine({ wx, boundaryY }, { wx + windowWidth, boundaryY }, red, 2.0f);
|
||||
ImGui::GetWindowDrawList()->AddLine({ wx, boundaryY }, { wx + windowWidth, boundaryY }, red, 1.0f);
|
||||
}
|
||||
|
||||
inline void ImGuiCpuProfiler::DrawThreadLabel(u64 baseRow, AZStd::thread_id threadId)
|
||||
inline void ImGuiCpuProfiler::DrawThreadLabel(u64 baseRow, size_t threadId)
|
||||
{
|
||||
auto [wx, wy] = ImGui::GetWindowPos();
|
||||
wy -= ImGui::GetScrollY();
|
||||
const AZStd::string threadIdText = CpuProfilerImGuiHelper::TextThreadId(threadId.m_id);
|
||||
const AZStd::string threadIdText = AZStd::string::format("Thread: %zu", threadId);
|
||||
|
||||
ImGui::GetWindowDrawList()->AddText({ wx + 10, wy + baseRow * RowHeight + 5 }, IM_COL32_WHITE, threadIdText.c_str());
|
||||
ImGui::GetWindowDrawList()->AddText({ wx + 10, wy + baseRow * RowHeight}, IM_COL32_WHITE, threadIdText.c_str());
|
||||
}
|
||||
|
||||
inline void ImGuiCpuProfiler::DrawFrameBoundaries()
|
||||
@@ -884,8 +941,10 @@ namespace AZ
|
||||
const float textBeginPixel = lastFrameBoundaryPixel + offset;
|
||||
const float textEndPixel = textBeginPixel + labelWidth;
|
||||
|
||||
const float verticalOffset = (ImGui::GetWindowHeight() - ImGui::GetFontSize()) / 2;
|
||||
|
||||
// Execution time label
|
||||
drawList->AddText({ textBeginPixel, wy + ImGui::GetWindowHeight() / 4 }, IM_COL32_WHITE, label.c_str());
|
||||
drawList->AddText({ textBeginPixel, wy + verticalOffset }, IM_COL32_WHITE, label.c_str());
|
||||
|
||||
// Left side
|
||||
drawList->AddLine(
|
||||
@@ -1043,7 +1102,7 @@ namespace AZ
|
||||
|
||||
// ---- TableRow impl ----
|
||||
|
||||
inline void TableRow::RecordRegion(const AZ::RHI::CachedTimeRegion& region, AZStd::thread_id threadId)
|
||||
inline void TableRow::RecordRegion(const AZ::RHI::CachedTimeRegion& region, size_t threadId)
|
||||
{
|
||||
const AZStd::sys_time_t deltaTime = region.m_endTick - region.m_startTick;
|
||||
|
||||
@@ -1072,7 +1131,7 @@ namespace AZ
|
||||
auto threadString = AZStd::string::format("Executed in %zu threads\n", m_executingThreads.size());
|
||||
for (const auto& threadId : m_executingThreads)
|
||||
{
|
||||
threadString.append(CpuProfilerImGuiHelper::TextThreadId(threadId.m_id) + "\n");
|
||||
threadString.append(AZStd::string::format("Thread: %zu\n", threadId));
|
||||
}
|
||||
return threadString;
|
||||
}
|
||||
|
||||
@@ -36,9 +36,18 @@ namespace AZ
|
||||
virtual void SetSortKey(RHI::DrawItemSortKey sortKey) = 0;
|
||||
virtual RHI::DrawItemSortKey GetSortKey() const = 0;
|
||||
|
||||
virtual void SetLodType(RPI::Cullable::LodType lodType) = 0;
|
||||
virtual RPI::Cullable::LodType GetLodType() const = 0;
|
||||
|
||||
virtual void SetLodOverride(RPI::Cullable::LodOverride lodOverride) = 0;
|
||||
virtual RPI::Cullable::LodOverride GetLodOverride() const = 0;
|
||||
|
||||
virtual void SetMinimumScreenCoverage(float minimumScreenCoverage) = 0;
|
||||
virtual float GetMinimumScreenCoverage() const = 0;
|
||||
|
||||
virtual void SetQualityDecayRate(float qualityDecayRate) = 0;
|
||||
virtual float GetQualityDecayRate() const = 0;
|
||||
|
||||
virtual void SetVisibility(bool visible) = 0;
|
||||
virtual bool GetVisibility() const = 0;
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <Material/MaterialComponentController.h>
|
||||
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
|
||||
#include <Atom/RPI.Reflect/Asset/AssetUtils.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AtomCore/Instance/InstanceDatabase.h>
|
||||
|
||||
@@ -133,7 +134,7 @@ namespace AZ
|
||||
{
|
||||
InitializeMaterialInstance(asset);
|
||||
}
|
||||
|
||||
|
||||
void MaterialComponentController::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
|
||||
{
|
||||
AZStd::unordered_set<MaterialAssignmentId> propertyOverrides;
|
||||
@@ -208,6 +209,7 @@ namespace AZ
|
||||
for (auto& materialPair : m_configuration.m_materials)
|
||||
{
|
||||
auto& materialAsset = materialPair.second.m_materialAsset;
|
||||
|
||||
if (materialAsset.GetId().IsValid() && !Data::AssetBus::MultiHandler::BusIsConnectedId(materialAsset.GetId()))
|
||||
{
|
||||
anyQueued = true;
|
||||
@@ -221,7 +223,7 @@ namespace AZ
|
||||
ReleaseMaterials();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MaterialComponentController::InitializeMaterialInstance(const Data::Asset<Data::AssetData>& asset)
|
||||
{
|
||||
bool allReady = true;
|
||||
|
||||
@@ -92,11 +92,11 @@ namespace AZ
|
||||
|
||||
AZ_DISABLE_COPY(MaterialComponentController);
|
||||
|
||||
//! Data::AssetBus interface
|
||||
//! Data::AssetBus overrides...
|
||||
void OnAssetReady(Data::Asset<Data::AssetData> asset) override;
|
||||
void OnAssetReloaded(Data::Asset<Data::AssetData> asset) override;
|
||||
|
||||
//! AZ::TickBus interface implementation
|
||||
// AZ::TickBus overrides...
|
||||
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
|
||||
|
||||
void LoadMaterials();
|
||||
|
||||
@@ -71,16 +71,36 @@ namespace AZ
|
||||
"MeshComponentConfig", "")
|
||||
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &MeshComponentConfig::m_modelAsset, "Mesh Asset", "Mesh asset reference")
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &MeshComponentConfig::m_sortKey, "Sort Key", "Transparent meshes are drawn by sort key then depth. Used this to force certain transparent meshes to draw before or after others.")
|
||||
->DataElement(AZ::Edit::UIHandlers::ComboBox, &MeshComponentConfig::m_lodOverride, "Lod Override", "Allows the rendered LOD to be overridden instead of being calculated automatically.")
|
||||
->Attribute(AZ::Edit::Attributes::EnumValues, &MeshComponentConfig::GetLodOverrideValues)
|
||||
->Attribute(AZ::Edit::Attributes::Visibility, &MeshComponentConfig::IsAssetSet)
|
||||
->DataElement(AZ::Edit::UIHandlers::CheckBox, &MeshComponentConfig::m_excludeFromReflectionCubeMaps, "Exclude from reflection cubemaps", "Mesh will not be visible in baked reflection probe cubemaps")
|
||||
->Attribute(AZ::Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly)
|
||||
->DataElement(AZ::Edit::UIHandlers::CheckBox, &MeshComponentConfig::m_useForwardPassIblSpecular, "Use Forward Pass IBL Specular",
|
||||
"Renders IBL specular reflections in the forward pass, using only the most influential probe (based on the position of the entity) and the global IBL cubemap. Can reduce rendering costs, but only recommended for static objects that are affected by at most one reflection probe.")
|
||||
->Attribute(AZ::Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly)
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &MeshComponentConfig::m_modelAsset, "Mesh Asset", "Mesh asset reference")
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &MeshComponentConfig::m_sortKey, "Sort Key", "Transparent meshes are drawn by sort key then depth. Used this to force certain transparent meshes to draw before or after others.")
|
||||
->Attribute(AZ::Edit::Attributes::Visibility, &MeshComponentConfig::IsAssetSet)
|
||||
->DataElement(AZ::Edit::UIHandlers::CheckBox, &MeshComponentConfig::m_excludeFromReflectionCubeMaps, "Exclude from reflection cubemaps", "Mesh will not be visible in baked reflection probe cubemaps")
|
||||
->Attribute(AZ::Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly)
|
||||
->DataElement(AZ::Edit::UIHandlers::CheckBox, &MeshComponentConfig::m_useForwardPassIblSpecular, "Use Forward Pass IBL Specular",
|
||||
"Renders IBL specular reflections in the forward pass, using only the most influential probe (based on the position of the entity) and the global IBL cubemap. Can reduce rendering costs, but only recommended for static objects that are affected by at most one reflection probe.")
|
||||
->Attribute(AZ::Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly)
|
||||
->DataElement(AZ::Edit::UIHandlers::ComboBox, &MeshComponentConfig::m_lodType, "Lod Type", "Lod Method.")
|
||||
->EnumAttribute(RPI::Cullable::LodType::Default, "Default")
|
||||
->EnumAttribute(RPI::Cullable::LodType::ScreenCoverage, "Screen Coverage")
|
||||
->EnumAttribute(RPI::Cullable::LodType::SpecificLod, "Specific Lod")
|
||||
->Attribute(AZ::Edit::Attributes::Visibility, &MeshComponentConfig::IsAssetSet)
|
||||
->Attribute(AZ::Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::EntireTree)
|
||||
->ClassElement(AZ::Edit::ClassElements::Group, "Lod Configuration")
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, false)
|
||||
->Attribute(AZ::Edit::Attributes::Visibility, &MeshComponentConfig::ShowLodConfig)
|
||||
->DataElement(AZ::Edit::UIHandlers::ComboBox, &MeshComponentConfig::m_lodOverride, "Lod Override", "Allows the rendered LOD to be overridden instead of being calculated automatically.")
|
||||
->Attribute(AZ::Edit::Attributes::EnumValues, &MeshComponentConfig::GetLodOverrideValues)
|
||||
->Attribute(AZ::Edit::Attributes::Visibility, &MeshComponentConfig::LodTypeIsSpecificLOD)
|
||||
->DataElement(AZ::Edit::UIHandlers::Slider, &MeshComponentConfig::m_minimumScreenCoverage, "Minimum Screen Coverage", "Minimum proportion of screen area an entitiy takes up, after that the entitiy is culled.")
|
||||
->Attribute(AZ::Edit::Attributes::Min, 0.f)
|
||||
->Attribute(AZ::Edit::Attributes::Max, 1.f)
|
||||
->Attribute(AZ::Edit::Attributes::Suffix, " percent")
|
||||
->Attribute(AZ::Edit::Attributes::Visibility, &MeshComponentConfig::LodTypeIsScreenCoverage)
|
||||
->DataElement(AZ::Edit::UIHandlers::Slider, &MeshComponentConfig::m_qualityDecayRate, "Quality Decay Rate",
|
||||
"Rate at which mesh quality decays (0 -> always stay highest quality, 1 -> quality falls off to lowest quality immediately).")
|
||||
->Attribute(AZ::Edit::Attributes::Min, 0.f)
|
||||
->Attribute(AZ::Edit::Attributes::Max, 1.f)
|
||||
->Attribute(AZ::Edit::Attributes::Visibility, &MeshComponentConfig::LodTypeIsScreenCoverage)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,18 +31,40 @@ namespace AZ
|
||||
{
|
||||
namespace Render
|
||||
{
|
||||
|
||||
namespace MeshComponentControllerVersionUtility
|
||||
{
|
||||
bool VersionConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement)
|
||||
{
|
||||
if (classElement.GetVersion() < 2)
|
||||
{
|
||||
RPI::Cullable::LodOverride lodOverride = aznumeric_cast<RPI::Cullable::LodOverride>(classElement.FindElement(AZ_CRC("LodOverride")));
|
||||
static constexpr uint8_t old_NoLodOverride = AZStd::numeric_limits <RPI::Cullable::LodOverride>::max();
|
||||
if (lodOverride == old_NoLodOverride)
|
||||
{
|
||||
classElement.AddElementWithData(context, "LodType", RPI::Cullable::LodType::SpecificLod);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // namespace MeshComponentControllerVersionUtility
|
||||
|
||||
void MeshComponentConfig::Reflect(ReflectContext* context)
|
||||
{
|
||||
if (auto* serializeContext = azrtti_cast<SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<MeshComponentConfig>()
|
||||
->Version(1)
|
||||
->Version(2, &MeshComponentControllerVersionUtility::VersionConverter)
|
||||
->Field("ModelAsset", &MeshComponentConfig::m_modelAsset)
|
||||
->Field("SortKey", &MeshComponentConfig::m_sortKey)
|
||||
->Field("LodOverride", &MeshComponentConfig::m_lodOverride)
|
||||
->Field("ExcludeFromReflectionCubeMaps", &MeshComponentConfig::m_excludeFromReflectionCubeMaps)
|
||||
->Field("UseForwardPassIBLSpecular", &MeshComponentConfig::m_useForwardPassIblSpecular);
|
||||
->Field("UseForwardPassIBLSpecular", &MeshComponentConfig::m_useForwardPassIblSpecular)
|
||||
->Field("LodType", &MeshComponentConfig::m_lodType)
|
||||
->Field("LodOverride", &MeshComponentConfig::m_lodOverride)
|
||||
->Field("MinimumScreenCoverage", &MeshComponentConfig::m_minimumScreenCoverage)
|
||||
->Field("QualityDecayRate", &MeshComponentConfig::m_qualityDecayRate);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool MeshComponentConfig::IsAssetSet()
|
||||
@@ -50,6 +72,21 @@ namespace AZ
|
||||
return m_modelAsset.GetId().IsValid();
|
||||
}
|
||||
|
||||
bool MeshComponentConfig::LodTypeIsScreenCoverage()
|
||||
{
|
||||
return m_lodType == RPI::Cullable::LodType::ScreenCoverage;
|
||||
}
|
||||
|
||||
bool MeshComponentConfig::LodTypeIsSpecificLOD()
|
||||
{
|
||||
return m_lodType == RPI::Cullable::LodType::SpecificLod;
|
||||
}
|
||||
|
||||
bool MeshComponentConfig::ShowLodConfig()
|
||||
{
|
||||
return LodTypeIsScreenCoverage() && LodTypeIsSpecificLOD();
|
||||
}
|
||||
|
||||
AZStd::vector<AZStd::pair<RPI::Cullable::LodOverride, AZStd::string>> MeshComponentConfig::GetLodOverrideValues()
|
||||
{
|
||||
AZStd::vector<AZStd::pair<RPI::Cullable::LodOverride, AZStd::string>> values;
|
||||
@@ -72,9 +109,9 @@ namespace AZ
|
||||
}
|
||||
|
||||
values.reserve(lodCount + 1);
|
||||
values.push_back({ RPI::Cullable::NoLodOverride, "Not Set" });
|
||||
values.push_back({ aznumeric_cast<RPI::Cullable::LodOverride>(0), "Default (Highest)" });
|
||||
|
||||
for (uint32_t i = 0; i < lodCount; ++i)
|
||||
for (uint32_t i = 1; i < lodCount; ++i)
|
||||
{
|
||||
AZStd::string enumDescription = AZStd::string::format("Lod %i", i);
|
||||
values.push_back({ aznumeric_cast<RPI::Cullable::LodOverride>(i), enumDescription.c_str() });
|
||||
@@ -102,7 +139,12 @@ namespace AZ
|
||||
|
||||
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
|
||||
{
|
||||
behaviorContext->ConstantProperty("NoLodOverride", BehaviorConstant(RPI::Cullable::NoLodOverride))
|
||||
behaviorContext->ConstantProperty("DefaultLodOverride", BehaviorConstant(0))
|
||||
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
|
||||
->Attribute(AZ::Script::Attributes::Category, "render")
|
||||
->Attribute(AZ::Script::Attributes::Module, "render");
|
||||
|
||||
behaviorContext->ConstantProperty("DefaultLodType", BehaviorConstant(RPI::Cullable::LodType::Default))
|
||||
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
|
||||
->Attribute(AZ::Script::Attributes::Category, "render")
|
||||
->Attribute(AZ::Script::Attributes::Module, "render");
|
||||
@@ -114,12 +156,21 @@ namespace AZ
|
||||
->Event("SetModelAssetPath", &MeshComponentRequestBus::Events::SetModelAssetPath)
|
||||
->Event("SetSortKey", &MeshComponentRequestBus::Events::SetSortKey)
|
||||
->Event("GetSortKey", &MeshComponentRequestBus::Events::GetSortKey)
|
||||
->Event("SetLodType", &MeshComponentRequestBus::Events::SetLodType)
|
||||
->Event("GetLodType", &MeshComponentRequestBus::Events::GetLodType)
|
||||
->Event("SetLodOverride", &MeshComponentRequestBus::Events::SetLodOverride)
|
||||
->Event("GetLodOverride", &MeshComponentRequestBus::Events::GetLodOverride)
|
||||
->Event("SetMinimumScreenCoverage", &MeshComponentRequestBus::Events::SetMinimumScreenCoverage)
|
||||
->Event("GetMinimumScreenCoverage", &MeshComponentRequestBus::Events::GetMinimumScreenCoverage)
|
||||
->Event("SetQualityDecayRate", &MeshComponentRequestBus::Events::SetQualityDecayRate)
|
||||
->Event("GetQualityDecayRate", &MeshComponentRequestBus::Events::GetQualityDecayRate)
|
||||
->VirtualProperty("ModelAssetId", "GetModelAssetId", "SetModelAssetId")
|
||||
->VirtualProperty("ModelAssetPath", "GetModelAssetPath", "SetModelAssetPath")
|
||||
->VirtualProperty("SortKey", "GetSortKey", "SetSortKey")
|
||||
->VirtualProperty("LodType", "GetLodType", "SetLodType")
|
||||
->VirtualProperty("LodOverride", "GetLodOverride", "SetLodOverride")
|
||||
->VirtualProperty("MinimumScreenCoverage", "GetMinimumScreenCoverage", "SetMinimumScreenCoverage")
|
||||
->VirtualProperty("QualityDecayRate", "GetQualityDecayRate", "SetQualityDecayRate")
|
||||
;
|
||||
}
|
||||
}
|
||||
@@ -343,7 +394,7 @@ namespace AZ
|
||||
|
||||
m_meshFeatureProcessor->SetTransform(m_meshHandle, transform, m_cachedNonUniformScale);
|
||||
m_meshFeatureProcessor->SetSortKey(m_meshHandle, m_configuration.m_sortKey);
|
||||
m_meshFeatureProcessor->SetLodOverride(m_meshHandle, m_configuration.m_lodOverride);
|
||||
m_meshFeatureProcessor->SetMeshLodConfiguration(m_meshHandle, GetMeshLodConfiguration());
|
||||
m_meshFeatureProcessor->SetExcludeFromReflectionCubeMaps(m_meshHandle, m_configuration.m_excludeFromReflectionCubeMaps);
|
||||
m_meshFeatureProcessor->SetVisible(m_meshHandle, m_isVisible);
|
||||
|
||||
@@ -434,15 +485,66 @@ namespace AZ
|
||||
return m_meshFeatureProcessor->GetSortKey(m_meshHandle);
|
||||
}
|
||||
|
||||
RPI::Cullable::LodConfiguration MeshComponentController::GetMeshLodConfiguration() const
|
||||
{
|
||||
return {
|
||||
m_configuration.m_lodType,
|
||||
m_configuration.m_lodOverride,
|
||||
m_configuration.m_minimumScreenCoverage,
|
||||
m_configuration.m_qualityDecayRate
|
||||
};
|
||||
}
|
||||
// -----------------------
|
||||
void MeshComponentController::SetLodType(RPI::Cullable::LodType lodType)
|
||||
{
|
||||
RPI::Cullable::LodConfiguration lodConfig = GetMeshLodConfiguration();
|
||||
lodConfig.m_lodType = lodType;
|
||||
m_meshFeatureProcessor->SetMeshLodConfiguration(m_meshHandle, lodConfig);
|
||||
}
|
||||
|
||||
RPI::Cullable::LodType MeshComponentController::GetLodType() const
|
||||
{
|
||||
RPI::Cullable::LodConfiguration lodConfig = m_meshFeatureProcessor->GetMeshLodConfiguration(m_meshHandle);
|
||||
return lodConfig.m_lodType;
|
||||
}
|
||||
|
||||
void MeshComponentController::SetLodOverride(RPI::Cullable::LodOverride lodOverride)
|
||||
{
|
||||
m_configuration.m_lodOverride = lodOverride; // Save for serialization
|
||||
m_meshFeatureProcessor->SetLodOverride(m_meshHandle, lodOverride);
|
||||
RPI::Cullable::LodConfiguration lodConfig = GetMeshLodConfiguration();
|
||||
lodConfig.m_lodOverride = lodOverride;
|
||||
m_meshFeatureProcessor->SetMeshLodConfiguration(m_meshHandle, lodConfig);
|
||||
}
|
||||
|
||||
RPI::Cullable::LodOverride MeshComponentController::GetLodOverride() const
|
||||
{
|
||||
return static_cast<RPI::Cullable::LodOverride>(m_meshFeatureProcessor->GetSortKey(m_meshHandle));
|
||||
RPI::Cullable::LodConfiguration lodConfig = m_meshFeatureProcessor->GetMeshLodConfiguration(m_meshHandle);
|
||||
return lodConfig.m_lodOverride;
|
||||
}
|
||||
|
||||
void MeshComponentController::SetMinimumScreenCoverage(float minimumScreenCoverage)
|
||||
{
|
||||
RPI::Cullable::LodConfiguration lodConfig = GetMeshLodConfiguration();
|
||||
lodConfig.m_minimumScreenCoverage = minimumScreenCoverage;
|
||||
m_meshFeatureProcessor->SetMeshLodConfiguration(m_meshHandle, lodConfig);
|
||||
}
|
||||
|
||||
float MeshComponentController::GetMinimumScreenCoverage() const
|
||||
{
|
||||
RPI::Cullable::LodConfiguration lodConfig = m_meshFeatureProcessor->GetMeshLodConfiguration(m_meshHandle);
|
||||
return lodConfig.m_minimumScreenCoverage;
|
||||
}
|
||||
|
||||
void MeshComponentController::SetQualityDecayRate(float qualityDecayRate)
|
||||
{
|
||||
RPI::Cullable::LodConfiguration lodConfig = GetMeshLodConfiguration();
|
||||
lodConfig.m_qualityDecayRate = qualityDecayRate;
|
||||
m_meshFeatureProcessor->SetMeshLodConfiguration(m_meshHandle, lodConfig);
|
||||
}
|
||||
|
||||
float MeshComponentController::GetQualityDecayRate() const
|
||||
{
|
||||
RPI::Cullable::LodConfiguration lodConfig = m_meshFeatureProcessor->GetMeshLodConfiguration(m_meshHandle);
|
||||
return lodConfig.m_qualityDecayRate;
|
||||
}
|
||||
|
||||
void MeshComponentController::SetVisibility(bool visible)
|
||||
|
||||
@@ -30,6 +30,9 @@ namespace AZ
|
||||
{
|
||||
namespace Render
|
||||
{
|
||||
|
||||
|
||||
|
||||
//! A configuration structure for the MeshComponentController
|
||||
class MeshComponentConfig final
|
||||
: public AZ::ComponentConfig
|
||||
@@ -41,13 +44,20 @@ namespace AZ
|
||||
|
||||
// Editor helper functions
|
||||
bool IsAssetSet();
|
||||
bool LodTypeIsScreenCoverage();
|
||||
bool LodTypeIsSpecificLOD();
|
||||
bool ShowLodConfig();
|
||||
AZStd::vector<AZStd::pair<RPI::Cullable::LodOverride, AZStd::string>> GetLodOverrideValues();
|
||||
|
||||
Data::Asset<RPI::ModelAsset> m_modelAsset = { AZ::Data::AssetLoadBehavior::QueueLoad };
|
||||
RHI::DrawItemSortKey m_sortKey = 0;
|
||||
RPI::Cullable::LodOverride m_lodOverride = RPI::Cullable::NoLodOverride;
|
||||
bool m_excludeFromReflectionCubeMaps = false;
|
||||
bool m_useForwardPassIblSpecular = false;
|
||||
|
||||
RPI::Cullable::LodType m_lodType = RPI::Cullable::LodType::Default;
|
||||
RPI::Cullable::LodOverride m_lodOverride = aznumeric_cast<RPI::Cullable::LodOverride>(0);
|
||||
float m_minimumScreenCoverage = 1.0f / 1080.0f;
|
||||
float m_qualityDecayRate = 0.5f;
|
||||
};
|
||||
|
||||
class MeshComponentController final
|
||||
@@ -94,8 +104,17 @@ namespace AZ
|
||||
void SetSortKey(RHI::DrawItemSortKey sortKey) override;
|
||||
RHI::DrawItemSortKey GetSortKey() const override;
|
||||
|
||||
void SetLodOverride(RPI::Cullable::LodOverride lodOverride) override;
|
||||
RPI::Cullable::LodOverride GetLodOverride() const override;
|
||||
void SetLodType(RPI::Cullable::LodType lodType) override;
|
||||
RPI::Cullable::LodType GetLodType() const override;
|
||||
|
||||
virtual void SetLodOverride(RPI::Cullable::LodOverride lodOverride);
|
||||
virtual RPI::Cullable::LodOverride GetLodOverride() const;
|
||||
|
||||
virtual void SetMinimumScreenCoverage(float minimumScreenCoverage);
|
||||
virtual float GetMinimumScreenCoverage() const;
|
||||
|
||||
virtual void SetQualityDecayRate(float qualityDecayRate);
|
||||
virtual float GetQualityDecayRate() const;
|
||||
|
||||
void SetVisibility(bool visible) override;
|
||||
bool GetVisibility() const override;
|
||||
@@ -132,6 +151,8 @@ namespace AZ
|
||||
void UnregisterModel();
|
||||
void RefreshModelRegistration();
|
||||
|
||||
RPI::Cullable::LodConfiguration GetMeshLodConfiguration() const;
|
||||
|
||||
void HandleNonUniformScaleChange(const AZ::Vector3& nonUniformScale);
|
||||
|
||||
Render::MeshFeatureProcessorInterface* m_meshFeatureProcessor = nullptr;
|
||||
|
||||
@@ -426,15 +426,53 @@ namespace AZ
|
||||
{
|
||||
return m_meshFeatureProcessor->GetSortKey(*m_meshHandle);
|
||||
}
|
||||
|
||||
void AtomActorInstance::SetLodType(RPI::Cullable::LodType lodType)
|
||||
{
|
||||
RPI::Cullable::LodConfiguration config = m_meshFeatureProcessor->GetMeshLodConfiguration(*m_meshHandle);
|
||||
config.m_lodType = lodType;
|
||||
m_meshFeatureProcessor->SetMeshLodConfiguration(*m_meshHandle, config);
|
||||
}
|
||||
|
||||
RPI::Cullable::LodType AtomActorInstance::GetLodType() const
|
||||
{
|
||||
return m_meshFeatureProcessor->GetMeshLodConfiguration(*m_meshHandle).m_lodType;
|
||||
}
|
||||
|
||||
void AtomActorInstance::SetLodOverride(RPI::Cullable::LodOverride lodOverride)
|
||||
{
|
||||
m_meshFeatureProcessor->SetLodOverride(*m_meshHandle, lodOverride);
|
||||
RPI::Cullable::LodConfiguration config = m_meshFeatureProcessor->GetMeshLodConfiguration(*m_meshHandle);
|
||||
config.m_lodOverride = lodOverride;
|
||||
m_meshFeatureProcessor->SetMeshLodConfiguration(*m_meshHandle, config);
|
||||
}
|
||||
|
||||
RPI::Cullable::LodOverride AtomActorInstance::GetLodOverride() const
|
||||
{
|
||||
return m_meshFeatureProcessor->GetLodOverride(*m_meshHandle);
|
||||
return m_meshFeatureProcessor->GetMeshLodConfiguration(*m_meshHandle).m_lodOverride;
|
||||
}
|
||||
|
||||
void AtomActorInstance::SetMinimumScreenCoverage(float minimumScreenCoverage)
|
||||
{
|
||||
RPI::Cullable::LodConfiguration config = m_meshFeatureProcessor->GetMeshLodConfiguration(*m_meshHandle);
|
||||
config.m_minimumScreenCoverage = minimumScreenCoverage;
|
||||
m_meshFeatureProcessor->SetMeshLodConfiguration(*m_meshHandle, config);
|
||||
}
|
||||
|
||||
float AtomActorInstance::GetMinimumScreenCoverage() const
|
||||
{
|
||||
return m_meshFeatureProcessor->GetMeshLodConfiguration(*m_meshHandle).m_minimumScreenCoverage;
|
||||
}
|
||||
|
||||
void AtomActorInstance::SetQualityDecayRate(float qualityDecayRate)
|
||||
{
|
||||
RPI::Cullable::LodConfiguration config = m_meshFeatureProcessor->GetMeshLodConfiguration(*m_meshHandle);
|
||||
config.m_qualityDecayRate = qualityDecayRate;
|
||||
m_meshFeatureProcessor->SetMeshLodConfiguration(*m_meshHandle, config);
|
||||
}
|
||||
|
||||
float AtomActorInstance::GetQualityDecayRate() const
|
||||
{
|
||||
return m_meshFeatureProcessor->GetMeshLodConfiguration(*m_meshHandle).m_qualityDecayRate;
|
||||
}
|
||||
|
||||
void AtomActorInstance::SetVisibility(bool visible)
|
||||
|
||||
@@ -141,8 +141,14 @@ namespace AZ
|
||||
AZ::Data::Instance<RPI::Model> GetModel() const override;
|
||||
void SetSortKey(RHI::DrawItemSortKey sortKey) override;
|
||||
RHI::DrawItemSortKey GetSortKey() const override;
|
||||
void SetLodType(RPI::Cullable::LodType lodType) override;
|
||||
RPI::Cullable::LodType GetLodType() const override;
|
||||
void SetLodOverride(RPI::Cullable::LodOverride lodOverride) override;
|
||||
RPI::Cullable::LodOverride GetLodOverride() const override;
|
||||
void SetMinimumScreenCoverage(float minimumScreenCoverage) override;
|
||||
float GetMinimumScreenCoverage() const override;
|
||||
void SetQualityDecayRate(float qualityDecayRate) override;
|
||||
float GetQualityDecayRate() const override;
|
||||
void SetVisibility(bool visible) override;
|
||||
bool GetVisibility() const override;
|
||||
// GetWorldBounds/GetLocalBounds already overridden by BoundsRequestBus::Handler
|
||||
|
||||
@@ -67,7 +67,7 @@ namespace ImGui
|
||||
// -- AzFramework::InputChannelEventListener and AzFramework::InputTextEventListener Interface ------------
|
||||
bool OnInputChannelEventFiltered(const AzFramework::InputChannel& inputChannel) override;
|
||||
bool OnInputTextEventFiltered(const AZStd::string& textUTF8) override;
|
||||
int GetPriority() const override { return AzFramework::InputChannelEventListener::GetPriorityDebug(); }
|
||||
int GetPriority() const override { return AzFramework::InputChannelEventListener::GetPriorityDebugUI(); }
|
||||
// -- AzFramework::InputChannelEventListener and AzFramework::InputTextEventListener Interface ------------
|
||||
|
||||
// AzFramework::WindowNotificationBus::Handler overrides...
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace LmbrCentral
|
||||
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/AudioProxy.svg")
|
||||
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
|
||||
->Attribute(AZ::Edit::Attributes::AddableByUser, true)
|
||||
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/audio-proxy/")
|
||||
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/audio/proxy/")
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace LmbrCentral
|
||||
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/AudioAreaEnvironment.svg")
|
||||
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
||||
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/audio-area-environment/")
|
||||
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/audio/area-environment/")
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &EditorAudioAreaEnvironmentComponent::m_broadPhaseTriggerArea,
|
||||
"Broad-phase trigger area", "The entity that contains a Trigger Area component for broad-phase checks")
|
||||
->Attribute(AZ::Edit::Attributes::RequiredService, AZ_CRC("ProximityTriggerService", 0x561f262c))
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace LmbrCentral
|
||||
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/AudioEnvironment.png")
|
||||
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
||||
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/audio-environment/")
|
||||
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/audio/environment/")
|
||||
->DataElement("AudioControl", &EditorAudioEnvironmentComponent::m_defaultEnvironment, "Default Environment", "Name of the default ATL Environment control to use")
|
||||
;
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace LmbrCentral
|
||||
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/AudioListener.svg")
|
||||
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
||||
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/audio-listener/")
|
||||
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/audio/listener/")
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &EditorAudioListenerComponent::m_rotationEntity,
|
||||
"Rotation Entity", "The Entity whose rotation the audio listener will adopt. If none set, will assume 'this' Entity")
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &EditorAudioListenerComponent::m_positionEntity,
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace LmbrCentral
|
||||
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/AudioMultiPosition.svg")
|
||||
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
||||
// Followup: Need Help URL
|
||||
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/audio/multi-position/")
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &EditorAudioMultiPositionComponent::m_entityRefs, "Entity References", "The entities from which positions will be obtained for multi-position audio")
|
||||
->DataElement(AZ::Edit::UIHandlers::ComboBox, &EditorAudioMultiPositionComponent::m_behaviorType, "Behavior Type", "Determines how multi-postion sounds are treated, Separate or Blended")
|
||||
;
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace LmbrCentral
|
||||
// Icon todo:
|
||||
//->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/AudioPreload.png")
|
||||
|
||||
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/audio-preload/")
|
||||
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/audio/preload/")
|
||||
|
||||
->DataElement("AudioControl", &EditorAudioPreloadComponent::m_defaultPreload, "Preload Name", "The default ATL Preload control to use")
|
||||
->DataElement(AZ::Edit::UIHandlers::ComboBox, &EditorAudioPreloadComponent::m_loadType, "Load Type", "Automatically when the component activates/deactivates, or Manually at user's request")
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace LmbrCentral
|
||||
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/AudioRtpc.svg")
|
||||
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
||||
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/audio-rtpc/")
|
||||
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/audio/rtpc/")
|
||||
->DataElement("AudioControl", &EditorAudioRtpcComponent::m_defaultRtpc, "Default Rtpc", "The default ATL Rtpc control to use")
|
||||
;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace LmbrCentral
|
||||
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/AudioSwitch.svg")
|
||||
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
||||
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/audio-switch/")
|
||||
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/audio/switch/")
|
||||
->DataElement("AudioControl", &EditorAudioSwitchComponent::m_defaultSwitch, "Default Switch", "The default ATL Switch to use when Activated")
|
||||
->DataElement("AudioControl", &EditorAudioSwitchComponent::m_defaultState, "Default State", "The default ATL State to set on the default Switch when Activated")
|
||||
;
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace LmbrCentral
|
||||
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/AudioTrigger.svg")
|
||||
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
||||
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/audio-trigger/")
|
||||
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/audio/trigger/")
|
||||
->DataElement("AudioControl", &EditorAudioTriggerComponent::m_defaultPlayTrigger, "Default 'play' Trigger", "The default ATL Trigger control used by 'Play'")
|
||||
->DataElement("AudioControl", &EditorAudioTriggerComponent::m_defaultStopTrigger, "Default 'stop' Trigger", "The default ATL Trigger control used by 'Stop'")
|
||||
->DataElement(AZ::Edit::UIHandlers::ComboBox, &EditorAudioTriggerComponent::m_obstructionType, "Obstruction Type", "Ray-casts used in calculation of obstruction and occlusion")
|
||||
|
||||
@@ -122,7 +122,10 @@ namespace PhysX
|
||||
if (m_shapeType != Physics::ShapeType::PhysicsAsset &&
|
||||
m_lastShapeType == Physics::ShapeType::PhysicsAsset)
|
||||
{
|
||||
//clean up any reference to a physics assets, and re-initialize to an empty Pipeline::MeshAsset asset.
|
||||
m_physicsAsset.m_pxAsset.Reset();
|
||||
m_physicsAsset.m_pxAsset = AZ::Data::Asset<Pipeline::MeshAsset>(AZ::Data::AssetLoadBehavior::QueueLoad);
|
||||
|
||||
m_physicsAsset.m_configuration = Physics::PhysicsAssetShapeConfiguration();
|
||||
}
|
||||
m_lastShapeType = m_shapeType;
|
||||
|
||||
@@ -73,6 +73,13 @@ namespace SceneBuilder
|
||||
required.emplace_back(AZ_CRC_CE("AssetImportRequestHandler"));
|
||||
}
|
||||
|
||||
void BuilderPluginComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& services)
|
||||
{
|
||||
// Any components that can modify the analysis fingerprint via SceneBuilderDependencyRequests::AddFingerprintInfo must be activated first,
|
||||
// so they contribute to the fingerprint calculated in BuilderPluginComponent::Activate().
|
||||
services.emplace_back(AZ_CRC_CE("FingerprintModification"));
|
||||
}
|
||||
|
||||
void BuilderPluginComponent::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
|
||||
|
||||
@@ -29,6 +29,7 @@ namespace SceneBuilder
|
||||
void Deactivate() override;
|
||||
|
||||
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
|
||||
static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& services);
|
||||
|
||||
private:
|
||||
SceneBuilderWorker m_sceneBuilder;
|
||||
|
||||
@@ -67,6 +67,8 @@ namespace SceneBuilder
|
||||
context->EnumerateDerived(callback, azrtti_typeid<AZ::SceneAPI::SceneCore::GenerationComponent>(), azrtti_typeid<AZ::SceneAPI::SceneCore::GenerationComponent>());
|
||||
context->EnumerateDerived(callback, azrtti_typeid<AZ::SceneAPI::SceneCore::LoadingComponent>(), azrtti_typeid<AZ::SceneAPI::SceneCore::LoadingComponent>());
|
||||
}
|
||||
|
||||
AZ::SceneAPI::SceneBuilderDependencyBus::Broadcast(&AZ::SceneAPI::SceneBuilderDependencyRequests::AddFingerprintInfo, fragments);
|
||||
|
||||
for (const AZStd::string& element : fragments)
|
||||
{
|
||||
|
||||
@@ -197,7 +197,8 @@ void {{attribute_QualifiedName}}::Reflect(AZ::ReflectContext* context)
|
||||
{% if item.attrib['Description'] is defined %}
|
||||
{% set description = item.attrib['Description'] %}
|
||||
{% endif %}
|
||||
// {{ item.attrib['Name'] }}
|
||||
|
||||
// {{ item.attrib['Name'] }}
|
||||
{{preEdit}}->DataElement({{ uihandler }}, &{{ attribute_Name }}::{{ item.attrib['Name'] }}, "{{ item.attrib['Name'] }}", "{{ description }}"){{postEdit}}
|
||||
{% for EditAttribute in item.iter('EditAttribute') %}
|
||||
{{preEdit}}->Attribute({{ EditAttribute.attrib['Key'] }}, {{ EditAttribute.attrib['Value'] }}){{postEdit}}
|
||||
@@ -272,6 +273,9 @@ void Nodes::{{ nodeableNodeName }}::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
{% if ExtendReflectionEdit is defined %}auto {{preEdit}} = {%endif%}editContext->Class<{{ nodeableNodeName }}>("{{ attribute_PreferredClassName }}", "{{ attribute_Description }}"){{postEdit}}
|
||||
{{preEdit}}->ClassElement(AZ::Edit::ClassElements::EditorData, ""){{postEdit}}
|
||||
{% if attribute_Category is defined %}
|
||||
{{preEdit}}->Attribute(AZ::Edit::Attributes::Category, "{{ attribute_Category }}"){{postEdit}}
|
||||
{% endif %}
|
||||
{{preEdit}}->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly){{postEdit}}
|
||||
{{preEdit}}->Attribute(AZ::Edit::Attributes::AutoExpand, true){{postEdit}}
|
||||
;
|
||||
|
||||
@@ -11,345 +11,28 @@
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/Serialization/EditContextConstants.inl>
|
||||
|
||||
|
||||
/*
|
||||
Any class that implements a nodeable AzAutoGen driver (i.e. *.ScriptCanvasNodeable.xml)
|
||||
requires that the SCRIPTCANVAS_NODE macro be declared within its class declaration.
|
||||
|
||||
Example:
|
||||
|
||||
class CustomNode
|
||||
: public ScriptCanvas::Nodeable
|
||||
{
|
||||
public:
|
||||
SCRIPTCANVAS_NODE(CustomNode);
|
||||
};
|
||||
|
||||
What will happen is that when AzAutoGen runs it will generate a preprocessor directive:
|
||||
|
||||
SCRIPTCANVAS_NODE_CustomNode
|
||||
|
||||
Which will define all of the node's boilerplate code and definitions. When CustomNode
|
||||
is compiled, the preprocessor will replace the macro with the auto generated
|
||||
code.
|
||||
*/
|
||||
|
||||
#define SCRIPTCANVAS_NODE(ClassName) SCRIPTCANVAS_NODE_##ClassName
|
||||
|
||||
/* ----------------------------------------------------------------------------------------------------------
|
||||
*
|
||||
* BaseDefinition
|
||||
* This tag must be included within the body of any custom nodeable class. It generates nodeable code only and it
|
||||
* should be used as a base class only.
|
||||
*
|
||||
* Note: This tag does not generate a node class, so it will be hidden during edit time.
|
||||
*
|
||||
* Example:
|
||||
* BaseDefinition(BaseHelloWorld, "Base Hello World", "My BaseHelloWorld.")
|
||||
*
|
||||
* ----------------------------------------------------------------------------------------------------------- */
|
||||
#define BaseDefinition(ClassName, Name, Description, ...) AZ_JOIN(AZ_GENERATED_, ClassName)
|
||||
|
||||
/* ----------------------------------------------------------------------------------------------------------
|
||||
*
|
||||
* NodeDefinition
|
||||
* This tag must be included within the body of any custom nodeable class. It generates the necessary code to support nodes
|
||||
* and customizes the serialization and reflection parameters(version, converter).
|
||||
*
|
||||
* Example:
|
||||
* NodeDefinition(HelloWorld, "Hello World", "My HelloWorld Node.")
|
||||
* NodeDefinition(HelloWorld, "Hello World", "My HelloWorld Node.",
|
||||
* NodeTags::Icon("Icons/ScriptCanvas/HelloWorld.png")
|
||||
* NodeTags::Version(3, VersionConverter))
|
||||
*
|
||||
* ----------------------------------------------------------------------------------------------------------- */
|
||||
#define NodeDefinition(ClassName, Name, Description, ...) AZ_JOIN(AZ_GENERATED_, ClassName)
|
||||
|
||||
/* ----------------------------------------------------------------------------------------------------------
|
||||
* InputMethod
|
||||
* Using InputMethod on a method will create execution in&out slots that is invoked
|
||||
* automatically. It will also allow the automatic generation of input or output data
|
||||
* slots according to the method's signature.
|
||||
*
|
||||
* Example
|
||||
* InputMethod("Do Something", "My DoSomething Function.")
|
||||
* InputMethod("Do Something", "My DoSomething Function.")
|
||||
* DataInput(int, "DoSomething:Arg", 0, "My DoSomething argument.")
|
||||
*
|
||||
* ----------------------------------------------------------------------------------------------------------- */
|
||||
#define InputMethod(Name, Description, ...)
|
||||
|
||||
/* ----------------------------------------------------------------------------------------------------------
|
||||
* BranchMethod
|
||||
* Using BranchMethod on a method will create execution input&output slots that is invoked
|
||||
* automatically. It will also allow the automatic generation of input data
|
||||
* slots according to the method's signature. BranchMethod should not be used on method
|
||||
* having return type.
|
||||
*
|
||||
* Coupled with macro ExecutionOutput to generate branch out execution slots.
|
||||
*
|
||||
* Example
|
||||
* BranchMethod("Branches", "My Branches Function.")
|
||||
* ExecutionOutput("Branch1", "My Branch1 Function.", SlotTags::BranchOf("Branches"))
|
||||
* ExecutionOutput("Branch2", "My Branch2 Function.", SlotTags::BranchOf("Branches"))
|
||||
*
|
||||
* ----------------------------------------------------------------------------------------------------------- */
|
||||
#define BranchMethod(Name, Description, ...)
|
||||
|
||||
/* ----------------------------------------------------------------------------------------------------------
|
||||
* OnInputChangeMethod
|
||||
* Using OnInputChangeMethod on a method will create one data input slot that is invoked
|
||||
* automatically, and it should be used only with one input method.
|
||||
*
|
||||
* Example
|
||||
* OnInputChangeMethod("MyInputChangeMethod", "My OnInputChange Function.")
|
||||
* DataInput(int, "MyInputChangeMethod:Arg", 0, "My MyInputChangeMethod argument.", SlotTags::DisplayGroup("MyInputChangeMethod"))
|
||||
*
|
||||
* ----------------------------------------------------------------------------------------------------------- */
|
||||
#define OnInputChangeMethod(Name, Description, ...)
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------------------------------------------
|
||||
*
|
||||
* ExecutionInput
|
||||
* This is a shorthand macro to easily create an execution input slot.
|
||||
*
|
||||
* Examples:
|
||||
* ExecutionInput("Start Process", "Signals this node to begin processing.")
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------------------------- */
|
||||
#define ExecutionInput(Name, Description, ...)
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------------------------------------------
|
||||
*
|
||||
* ExecutionOutput
|
||||
* This is a shorthand macro to easily create an execution output slot.
|
||||
*
|
||||
* Examples:
|
||||
* ExecutionOutput("On Start Process", "Output of start process execution.");
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------------------------- */
|
||||
#define ExecutionOutput(Name, Description, ...)
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------------------------------------------
|
||||
*
|
||||
* ExecutionLatentOutput
|
||||
* Similar to ExecutionOutput however it is used to make it explicit that the output slot will be latent,
|
||||
* this means that the node maintains state and may not signal this slot immediately.
|
||||
*
|
||||
* Example:
|
||||
* ExecutionLatentOutput("On Finished", "Will be signaled when the operation is complete.");
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------------------------- */
|
||||
#define ExecutionLatentOutput(Name, Description, ...)
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------------------------------------------
|
||||
*
|
||||
* Data
|
||||
* Provides shorthand for exposing data to serialize context and edit context,
|
||||
* mainly used with SlotTags::PropertyReference for property data.
|
||||
*
|
||||
* Example:
|
||||
* int m_data = 1;
|
||||
* PropertyData(int, "My Data", "My Serialized Data.", SlotTags::PropertyReference(m_data));
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------------------------- */
|
||||
#define PropertyData(Type, Name, Description, ...)
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------------------------------------------
|
||||
*
|
||||
* DataInput
|
||||
* Provides shorthand for creating an input data slot.
|
||||
*
|
||||
* Coupled with macro InputMethod/BranchMethod/OnInputChangeMethod to give parameter editor definition
|
||||
*
|
||||
* Example:
|
||||
* InputMethod("Do Something", "My DoSomething Function.")
|
||||
* DataInput(int, "DoSomething:Arg", 0, "My DoSomething argument.")
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------------------------- */
|
||||
#define DataInput(Type, Name, DefaultVal, Description, ...)
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------------------------------------------
|
||||
*
|
||||
* DataOutput
|
||||
* Provides shorthand for creating an output data slot.
|
||||
*
|
||||
* Coupled with macro InputMethod/BranchMethod/OnInputChangeMethod to give result editor definition
|
||||
*
|
||||
* Example:
|
||||
* InputMethod("Do Something", "My DoSomething Function.")
|
||||
* DataOutput(int, "DoSomething:Result", 0, "My DoSomething result.")
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------------------------- */
|
||||
#define DataOutput(Type, Name, Description, ...)
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------------------------------------------
|
||||
*
|
||||
* DynamicValueDataInput
|
||||
* Provides shorthand for creating an input dynamic value data slot.
|
||||
*
|
||||
* Examples:
|
||||
* DynamicValueDataInput("ValueData", "A generic value data.")
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------------------------- */
|
||||
#define DynamicValueDataInput(Name, Description, ...)
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------------------------------------------
|
||||
*
|
||||
* DynamicValueDataOutput
|
||||
* Provides shorthand for creating an output dynamic value data slot.
|
||||
*
|
||||
* Examples:
|
||||
* DynamicValueDataOutput("ValueData", "A generic value data.")
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------------------------- */
|
||||
#define DynamicValueDataOutput(Name, Description, ...)
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------------------------------------------
|
||||
*
|
||||
* DynamicContainerDataInput
|
||||
* Provides shorthand for creating an input dynamic container data slot.
|
||||
*
|
||||
* Coupled with macro ExecutionInput/ExecutionOutput/ExecutionLatentOutput to generate dynamic data input slot
|
||||
*
|
||||
* Examples:
|
||||
* DynamicContainerDataInput("ContainerData", "A generic container data.")
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------------------------- */
|
||||
#define DynamicContainerDataInput(Name, Description, ...)
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------------------------------------------
|
||||
*
|
||||
* DynamicContainerDataOutput
|
||||
* Provides shorthand for creating an output dynamic container data slot.
|
||||
*
|
||||
* Coupled with macro ExecutionInput/ExecutionOutput/ExecutionLatentOutput to generate dynamic data output slot
|
||||
*
|
||||
* Examples:
|
||||
* DynamicContainerDataOutput("ContainerData", "A generic container data.")
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------------------------- */
|
||||
#define DynamicContainerDataOutput(Name, Description, ...)
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------------------------------------------
|
||||
*
|
||||
* DynamicAnyDataInput
|
||||
* Provides shorthand for creating an input dynamic any data slot.
|
||||
*
|
||||
* Coupled with macro ExecutionInput/ExecutionOutput/ExecutionLatentOutput to generate dynamic data input slot
|
||||
*
|
||||
* Examples:
|
||||
* DynamicAnyDataInput("AnyData", "A generic any data.")
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------------------------- */
|
||||
#define DynamicAnyDataInput(Name, Description, ...)
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------------------------------------------
|
||||
*
|
||||
* DynamicAnyDataOutput
|
||||
* Provides shorthand for creating an output dynamic any data slot.
|
||||
*
|
||||
* Coupled with macro ExecutionInput/ExecutionOutput/ExecutionLatentOutput to generate dynamic data output slot
|
||||
*
|
||||
* Examples:
|
||||
* DynamicAnyDataOutput("AnyData", "A generic any data.")
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------------------------- */
|
||||
#define DynamicAnyDataOutput(Name, Description, ...)
|
||||
|
||||
// Intellisense helpers, the following definitions exist to provide code completion details regarding what attributes are
|
||||
// supported by the different tags.
|
||||
|
||||
// Revisited common tags, we should be able to remove NodeableCodegen eventually
|
||||
namespace NodeableCodegen
|
||||
{
|
||||
namespace ScriptCanvasTags
|
||||
{
|
||||
using OverrideName = const char*;
|
||||
using Uuid = const char*;
|
||||
using Category = const char*;
|
||||
using Icon = const char*;
|
||||
using Deprecated = const char*;
|
||||
|
||||
struct Version
|
||||
{
|
||||
using ConverterFunction = bool(class AZ::SerializeContext& context, class AZ::SerializeContext::DataElementNode& classElement);
|
||||
Version(unsigned int /*version*/) {}
|
||||
Version(unsigned int /*version*/, ConverterFunction /*converter*/) {}
|
||||
};
|
||||
|
||||
template <class EventHandlerType>
|
||||
struct EventHandler
|
||||
{
|
||||
EventHandler() = default;
|
||||
};
|
||||
|
||||
namespace Edit
|
||||
{
|
||||
struct UIHandler
|
||||
{
|
||||
UIHandler([[maybe_unused]] const AZ::Crc32& uiHandler = AZ::Edit::UIHandlers::Default) {}
|
||||
};
|
||||
}
|
||||
|
||||
struct EditAttributes
|
||||
{
|
||||
template <typename ...Args>
|
||||
EditAttributes(Args&& ... args) {}
|
||||
};
|
||||
|
||||
struct BaseClass
|
||||
{
|
||||
BaseClass(AZStd::initializer_list<const char*>) {}
|
||||
};
|
||||
|
||||
//struct Contracts
|
||||
//{
|
||||
// explicit Contracts(AZStd::initializer_list<ScriptCanvas::Contract>) {}
|
||||
//};
|
||||
|
||||
//struct RestrictedTypeContractTag
|
||||
//{
|
||||
// explicit RestrictedTypeContractTag(AZStd::initializer_list<ScriptCanvas::Data::Type>) {}
|
||||
//};
|
||||
|
||||
struct SupportsMethodContractTag
|
||||
{
|
||||
explicit SupportsMethodContractTag(const char*) {}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
namespace NodeTags
|
||||
{
|
||||
using NodeableCodegen::ScriptCanvasTags::OverrideName;
|
||||
using NodeableCodegen::ScriptCanvasTags::Uuid;
|
||||
using NodeableCodegen::ScriptCanvasTags::Version;
|
||||
using NodeableCodegen::ScriptCanvasTags::Icon;
|
||||
using NodeableCodegen::ScriptCanvasTags::EditAttributes;
|
||||
using NodeableCodegen::ScriptCanvasTags::Category;
|
||||
using NodeableCodegen::ScriptCanvasTags::Deprecated;
|
||||
|
||||
using GraphEntryPoint = bool;
|
||||
}
|
||||
|
||||
namespace SlotTags
|
||||
{
|
||||
using NodeableCodegen::ScriptCanvasTags::OverrideName;
|
||||
//using NodeableCodegen::ScriptCanvasTags::Contracts;
|
||||
|
||||
// Data specific
|
||||
using DisplayGroup = const char*;
|
||||
|
||||
// PropertyData specific
|
||||
using PropertyReference = const char*;
|
||||
using PropertyInterface = const char*;
|
||||
|
||||
// ExecutionSlot specific
|
||||
using BranchOf = const char*;
|
||||
|
||||
// EditContext specific
|
||||
using NodeableCodegen::ScriptCanvasTags::EditAttributes;
|
||||
using NodeableCodegen::ScriptCanvasTags::Edit::UIHandler;
|
||||
using AzCommon::Attributes::ChangeNotify;
|
||||
using AzCommon::Attributes::Visibility;
|
||||
using AzCommon::Attributes::AutoExpand;
|
||||
using AzCommon::Attributes::DescriptionTextOverride;
|
||||
using AzCommon::Attributes::NameLabelOverride;
|
||||
using AzCommon::Attributes::Min;
|
||||
using AzCommon::Attributes::Max;
|
||||
|
||||
// DynamicData specific
|
||||
//using NodeableCodegen::ScriptCanvasTags::RestrictedTypeContractTag;
|
||||
using NodeableCodegen::ScriptCanvasTags::SupportsMethodContractTag;
|
||||
using DynamicGroup = const char*;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include <SurfaceDataSystemComponent.h>
|
||||
#include <Components/SurfaceDataColliderComponent.h>
|
||||
#include <Components/SurfaceDataShapeComponent.h>
|
||||
#include <TerrainSurfaceDataSystemComponent.h>
|
||||
|
||||
namespace SurfaceData
|
||||
{
|
||||
@@ -20,7 +19,6 @@ namespace SurfaceData
|
||||
SurfaceDataSystemComponent::CreateDescriptor(),
|
||||
SurfaceDataColliderComponent::CreateDescriptor(),
|
||||
SurfaceDataShapeComponent::CreateDescriptor(),
|
||||
Terrain::TerrainSurfaceDataSystemComponent::CreateDescriptor(),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -28,7 +26,6 @@ namespace SurfaceData
|
||||
{
|
||||
return AZ::ComponentTypeList{
|
||||
azrtti_typeid<SurfaceDataSystemComponent>(),
|
||||
azrtti_typeid<Terrain::TerrainSurfaceDataSystemComponent>(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,8 +19,6 @@ set(FILES
|
||||
Include/SurfaceData/Utility/SurfaceDataUtility.h
|
||||
Source/SurfaceDataSystemComponent.cpp
|
||||
Source/SurfaceDataSystemComponent.h
|
||||
Source/TerrainSurfaceDataSystemComponent.cpp
|
||||
Source/TerrainSurfaceDataSystemComponent.h
|
||||
Source/SurfaceTag.cpp
|
||||
Source/Components/SurfaceDataColliderComponent.cpp
|
||||
Source/Components/SurfaceDataColliderComponent.h
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>icon / Environmental / Terrain Height</title>
|
||||
<g id="icon-/-Environmental-/-Terrain-Height" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M15,4 L22,20 L7.9986687,19.9993631 L7.9986687,14.9993631 L10,15 L8,11 L7.9986687,11.0013631 L7.9986687,8.00136309 L8,8 L10.8,13.6 L15,4 Z M15,7 L13,12 L17,12 L15,7 Z M7.21914721,0.964363085 L7.21914721,5.50135833 L5.8081113,5.50135833 L5.8081113,6.20344432 L7.21914721,6.20344432 L7.21914721,7.31885826 L5.8081113,7.31885826 L5.8081113,7.94758992 L7.21914721,7.94758992 L7.21914721,9.12604407 L6.01490518,9.12604407 L6.01490518,9.69309095 L7.21914721,9.69309095 L7.21914721,10.8434468 L6.01490518,10.8434468 L6.01490518,11.3367478 L7.21914721,11.3367478 L7.21914721,12.561057 L6.01490518,12.561057 L6.01490518,13.2062487 L7.21914721,13.2062487 L7.21914721,14.3024381 L6.01490518,14.3024381 L6.01490518,14.9912747 L7.21914721,14.9912747 L7.21914721,16.0640829 L6.01490518,16.0640829 L6.01490518,16.762272 L7.21914721,16.762272 L7.21914721,17.9022362 L6.01490518,17.9022362 L6.01490518,18.5465186 L7.21914721,18.5465186 L7.21914721,19.5980239 L6.01490518,19.5980239 L6.01490518,20.2294467 L7.21914721,20.2294467 L7.21914721,21.2565316 L5.8081113,21.2565316 L5.8081113,21.9825184 L7.21914721,21.9825184 L7.21914721,23.0479225 L2.9026687,23.0479225 C2.35038395,23.0479225 1.9026687,22.6002072 1.9026687,22.0479225 L1.9026687,1.96436309 C1.9026687,1.41207834 2.35038395,0.964363085 2.9026687,0.964363085 L7.21914721,0.964363085 Z M4.60708525,1.76269886 C4.1792616,1.76269886 3.832442,2.10951846 3.832442,2.53734212 C3.832442,2.96516577 4.1792616,3.31198537 4.60708525,3.31198537 C5.03490891,3.31198537 5.38172851,2.96516577 5.38172851,2.53734212 C5.38172851,2.10951846 5.03490891,1.76269886 4.60708525,1.76269886 Z" id="Combined-Shape" fill="#8B572A"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>icon / Environmental / Terrain Mesh</title>
|
||||
<g id="icon-/-Environmental-/-Terrain-Mesh" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M16,5.23640192 L23,21.2364019 L1,21.2364019 L3.153,16.9310652 L10.848,16.9310652 L10.867,16.9694019 L16,5.23640192 Z M11.279112,8.05162125 C11.8313968,8.05162125 12.279112,8.4993365 12.279112,9.05162125 L12.279112,9.57054521 L9.49851168,15.5710293 L2,15.5710293 C1.44771525,15.5710293 1,15.123314 1,14.5710293 L1,9.05162125 C1,8.4993365 1.44771525,8.05162125 2,8.05162125 L11.279112,8.05162125 Z M16,8.23640192 L14,13.2364019 L18,13.2364019 L16,8.23640192 Z M11.339186,5.23184324 L11.339186,7.11169525 L1.939926,7.11169525 L1.939926,5.23184324 L11.339186,5.23184324 Z M10.39926,2.41206524 L10.39926,4.29191724 L2.879852,4.29191724 L2.879852,2.41206524 L10.39926,2.41206524 Z" id="Combined-Shape" fill="#8B572A"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>icon / Environmental / Generate Terrian </title>
|
||||
<g id="icon-/-Environmental-/-Generate-Terrian-" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M16,5.23640192 L23,21.2364019 L1,21.2364019 L3.153,16.9311553 L10.7351274,16.9317461 L10.786,16.8081553 L10.867,16.9694019 L16,5.23640192 Z M10.7866981,7.88269686 C11.1462033,8.9008023 11.3517156,9.91858532 11.3517156,10.6951961 C11.3517156,13.0695588 9.43072532,14.9943601 7.06106374,14.9943601 C4.69140216,14.9943601 2.77041189,13.0695588 2.77041189,10.6951961 C2.77041189,9.98779834 2.94092524,9.08030022 3.24301589,8.15472512 L5.48573411,10.2222098 L7.16571378,8.51342542 L8.83993571,10.2222098 L10.7866981,7.88269686 Z M16,8.23640192 L14,13.2364019 L18,13.2364019 L16,8.23640192 Z M7.06106374,3.69215527 C8.35768587,3.69215527 9.51997507,5.07798918 10.306728,6.72204766 L8.83082438,8.28190713 L7.15660245,6.58611652 L5.47662277,8.28190713 L3.80919728,6.73502979 C4.59603726,5.08529116 5.76103013,3.69215527 7.06106374,3.69215527 Z" id="Combined-Shape" fill="#8B572A"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>icon / Environmental / Terrain Refactor</title>
|
||||
<g id="icon-/-Environmental-/-Terrain-Refactor" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<rect id="Icon-Background" x="0" y="0" width="24" height="24"></rect>
|
||||
<path d="M2,20 L8,8 L10.8,13.6 L15,4 L22,20 L2,20 Z M8,11 L6,15 L10,15 L8,11 Z M15,7 L13,12 L17,12 L15,7 Z" id="Combined-Shape" fill="#8B572A"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 611 B |
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>icon / Environmental / Terrain World Debugger</title>
|
||||
<g id="icon-/-Environmental-/-Terrain-World-Debugger" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<rect id="Icon-Background" x="0" y="0" width="24" height="24"></rect>
|
||||
<path d="M16,5.23640192 L23,21.2364019 L1,21.2364019 L4.034,15.1675981 L5.534,15.1675981 L5,16.2364019 L9,16.2364019 L8.466,15.1675981 L9.966,15.1675981 L10.867,16.9694019 L16,5.23640192 Z M5.7575896,2.84113022 C6.03130743,3.01216811 5.99303429,3.29144758 5.82199641,3.56516541 C5.5939459,3.93012252 5.63692265,4.11800916 5.79661523,4.47152561 C6.28699059,4.39735197 7.01109383,4.59333567 7.42167058,4.8498925 C7.55852949,4.93541144 7.6668821,5.06655002 7.80374101,5.15206897 C8.11735815,5.15774162 8.40246897,5.20903391 8.76170575,5.24321288 L9.24631309,4.46767902 C8.9379383,4.03250676 8.70982422,3.7208123 8.56197083,3.53259562 C8.34019074,3.25027061 9.17154039,2.55841555 9.4400416,2.93949264 C9.57872751,3.13632611 9.82791397,3.48184986 10.187601,3.9760639 C10.2921591,4.119728 10.3942808,4.6608066 10.3267772,4.76883491 L9.87364273,5.4940017 C10.1587536,5.54529399 10.3697384,5.61369961 10.5807233,5.68210522 C10.8373278,5.77901716 11.1395519,5.9044354 11.4132698,6.07547329 L11.7101975,5.60028961 C11.8353154,5.40005911 12.2927086,5.36754107 12.4693791,5.39797939 C13.0265899,5.49398048 13.4462261,5.56768275 13.7282877,5.61908617 C14.2855266,5.72063845 13.9217192,6.73248862 13.5801311,6.66829019 C13.3524057,6.62549124 12.9722579,6.55710151 12.4396878,6.46312102 L12.2686499,6.73683885 C12.5480881,7.10174828 13.0794962,7.47936459 13.5424736,8.2230391 C13.2332304,8.82332896 12.3709586,10.4987773 10.9556581,13.249384 C10.2300617,13.3746794 8.81376094,13.077921 8.3917912,12.9411097 L8.22075331,13.2148276 C8.50116176,13.5796109 8.72927585,13.8913054 8.90509558,14.149911 C9.16882518,14.5378194 8.27592007,15.0939625 8.02702481,14.743014 C7.89765153,14.5605944 7.64842782,14.2153849 7.2793537,13.7073854 C7.12105098,13.489495 7.07511207,13.0179769 7.14377888,12.908087 L7.42238579,12.4622227 C7.14866796,12.2911849 6.90345644,12.0745273 6.70386456,11.8863761 C6.50427268,11.6982249 6.37880675,11.4929604 6.22483451,11.3333155 L5.71101869,12.1555927 C5.66374222,12.2312508 5.39038727,12.3573847 5.30398078,12.3447359 C4.6772029,12.2529836 4.15546889,12.1592118 3.73877875,12.0634204 C3.21667547,11.8756628 3.41280304,11.0226842 3.88693529,11.0142164 C4.12232485,11.0454571 4.51197472,11.0986403 5.0558849,11.1737659 L5.54049224,10.3982321 C5.32378704,10.1359549 5.18120779,9.85656443 5.03862853,9.57717395 C4.8732633,9.53727464 4.70789807,9.49737534 4.57103915,9.4118564 C4.1604624,9.15529957 3.78986942,8.48459472 3.61306352,8.05695233 C3.17970079,8.03988668 2.99858532,8.18114181 2.77053481,8.54609893 C2.59949692,8.81981676 2.32495313,9.10238902 2.0512353,8.93135114 C1.77751746,8.76031325 1.77825895,8.55548589 1.94929684,8.28176806 C2.34838524,7.64309311 2.76327156,7.33075241 3.4931381,7.27936476 C3.53871006,6.80038239 3.65840797,6.3042867 3.94347111,5.84809031 C4.22853425,5.39189392 4.59344369,5.11245575 5.00397276,4.86152389 C4.73020724,4.18299733 4.73852278,3.70999986 5.13761117,3.07132492 C5.30864906,2.79760708 5.48387176,2.67009234 5.7575896,2.84113022 Z M16,8.23640192 L14,13.2364019 L18,13.2364019 L16,8.23640192 Z M8.03008933,6.27169476 C7.86593803,6.90855503 7.62722372,7.47571465 7.31394641,7.9731736 C7.00066911,8.47063256 6.59764439,8.93015918 6.10487226,9.35175347 C6.78174598,10.4775099 7.41236887,11.2247577 7.99674093,11.5934967 C8.5453571,11.9396737 9.08387287,12.311171 9.87590139,12.1865085 C10.2486081,11.6217646 11.911756,8.7895471 12.3207747,8.14675221 C11.6119238,7.43916872 11.2609677,7.0257827 10.6750595,6.74648915 C9.90539682,6.37960262 8.70735067,6.16885245 8.03008933,6.27169476 Z M4.85586389,6.41821659 C4.37125655,7.19375045 4.50249049,8.10037522 5.14116544,8.49946362 C5.27802436,8.58498256 5.3267053,8.57787184 5.47944458,8.48580763 C5.99590493,8.07119781 6.32940283,7.74343935 6.4799383,7.50253224 C6.63347019,7.25682987 6.79975217,6.83183484 6.97878426,6.22754717 C7.02310864,5.99705844 6.98840321,5.84780422 6.85154429,5.76228528 C6.21286935,5.36319688 5.34047123,5.64268273 4.85586389,6.41821659 Z" id="Combined-Shape" fill="#8B572A"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.4 KiB |
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>icon / Environmental / Terrain World Renderer</title>
|
||||
<g id="icon-/-Environmental-/-Terrain-World-Renderer" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<rect id="Icon-Background" x="0" y="0" width="24" height="24"></rect>
|
||||
<path d="M14.128,5.993 L14.1280665,8 L16.5280665,8 L16.528,7.493 L16.75,7.999 L16.5280665,8 L16.5280665,10.4 L17.8,10.399 L18.85,12.799 L16.5280665,12.8 L16.5280665,15.2 L18.9280665,15.2 L18.928,12.978 L19.9,15.199 L18.9280665,15.2 L18.9280665,17.6 L20.95,17.599 L22,20 L14.1280665,20 L14.1280665,17.6 L11.7280665,17.6 L11.7280665,20 L2,20 L8,8 L10.8,13.6 L12.2,10.399 L14.1280665,10.4 L14.1280665,8 L13.25,7.999 L14.128,5.993 Z M18.9280665,17.6 L16.5280665,17.6 L16.5280665,20 L18.9280665,20 L18.9280665,17.6 Z M16.5280665,15.2 L14.1280665,15.2 L14.1280665,17.6 L16.5280665,17.6 L16.5280665,15.2 Z M14.1280665,12.8 L11.7280665,12.8 L11.7280665,15.2 L14.1280665,15.2 L14.1280665,12.8 Z M8,11 L6,15 L10,15 L8,11 Z M16.5280665,10.4 L14.1280665,10.4 L14.1280665,12.8 L16.5280665,12.8 L16.5280665,10.4 Z M15,4 L15.7,5.599 L14.3,5.599 L15,4 Z" id="Combined-Shape" fill="#8B572A"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>icon / Environmental / Terrain Height - box</title>
|
||||
<defs>
|
||||
<linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient-1">
|
||||
<stop stop-color="#ADADAD" stop-opacity="0" offset="0%"></stop>
|
||||
<stop stop-color="#9D9D9D" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
<path d="M20.924815,0 C23.1021676,0 23.9566542,1.77846722 23.9566542,3.01865122 C23.9566542,4.25883521 23.9566542,18.7815894 23.9566542,20.6420628 C23.9566542,22.3929727 22.7281352,23.9566542 20.924815,23.9566542 C19.1214948,23.9566542 4.64929841,23.9566542 2.80929064,23.9566542 C1.38661643,23.9566542 0,22.5147712 0,21.1586662 C0,19.8025612 0,4.03532882 0,2.67255351 C0,1.3097782 1.36539793,0 2.60955829,0 C3.85371865,0 18.9601214,0 20.924815,0 Z" id="path-2"></path>
|
||||
</defs>
|
||||
<g id="icon-/-Environmental-/-Terrain-Height---box" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="icon/general/color/-box-template">
|
||||
<mask id="mask-3" fill="white">
|
||||
<use xlink:href="#path-2"></use>
|
||||
</mask>
|
||||
<use id="Path-3" fill="url(#linearGradient-1)" xlink:href="#path-2"></use>
|
||||
<g id="Group" mask="url(#mask-3)" fill="#8B572A">
|
||||
<g id="icon/color/AI" style="mix-blend-mode: multiply;">
|
||||
<rect id="Lights" x="0" y="0" width="24" height="24"></rect>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<path d="M15.0973313,4.03563691 L22.0973313,20.0356369 L8.096,20.035 L8.096,15.035 L10.0973313,15.0356369 L8.0973313,11.0356369 L8.096,11.037 L8.096,8.037 L8.0973313,8.03563691 L10.8973313,13.6356369 L15.0973313,4.03563691 Z M15.0973313,7.03563691 L13.0973313,12.0356369 L17.0973313,12.0356369 L15.0973313,7.03563691 Z M7.31647851,1 L7.31647851,5.53699525 L5.9054426,5.53699525 L5.9054426,6.23908123 L7.31647851,6.23908123 L7.31647851,7.35449518 L5.9054426,7.35449518 L5.9054426,7.98322684 L7.31647851,7.98322684 L7.31647851,9.16168098 L6.11223647,9.16168098 L6.11223647,9.72872786 L7.31647851,9.72872786 L7.31647851,10.8790837 L6.11223647,10.8790837 L6.11223647,11.3723847 L7.31647851,11.3723847 L7.31647851,12.5966939 L6.11223647,12.5966939 L6.11223647,13.2418856 L7.31647851,13.2418856 L7.31647851,14.338075 L6.11223647,14.338075 L6.11223647,15.0269116 L7.31647851,15.0269116 L7.31647851,16.0997198 L6.11223647,16.0997198 L6.11223647,16.7979089 L7.31647851,16.7979089 L7.31647851,17.9378731 L6.11223647,17.9378731 L6.11223647,18.5821555 L7.31647851,18.5821555 L7.31647851,19.6336608 L6.11223647,19.6336608 L6.11223647,20.2650836 L7.31647851,20.2650836 L7.31647851,21.2921685 L5.9054426,21.2921685 L5.9054426,22.0181553 L7.31647851,22.0181553 L7.31647851,23.0835594 L3,23.0835594 C2.44771525,23.0835594 2,22.6358441 2,22.0835594 L2,2 C2,1.44771525 2.44771525,1 3,1 L7.31647851,1 Z M4.70441655,1.79833578 C4.27659289,1.79833578 3.92977329,2.14515537 3.92977329,2.57297903 C3.92977329,3.00080269 4.27659289,3.34762229 4.70441655,3.34762229 C5.13224021,3.34762229 5.47905981,3.00080269 5.47905981,2.57297903 C5.47905981,2.14515537 5.13224021,1.79833578 4.70441655,1.79833578 Z" id="Combined-Shape-Copy" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.3 KiB |
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>icon / Environmental / Terrain Mesh - box</title>
|
||||
<defs>
|
||||
<linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient-1">
|
||||
<stop stop-color="#ADADAD" stop-opacity="0" offset="0%"></stop>
|
||||
<stop stop-color="#9D9D9D" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
<path d="M20.924815,0 C23.1021676,0 23.9566542,1.77846722 23.9566542,3.01865122 C23.9566542,4.25883521 23.9566542,18.7815894 23.9566542,20.6420628 C23.9566542,22.3929727 22.7281352,23.9566542 20.924815,23.9566542 C19.1214948,23.9566542 4.64929841,23.9566542 2.80929064,23.9566542 C1.38661643,23.9566542 0,22.5147712 0,21.1586662 C0,19.8025612 0,4.03532882 0,2.67255351 C0,1.3097782 1.36539793,0 2.60955829,0 C3.85371865,0 18.9601214,0 20.924815,0 Z" id="path-2"></path>
|
||||
</defs>
|
||||
<g id="icon-/-Environmental-/-Terrain-Mesh---box" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="icon/general/color/-box-template">
|
||||
<mask id="mask-3" fill="white">
|
||||
<use xlink:href="#path-2"></use>
|
||||
</mask>
|
||||
<use id="Path-3" fill="url(#linearGradient-1)" xlink:href="#path-2"></use>
|
||||
<g id="Group" mask="url(#mask-3)" fill="#8B572A">
|
||||
<g id="icon/color/AI" style="mix-blend-mode: multiply;">
|
||||
<rect id="Lights" x="0" y="0" width="24" height="24"></rect>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<path d="M16,5.23640192 L23,21.2364019 L1,21.2364019 L3.153,16.9310652 L10.848,16.9310652 L10.867,16.9694019 L16,5.23640192 Z M11.279112,8.05162125 C11.8313968,8.05162125 12.279112,8.4993365 12.279112,9.05162125 L12.279112,9.57054521 L9.49851168,15.5710293 L2,15.5710293 C1.44771525,15.5710293 1,15.123314 1,14.5710293 L1,9.05162125 C1,8.4993365 1.44771525,8.05162125 2,8.05162125 L11.279112,8.05162125 Z M16,8.23640192 L14,13.2364019 L18,13.2364019 L16,8.23640192 Z M11.339186,5.23184324 L11.339186,7.11169525 L1.939926,7.11169525 L1.939926,5.23184324 L11.339186,5.23184324 Z M10.39926,2.41206524 L10.39926,4.29191724 L2.879852,4.29191724 L2.879852,2.41206524 L10.39926,2.41206524 Z" id="Combined-Shape" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>icon / Environmental / Generate Terrian - box</title>
|
||||
<defs>
|
||||
<linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient-1">
|
||||
<stop stop-color="#ADADAD" stop-opacity="0" offset="0%"></stop>
|
||||
<stop stop-color="#9D9D9D" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
<path d="M20.924815,0 C23.1021676,0 23.9566542,1.77846722 23.9566542,3.01865122 C23.9566542,4.25883521 23.9566542,18.7815894 23.9566542,20.6420628 C23.9566542,22.3929727 22.7281352,23.9566542 20.924815,23.9566542 C19.1214948,23.9566542 4.64929841,23.9566542 2.80929064,23.9566542 C1.38661643,23.9566542 0,22.5147712 0,21.1586662 C0,19.8025612 0,4.03532882 0,2.67255351 C0,1.3097782 1.36539793,0 2.60955829,0 C3.85371865,0 18.9601214,0 20.924815,0 Z" id="path-2"></path>
|
||||
</defs>
|
||||
<g id="icon-/-Environmental-/-Generate-Terrian---box" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="icon/general/color/-box-template">
|
||||
<mask id="mask-3" fill="white">
|
||||
<use xlink:href="#path-2"></use>
|
||||
</mask>
|
||||
<use id="Path-3" fill="url(#linearGradient-1)" xlink:href="#path-2"></use>
|
||||
<g id="Group" mask="url(#mask-3)" fill="#8B572A">
|
||||
<g id="icon/color/AI" style="mix-blend-mode: multiply;">
|
||||
<rect id="Lights" x="0" y="0" width="24" height="24"></rect>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<path d="M16,5.23640192 L23,21.2364019 L1,21.2364019 L3.153,16.9311553 L10.7351274,16.9317461 L10.786,16.8081553 L10.867,16.9694019 L16,5.23640192 Z M10.7866981,7.88269686 C11.1462033,8.9008023 11.3517156,9.91858532 11.3517156,10.6951961 C11.3517156,13.0695588 9.43072532,14.9943601 7.06106374,14.9943601 C4.69140216,14.9943601 2.77041189,13.0695588 2.77041189,10.6951961 C2.77041189,9.98779834 2.94092524,9.08030022 3.24301589,8.15472512 L5.48573411,10.2222098 L7.16571378,8.51342542 L8.83993571,10.2222098 L10.7866981,7.88269686 Z M16,8.23640192 L14,13.2364019 L18,13.2364019 L16,8.23640192 Z M7.06106374,3.69215527 C8.35768587,3.69215527 9.51997507,5.07798918 10.306728,6.72204766 L8.83082438,8.28190713 L7.15660245,6.58611652 L5.47662277,8.28190713 L3.80919728,6.73502979 C4.59603726,5.08529116 5.76103013,3.69215527 7.06106374,3.69215527 Z" id="Combined-Shape" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>icon / Environmental / Terrain Refactor - box</title>
|
||||
<defs>
|
||||
<linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient-1">
|
||||
<stop stop-color="#ADADAD" stop-opacity="0" offset="0%"></stop>
|
||||
<stop stop-color="#9D9D9D" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
<path d="M20.924815,0 C23.1021676,0 23.9566542,1.77846722 23.9566542,3.01865122 C23.9566542,4.25883521 23.9566542,18.7815894 23.9566542,20.6420628 C23.9566542,22.3929727 22.7281352,23.9566542 20.924815,23.9566542 C19.1214948,23.9566542 4.64929841,23.9566542 2.80929064,23.9566542 C1.38661643,23.9566542 0,22.5147712 0,21.1586662 C0,19.8025612 0,4.03532882 0,2.67255351 C0,1.3097782 1.36539793,0 2.60955829,0 C3.85371865,0 18.9601214,0 20.924815,0 Z" id="path-2"></path>
|
||||
</defs>
|
||||
<g id="icon-/-Environmental-/-Terrain-Refactor---box" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="icon/general/color/-box-template">
|
||||
<mask id="mask-3" fill="white">
|
||||
<use xlink:href="#path-2"></use>
|
||||
</mask>
|
||||
<use id="Path-3" fill="url(#linearGradient-1)" xlink:href="#path-2"></use>
|
||||
<g id="Group" mask="url(#mask-3)" fill="#8B572A">
|
||||
<g id="icon/color/AI" style="mix-blend-mode: multiply;">
|
||||
<rect id="Lights" x="0" y="0" width="24" height="24"></rect>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<path d="M2,20 L8,8 L10.8,13.6 L15,4 L22,20 L2,20 Z M8,11 L6,15 L10,15 L8,11 Z M15,7 L13,12 L17,12 L15,7 Z" id="Combined-Shape" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>icon / Environmental / Terrain World Debugger - box</title>
|
||||
<defs>
|
||||
<linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient-1">
|
||||
<stop stop-color="#ADADAD" stop-opacity="0" offset="0%"></stop>
|
||||
<stop stop-color="#9D9D9D" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
<path d="M20.924815,0 C23.1021676,0 23.9566542,1.77846722 23.9566542,3.01865122 C23.9566542,4.25883521 23.9566542,18.7815894 23.9566542,20.6420628 C23.9566542,22.3929727 22.7281352,23.9566542 20.924815,23.9566542 C19.1214948,23.9566542 4.64929841,23.9566542 2.80929064,23.9566542 C1.38661643,23.9566542 0,22.5147712 0,21.1586662 C0,19.8025612 0,4.03532882 0,2.67255351 C0,1.3097782 1.36539793,0 2.60955829,0 C3.85371865,0 18.9601214,0 20.924815,0 Z" id="path-2"></path>
|
||||
</defs>
|
||||
<g id="icon-/-Environmental-/-Terrain-World-Debugger---box" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="icon/general/color/-box-template">
|
||||
<mask id="mask-3" fill="white">
|
||||
<use xlink:href="#path-2"></use>
|
||||
</mask>
|
||||
<use id="Path-3" fill="url(#linearGradient-1)" xlink:href="#path-2"></use>
|
||||
<g id="Group" mask="url(#mask-3)" fill="#8B572A">
|
||||
<g id="icon/color/AI" style="mix-blend-mode: multiply;">
|
||||
<rect id="Lights" x="0" y="0" width="24" height="24"></rect>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<path d="M16.1386919,4 L23.1386919,20 L1.13869185,20 L4.17269185,13.9311962 L5.67269185,13.9311962 L5.13869185,15 L9.13869185,15 L8.60469185,13.9311962 L10.1046919,13.9311962 L11.0056919,15.733 L16.1386919,4 Z M5.89628145,1.6047283 C6.16999928,1.77576619 6.13172615,2.05504566 5.96068826,2.32876349 C5.73263775,2.6937206 5.7756145,2.88160724 5.93530708,3.23512368 C6.42568244,3.16095005 7.14978568,3.35693375 7.56036243,3.61349058 C7.69722134,3.69900952 7.80557395,3.8301481 7.94243286,3.91566705 C8.25605,3.9213397 8.54116082,3.97263199 8.9003976,4.00681096 L9.38500494,3.2312771 C9.07663015,2.79610484 8.84851607,2.48441038 8.70066268,2.2961937 C8.47888259,2.01386869 9.31023224,1.32201362 9.57873345,1.70309072 C9.71741936,1.89992419 9.96660582,2.24544794 10.3262928,2.73966198 C10.430851,2.88332607 10.5329727,3.42440468 10.4654691,3.53243299 L10.0123346,4.25759977 C10.2974454,4.30889207 10.5084303,4.37729768 10.7194151,4.4457033 C10.9760197,4.54261523 11.2782438,4.66803348 11.5519616,4.83907137 L11.8488893,4.36388769 C11.9740073,4.16365719 12.4314004,4.13113915 12.6080709,4.16157747 C13.1652817,4.25757856 13.5849179,4.33128082 13.8669795,4.38268425 C14.4242185,4.48423653 14.0604111,5.4960867 13.718823,5.43188827 C13.4910975,5.38908932 13.1109498,5.32069959 12.5783797,5.2267191 L12.4073418,5.50043693 C12.68678,5.86534636 13.2181881,6.24296266 13.6811655,6.98663718 C13.3719223,7.58692704 12.5096505,9.26237536 11.09435,12.0129821 C10.3687536,12.1382775 8.95245279,11.8415191 8.53048305,11.7047078 L8.35944516,11.9784257 C8.63985361,12.343209 8.8679677,12.6549035 9.04378743,12.9135091 C9.30751703,13.3014174 8.41461192,13.8575606 8.16571666,13.506612 C8.03634338,13.3241925 7.78711967,12.9789829 7.41804555,12.4709835 C7.25974283,12.2530931 7.21380392,11.7815749 7.28247073,11.6716851 L7.56107764,11.2258208 C7.28735981,11.0547829 7.04214829,10.8381254 6.84255641,10.6499742 C6.64296453,10.461823 6.5174986,10.2565585 6.36352636,10.0969136 L5.84971054,10.9191908 C5.80243407,10.9948489 5.52907912,11.1209828 5.44267263,11.108334 C4.81589475,11.0165817 4.29416074,10.9228098 3.8774706,10.8270185 C3.35536732,10.6392609 3.55149489,9.78628232 4.02562714,9.77781449 C4.2610167,9.8090552 4.65066657,9.86223838 5.19457675,9.93736403 L5.67918409,9.16183016 C5.46247889,8.899553 5.31989964,8.62016251 5.17732038,8.34077202 C5.01195515,8.30087272 4.84658992,8.26097342 4.709731,8.17545447 C4.29915425,7.91889765 3.92856127,7.2481928 3.75175537,6.82055041 C3.31839264,6.80348476 3.13727717,6.94473989 2.90922666,7.309697 C2.73818877,7.58341484 2.46364498,7.8659871 2.18992715,7.69494921 C1.91620931,7.52391133 1.91695081,7.31908397 2.08798869,7.04536614 C2.48707709,6.40669119 2.90196341,6.09435049 3.63182995,6.04296284 C3.67740191,5.56398047 3.79709982,5.06788478 4.08216296,4.61168839 C4.36722611,4.155492 4.73213554,3.87605383 5.14266461,3.62512197 C4.86889909,2.9465954 4.87721463,2.47359794 5.27630303,1.834923 C5.44734091,1.56120516 5.62256361,1.43369042 5.89628145,1.6047283 Z M16.1386919,7 L14.1386919,12 L18.1386919,12 L16.1386919,7 Z M8.16878118,5.03529284 C8.00462988,5.67215311 7.76591557,6.23931272 7.45263826,6.73677168 C7.13936096,7.23423064 6.73633624,7.69375726 6.24356411,8.11535154 C6.92043783,9.241108 7.55106072,9.98835574 8.13543278,10.3570948 C8.68404895,10.7032718 9.22256472,11.0747691 10.0145932,10.9501066 C10.3872999,10.3853627 12.0504479,7.55314517 12.4594665,6.91035029 C11.7506156,6.20276679 11.3996595,5.78938078 10.8137513,5.51008723 C10.0440887,5.1432007 8.84604252,4.93245053 8.16878118,5.03529284 Z M4.99455574,5.18181467 C4.5099484,5.95734853 4.64118234,6.8639733 5.27985729,7.2630617 C5.41671621,7.34858064 5.46539716,7.34146992 5.61813643,7.24940571 C6.13459678,6.83479589 6.46809468,6.50703743 6.61863015,6.26613032 C6.77216204,6.02042794 6.93844402,5.59543292 7.11747611,4.99114525 C7.16180049,4.76065652 7.12709506,4.6114023 6.99023614,4.52588336 C6.3515612,4.12679496 5.47916308,4.40628081 4.99455574,5.18181467 Z" id="Combined-Shape" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.5 KiB |
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>icon / Environmental / Terrain World Renderer - box</title>
|
||||
<defs>
|
||||
<linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient-1">
|
||||
<stop stop-color="#ADADAD" stop-opacity="0" offset="0%"></stop>
|
||||
<stop stop-color="#9D9D9D" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
<path d="M20.924815,0 C23.1021676,0 23.9566542,1.77846722 23.9566542,3.01865122 C23.9566542,4.25883521 23.9566542,18.7815894 23.9566542,20.6420628 C23.9566542,22.3929727 22.7281352,23.9566542 20.924815,23.9566542 C19.1214948,23.9566542 4.64929841,23.9566542 2.80929064,23.9566542 C1.38661643,23.9566542 0,22.5147712 0,21.1586662 C0,19.8025612 0,4.03532882 0,2.67255351 C0,1.3097782 1.36539793,0 2.60955829,0 C3.85371865,0 18.9601214,0 20.924815,0 Z" id="path-2"></path>
|
||||
</defs>
|
||||
<g id="icon-/-Environmental-/-Terrain-World-Renderer---box" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="icon/general/color/-box-template">
|
||||
<mask id="mask-3" fill="white">
|
||||
<use xlink:href="#path-2"></use>
|
||||
</mask>
|
||||
<use id="Path-3" fill="url(#linearGradient-1)" xlink:href="#path-2"></use>
|
||||
<g id="Group" mask="url(#mask-3)" fill="#8B572A">
|
||||
<g id="icon/color/AI" style="mix-blend-mode: multiply;">
|
||||
<rect id="Lights" x="0" y="0" width="24" height="24"></rect>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<path d="M14.128,5.993 L14.1280665,8 L16.5280665,8 L16.528,7.493 L16.75,7.999 L16.5280665,8 L16.5280665,10.4 L17.8,10.399 L18.85,12.799 L16.5280665,12.8 L16.5280665,15.2 L18.9280665,15.2 L18.928,12.978 L19.9,15.199 L18.9280665,15.2 L18.9280665,17.6 L20.95,17.599 L22,20 L14.1280665,20 L14.1280665,17.6 L11.7280665,17.6 L11.7280665,20 L2,20 L8,8 L10.8,13.6 L12.2,10.399 L14.1280665,10.4 L14.1280665,8 L13.25,7.999 L14.128,5.993 Z M18.9280665,17.6 L16.5280665,17.6 L16.5280665,20 L18.9280665,20 L18.9280665,17.6 Z M16.5280665,15.2 L14.1280665,15.2 L14.1280665,17.6 L16.5280665,17.6 L16.5280665,15.2 Z M14.1280665,12.8 L11.7280665,12.8 L11.7280665,15.2 L14.1280665,15.2 L14.1280665,12.8 Z M8,11 L6,15 L10,15 L8,11 Z M16.5280665,10.4 L14.1280665,10.4 L14.1280665,12.8 L16.5280665,12.8 L16.5280665,10.4 Z M15,4 L15.7,5.599 L14.3,5.599 L15,4 Z" id="Combined-Shape-Copy" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
@@ -0,0 +1,8 @@
|
||||
#
|
||||
# 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
|
||||
#
|
||||
#
|
||||
|
||||
add_subdirectory(Code)
|
||||
@@ -0,0 +1,139 @@
|
||||
#
|
||||
# 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
|
||||
#
|
||||
#
|
||||
|
||||
ly_add_target(
|
||||
NAME Terrain.Static STATIC
|
||||
NAMESPACE Gem
|
||||
FILES_CMAKE
|
||||
terrain_files.cmake
|
||||
INCLUDE_DIRECTORIES
|
||||
PUBLIC
|
||||
Include
|
||||
PRIVATE
|
||||
Source
|
||||
BUILD_DEPENDENCIES
|
||||
PUBLIC
|
||||
AZ::AzCore
|
||||
AZ::AzFramework
|
||||
Gem::Atom_RPI.Public
|
||||
Gem::Atom_Utils.Static
|
||||
Gem::GradientSignal
|
||||
Gem::SurfaceData
|
||||
Gem::LmbrCentral
|
||||
)
|
||||
|
||||
ly_add_target(
|
||||
NAME Terrain ${PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE}
|
||||
NAMESPACE Gem
|
||||
FILES_CMAKE
|
||||
terrain_shared_files.cmake
|
||||
INCLUDE_DIRECTORIES
|
||||
PUBLIC
|
||||
Include
|
||||
PRIVATE
|
||||
Source
|
||||
BUILD_DEPENDENCIES
|
||||
PRIVATE
|
||||
Gem::Terrain.Static
|
||||
Gem::LmbrCentral
|
||||
RUNTIME_DEPENDENCIES
|
||||
Gem::LmbrCentral
|
||||
)
|
||||
|
||||
# the above module is for use in all client/server types
|
||||
ly_create_alias(NAME Terrain.Servers NAMESPACE Gem TARGETS Gem::Terrain)
|
||||
ly_create_alias(NAME Terrain.Clients NAMESPACE Gem TARGETS Gem::Terrain)
|
||||
|
||||
# If we are on a host platform, we want to add the host tools targets like the Terrain.Editor target which
|
||||
# will also depend on Terrain.Static
|
||||
if(PAL_TRAIT_BUILD_HOST_TOOLS)
|
||||
ly_add_target(
|
||||
NAME Terrain.Editor MODULE
|
||||
NAMESPACE Gem
|
||||
AUTOMOC
|
||||
OUTPUT_NAME Gem.Terrain.Editor
|
||||
FILES_CMAKE
|
||||
terrain_editor_shared_files.cmake
|
||||
COMPILE_DEFINITIONS
|
||||
PRIVATE
|
||||
TERRAIN_EDITOR
|
||||
INCLUDE_DIRECTORIES
|
||||
PRIVATE
|
||||
Source
|
||||
PUBLIC
|
||||
Include
|
||||
BUILD_DEPENDENCIES
|
||||
PUBLIC
|
||||
AZ::AzToolsFramework
|
||||
Gem::GradientSignal
|
||||
Gem::LmbrCentral
|
||||
Gem::Terrain.Static
|
||||
)
|
||||
|
||||
# the above module is for use in dev tool situations
|
||||
ly_create_alias(NAME Terrain.Builders NAMESPACE Gem TARGETS Gem::Terrain.Editor)
|
||||
ly_create_alias(NAME Terrain.Tools NAMESPACE Gem TARGETS Gem::Terrain.Editor)
|
||||
endif()
|
||||
|
||||
################################################################################
|
||||
# Tests
|
||||
################################################################################
|
||||
# See if globally, tests are supported
|
||||
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
# We globally support tests, see if we support tests on this platform for Terrain.Static
|
||||
if(PAL_TRAIT_TERRAIN_TEST_SUPPORTED)
|
||||
# We support Terrain.Tests on this platform, add Terrain.Tests target which depends on Terrain.Static
|
||||
ly_add_target(
|
||||
NAME Terrain.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
|
||||
NAMESPACE Gem
|
||||
FILES_CMAKE
|
||||
terrain_files.cmake
|
||||
terrain_tests_files.cmake
|
||||
INCLUDE_DIRECTORIES
|
||||
PRIVATE
|
||||
Tests
|
||||
Source
|
||||
BUILD_DEPENDENCIES
|
||||
PRIVATE
|
||||
AZ::AzTest
|
||||
AZ::AzFramework
|
||||
Gem::Terrain.Static
|
||||
)
|
||||
|
||||
# Add Terrain.Tests to googletest
|
||||
ly_add_googletest(
|
||||
NAME Gem::Terrain.Tests
|
||||
)
|
||||
endif()
|
||||
|
||||
# If we are a host platform we want to add tools test like editor tests here
|
||||
if(PAL_TRAIT_BUILD_HOST_TOOLS)
|
||||
# We are a host platform, see if Editor tests are supported on this platform
|
||||
if(PAL_TRAIT_TERRAIN_EDITOR_TEST_SUPPORTED)
|
||||
# We support Terrain.Editor.Tests on this platform, add Terrain.Editor.Tests target which depends on Terrain.Editor
|
||||
ly_add_target(
|
||||
NAME Terrain.Editor.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
|
||||
NAMESPACE Gem
|
||||
FILES_CMAKE
|
||||
terrain_editor_tests_files.cmake
|
||||
INCLUDE_DIRECTORIES
|
||||
PRIVATE
|
||||
Tests
|
||||
Source
|
||||
BUILD_DEPENDENCIES
|
||||
PRIVATE
|
||||
AZ::AzTest
|
||||
Gem::Terrain.Editor
|
||||
)
|
||||
|
||||
# Add Terrain.Editor.Tests to googletest
|
||||
ly_add_googletest(
|
||||
NAME Gem::Terrain.Editor.Tests
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
@@ -6,7 +6,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <TerrainSurfaceDataSystemComponent.h>
|
||||
#include <Components/TerrainSurfaceDataSystemComponent.h>
|
||||
#include <AzCore/Debug/Profiler.h>
|
||||
#include <AzCore/Math/MathUtils.h>
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
@@ -58,7 +58,7 @@ namespace Terrain
|
||||
editContext->Class<TerrainSurfaceDataSystemComponent>("Terrain Surface Data System", "Manages surface data requests against legacy terrain")
|
||||
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
||||
->Attribute(AZ::Edit::Attributes::Category, "Surface Data")
|
||||
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("System", 0xc94d118b))
|
||||
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("System"))
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
||||
->DataElement(0, &TerrainSurfaceDataSystemComponent::m_configuration, "Configuration", "")
|
||||
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
|
||||
@@ -78,18 +78,18 @@ namespace Terrain
|
||||
|
||||
void TerrainSurfaceDataSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services)
|
||||
{
|
||||
services.push_back(AZ_CRC("SurfaceDataProviderService", 0xfe9fb95e));
|
||||
services.push_back(AZ_CRC("TerrainSurfaceDataProviderService", 0xa1ac7717));
|
||||
services.push_back(AZ_CRC_CE("SurfaceDataProviderService"));
|
||||
services.push_back(AZ_CRC_CE("TerrainSurfaceDataProviderService"));
|
||||
}
|
||||
|
||||
void TerrainSurfaceDataSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services)
|
||||
{
|
||||
services.push_back(AZ_CRC("TerrainSurfaceDataProviderService", 0xa1ac7717));
|
||||
services.push_back(AZ_CRC_CE("TerrainSurfaceDataProviderService"));
|
||||
}
|
||||
|
||||
void TerrainSurfaceDataSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services)
|
||||
{
|
||||
services.push_back(AZ_CRC("SurfaceDataSystemService", 0x1d44d25f));
|
||||
services.push_back(AZ_CRC_CE("SurfaceDataSystemService"));
|
||||
}
|
||||
|
||||
void TerrainSurfaceDataSystemComponent::Activate()
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <Components/TerrainSystemComponent.h>
|
||||
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
#include <AzCore/Serialization/EditContextConstants.inl>
|
||||
|
||||
#include <Atom/RPI.Public/FeatureProcessorFactory.h>
|
||||
|
||||
namespace Terrain
|
||||
{
|
||||
void TerrainSystemComponent::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serialize->Class<TerrainSystemComponent, AZ::Component>()
|
||||
->Version(0);
|
||||
|
||||
if (AZ::EditContext* ec = serialize->GetEditContext())
|
||||
{
|
||||
ec->Class<TerrainSystemComponent>("Terrain", "The Terrain System Component enables Terrain.")
|
||||
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
||||
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("System"))
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TerrainSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
|
||||
{
|
||||
provided.push_back(AZ_CRC_CE("TerrainService"));
|
||||
}
|
||||
|
||||
void TerrainSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
|
||||
{
|
||||
incompatible.push_back(AZ_CRC_CE("TerrainService"));
|
||||
}
|
||||
|
||||
void TerrainSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
|
||||
{
|
||||
required.push_back(AZ_CRC_CE("RPISystem"));
|
||||
}
|
||||
|
||||
void TerrainSystemComponent::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent)
|
||||
{
|
||||
}
|
||||
|
||||
void TerrainSystemComponent::Init()
|
||||
{
|
||||
}
|
||||
|
||||
void TerrainSystemComponent::Activate()
|
||||
{
|
||||
}
|
||||
|
||||
void TerrainSystemComponent::Deactivate()
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Component/Component.h>
|
||||
|
||||
namespace Terrain
|
||||
{
|
||||
class TerrainSystem;
|
||||
|
||||
class TerrainSystemComponent
|
||||
: public AZ::Component
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(TerrainSystemComponent, "{CD5A517E-3BD8-49AE-8F9B-33C6FC47EC67}");
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
|
||||
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
|
||||
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
|
||||
static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent);
|
||||
|
||||
protected:
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// AZ::Component interface implementation
|
||||
void Init() override;
|
||||
void Activate() override;
|
||||
void Deactivate() override;
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
TerrainSystem* m_terrainSystem{ nullptr };
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <EditorComponents/EditorTerrainSystemComponent.h>
|
||||
|
||||
namespace Terrain
|
||||
{
|
||||
void EditorTerrainSystemComponent::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<EditorTerrainSystemComponent, AZ::Component>()->Version(1);
|
||||
}
|
||||
}
|
||||
|
||||
void EditorTerrainSystemComponent::Activate()
|
||||
{
|
||||
AzToolsFramework::EditorEvents::Bus::Handler::BusConnect();
|
||||
}
|
||||
|
||||
void EditorTerrainSystemComponent::Deactivate()
|
||||
{
|
||||
AzToolsFramework::EditorEvents::Bus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
} // namespace Terrain
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Component/Component.h>
|
||||
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
|
||||
|
||||
namespace Terrain
|
||||
{
|
||||
/// System component for Terrain editor
|
||||
class EditorTerrainSystemComponent
|
||||
: public AZ::Component
|
||||
, private AzToolsFramework::EditorEvents::Bus::Handler
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(EditorTerrainSystemComponent, "{5E9f2200-9099-4325-BABD-6A533A1ABEA8}");
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
EditorTerrainSystemComponent() = default;
|
||||
|
||||
private:
|
||||
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
|
||||
{
|
||||
provided.push_back(AZ_CRC_CE("TerrainEditorService"));
|
||||
}
|
||||
|
||||
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
|
||||
{
|
||||
required.push_back(AZ_CRC_CE("TerrainService"));
|
||||
}
|
||||
|
||||
// AZ::Component
|
||||
void Activate() override;
|
||||
void Deactivate() override;
|
||||
};
|
||||
} // namespace Terrain
|
||||