Merge branch 'development' into o3de_sdk/installer_configs
Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com>
This commit is contained in:
@@ -254,4 +254,35 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
ly_add_googletest(
|
||||
NAME Legacy::EditorLib.Tests
|
||||
)
|
||||
|
||||
ly_add_target(
|
||||
NAME EditorLib.Camera.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
|
||||
NAMESPACE Legacy
|
||||
FILES_CMAKE
|
||||
Lib/Tests/Camera/editor_lib_camera_test_files.cmake
|
||||
INCLUDE_DIRECTORIES
|
||||
PRIVATE
|
||||
.
|
||||
BUILD_DEPENDENCIES
|
||||
PRIVATE
|
||||
AZ::AzCore
|
||||
AZ::AzTest
|
||||
AZ::AzToolsFramework
|
||||
AZ::AzTestShared
|
||||
Legacy::EditorLib
|
||||
Gem::Camera.Editor
|
||||
Gem::AtomToolsFramework.Static
|
||||
RUNTIME_DEPENDENCIES
|
||||
Legacy::EditorLib
|
||||
)
|
||||
|
||||
ly_add_source_properties(
|
||||
SOURCES Lib/Tests/Camera/test_EditorCamera.cpp
|
||||
PROPERTY COMPILE_DEFINITIONS
|
||||
VALUES CAMERA_EDITOR_MODULE="$<TARGET_FILE_BASE_NAME:Camera.Editor>"
|
||||
)
|
||||
|
||||
ly_add_googletest(
|
||||
NAME Legacy::EditorLib.Camera.Tests
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <EditorModularViewportCameraComposer.h>
|
||||
|
||||
#include <AtomToolsFramework/Viewport/ModularViewportCameraControllerRequestBus.h>
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
#include <AzFramework/Render/IntersectorInterface.h>
|
||||
#include <AzToolsFramework/Viewport/ViewportMessages.h>
|
||||
@@ -34,10 +35,12 @@ namespace SandboxEditor
|
||||
: m_viewportId(viewportId)
|
||||
{
|
||||
EditorModularViewportCameraComposerNotificationBus::Handler::BusConnect(viewportId);
|
||||
Camera::EditorCameraNotificationBus::Handler::BusConnect();
|
||||
}
|
||||
|
||||
EditorModularViewportCameraComposer::~EditorModularViewportCameraComposer()
|
||||
{
|
||||
Camera::EditorCameraNotificationBus::Handler::BusDisconnect();
|
||||
EditorModularViewportCameraComposerNotificationBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
@@ -283,4 +286,22 @@ namespace SandboxEditor
|
||||
m_orbitCamera->SetOrbitInputChannelId(SandboxEditor::CameraOrbitChannelId());
|
||||
m_orbitDollyMoveCamera->SetDollyInputChannelId(SandboxEditor::CameraOrbitDollyChannelId());
|
||||
}
|
||||
|
||||
void EditorModularViewportCameraComposer::OnViewportViewEntityChanged(const AZ::EntityId& viewEntityId)
|
||||
{
|
||||
if (viewEntityId.IsValid())
|
||||
{
|
||||
AZ::Transform worldFromLocal = AZ::Transform::CreateIdentity();
|
||||
AZ::TransformBus::EventResult(worldFromLocal, viewEntityId, &AZ::TransformBus::Events::GetWorldTM);
|
||||
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
|
||||
m_viewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::SetReferenceFrame,
|
||||
worldFromLocal);
|
||||
}
|
||||
else
|
||||
{
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
|
||||
m_viewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::ClearReferenceFrame);
|
||||
}
|
||||
}
|
||||
} // namespace SandboxEditor
|
||||
|
||||
@@ -10,13 +10,16 @@
|
||||
|
||||
#include <AtomToolsFramework/Viewport/ModularViewportCameraController.h>
|
||||
#include <AzFramework/Viewport/CameraInput.h>
|
||||
#include <AzToolsFramework/API/EditorCameraBus.h>
|
||||
#include <EditorModularViewportCameraComposerBus.h>
|
||||
#include <SandboxAPI.h>
|
||||
|
||||
namespace SandboxEditor
|
||||
{
|
||||
//! Type responsible for building the editor's modular viewport camera controller.
|
||||
class EditorModularViewportCameraComposer : private EditorModularViewportCameraComposerNotificationBus::Handler
|
||||
class EditorModularViewportCameraComposer
|
||||
: private EditorModularViewportCameraComposerNotificationBus::Handler
|
||||
, private Camera::EditorCameraNotificationBus::Handler
|
||||
{
|
||||
public:
|
||||
SANDBOX_API explicit EditorModularViewportCameraComposer(AzFramework::ViewportId viewportId);
|
||||
@@ -32,6 +35,9 @@ namespace SandboxEditor
|
||||
// EditorModularViewportCameraComposerNotificationBus overrides ...
|
||||
void OnEditorModularViewportCameraComposerSettingsChanged() override;
|
||||
|
||||
// EditorCameraNotificationBus overrides ...
|
||||
void OnViewportViewEntityChanged(const AZ::EntityId& viewEntityId) override;
|
||||
|
||||
AZStd::shared_ptr<AzFramework::RotateCameraInput> m_firstPersonRotateCamera;
|
||||
AZStd::shared_ptr<AzFramework::PanCameraInput> m_firstPersonPanCamera;
|
||||
AZStd::shared_ptr<AzFramework::TranslateCameraInput> m_firstPersonTranslateCamera;
|
||||
|
||||
+2
-1
@@ -6,5 +6,6 @@
|
||||
#
|
||||
#
|
||||
|
||||
set(GEM_DEPENDENCIES
|
||||
set(FILES
|
||||
test_EditorCamera.cpp
|
||||
)
|
||||
@@ -0,0 +1,225 @@
|
||||
/*
|
||||
* 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 <AZTestShared/Math/MathTestHelpers.h>
|
||||
#include <AtomToolsFramework/Viewport/ModularViewportCameraController.h>
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
#include <AzFramework/Viewport/ViewportControllerList.h>
|
||||
#include <AzTest/GemTestEnvironment.h>
|
||||
#include <AzToolsFramework/API/EditorCameraBus.h>
|
||||
#include <AzToolsFramework/ToolsComponents/TransformComponent.h>
|
||||
#include <EditorModularViewportCameraComposer.h>
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
class EditorCameraTestEnvironment : public AZ::Test::GemTestEnvironment
|
||||
{
|
||||
// AZ::Test::GemTestEnvironment overrides ...
|
||||
void AddGemsAndComponents() override;
|
||||
};
|
||||
|
||||
void EditorCameraTestEnvironment::AddGemsAndComponents()
|
||||
{
|
||||
AddDynamicModulePaths({ CAMERA_EDITOR_MODULE });
|
||||
AddComponentDescriptors({ AzToolsFramework::Components::TransformComponent::CreateDescriptor() });
|
||||
}
|
||||
|
||||
class EditorCameraFixture : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
AtomToolsFramework::ModularCameraViewportContext* m_cameraViewportContextView = nullptr;
|
||||
AZStd::unique_ptr<SandboxEditor::EditorModularViewportCameraComposer> m_editorModularViewportCameraComposer;
|
||||
AZStd::unique_ptr<AZ::DynamicModuleHandle> m_editorLibHandle;
|
||||
AzFramework::ViewportControllerListPtr m_controllerList;
|
||||
AZStd::unique_ptr<AZ::Entity> m_entity;
|
||||
|
||||
static const AzFramework::ViewportId TestViewportId;
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
m_editorLibHandle = AZ::DynamicModuleHandle::Create("EditorLib");
|
||||
[[maybe_unused]] const bool loaded = m_editorLibHandle->Load(true);
|
||||
AZ_Assert(loaded, "EditorLib could not be loaded");
|
||||
|
||||
m_controllerList = AZStd::make_shared<AzFramework::ViewportControllerList>();
|
||||
m_controllerList->RegisterViewportContext(TestViewportId);
|
||||
|
||||
m_entity = AZStd::make_unique<AZ::Entity>();
|
||||
m_entity->Init();
|
||||
m_entity->CreateComponent<AzToolsFramework::Components::TransformComponent>();
|
||||
m_entity->Activate();
|
||||
|
||||
m_editorModularViewportCameraComposer = AZStd::make_unique<SandboxEditor::EditorModularViewportCameraComposer>(TestViewportId);
|
||||
|
||||
auto controller = m_editorModularViewportCameraComposer->CreateModularViewportCameraController();
|
||||
// set some overrides for the test
|
||||
controller->SetCameraViewportContextBuilderCallback(
|
||||
[this](AZStd::unique_ptr<AtomToolsFramework::ModularCameraViewportContext>& cameraViewportContext) mutable
|
||||
{
|
||||
cameraViewportContext = AZStd::make_unique<AtomToolsFramework::PlaceholderModularCameraViewportContextImpl>();
|
||||
m_cameraViewportContextView = cameraViewportContext.get();
|
||||
});
|
||||
|
||||
m_controllerList->Add(controller);
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
m_editorModularViewportCameraComposer.reset();
|
||||
m_cameraViewportContextView = nullptr;
|
||||
m_entity.reset();
|
||||
m_editorLibHandle = {};
|
||||
}
|
||||
};
|
||||
|
||||
const AzFramework::ViewportId EditorCameraFixture::TestViewportId = AzFramework::ViewportId(1337);
|
||||
|
||||
TEST_F(EditorCameraFixture, ModularViewportCameraControllerReferenceFrameUpdatedWhenViewportEntityisChanged)
|
||||
{
|
||||
// Given
|
||||
const auto entityTransform = AZ::Transform::CreateFromQuaternionAndTranslation(
|
||||
AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)), AZ::Vector3(10.0f, 5.0f, -2.0f));
|
||||
AZ::TransformBus::Event(m_entity->GetId(), &AZ::TransformBus::Events::SetWorldTM, entityTransform);
|
||||
|
||||
// When
|
||||
// imitate viewport entity changing
|
||||
Camera::EditorCameraNotificationBus::Broadcast(
|
||||
&Camera::EditorCameraNotificationBus::Events::OnViewportViewEntityChanged, m_entity->GetId());
|
||||
|
||||
// ensure the viewport updates after the viewport view entity change
|
||||
const float deltaTime = 1.0f / 60.0f;
|
||||
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(deltaTime), AZ::ScriptTimePoint() });
|
||||
|
||||
// retrieve updated camera transform
|
||||
const AZ::Transform cameraTransform = m_cameraViewportContextView->GetCameraTransform();
|
||||
|
||||
// Then
|
||||
// camera transform matches that of the entity
|
||||
EXPECT_THAT(cameraTransform, IsClose(entityTransform));
|
||||
}
|
||||
|
||||
TEST_F(EditorCameraFixture, ReferenceFrameRemainsIdentityAfterExternalCameraTransformChangeWhenNotSet)
|
||||
{
|
||||
// Given
|
||||
m_cameraViewportContextView->SetCameraTransform(AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 20.0f, 30.0f)));
|
||||
|
||||
// When
|
||||
AZ::Transform referenceFrame = AZ::Transform::CreateTranslation(AZ::Vector3(1.0f, 2.0f, 3.0f));
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
|
||||
referenceFrame, TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::GetReferenceFrame);
|
||||
|
||||
// Then
|
||||
// reference frame is still the identity
|
||||
EXPECT_THAT(referenceFrame, IsClose(AZ::Transform::CreateIdentity()));
|
||||
}
|
||||
|
||||
TEST_F(EditorCameraFixture, ExternalCameraTransformChangeWhenReferenceFrameIsSetUpdatesReferenceFrame)
|
||||
{
|
||||
// Given
|
||||
const AZ::Transform referenceFrame = AZ::Transform::CreateFromQuaternionAndTranslation(
|
||||
AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)), AZ::Vector3(1.0f, 2.0f, 3.0f));
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
|
||||
TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::SetReferenceFrame, referenceFrame);
|
||||
|
||||
const AZ::Transform nextTransform = AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 20.0f, 30.0f));
|
||||
m_cameraViewportContextView->SetCameraTransform(nextTransform);
|
||||
|
||||
// When
|
||||
AZ::Transform currentReferenceFrame = AZ::Transform::CreateTranslation(AZ::Vector3(1.0f, 2.0f, 3.0f));
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
|
||||
currentReferenceFrame, TestViewportId,
|
||||
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::GetReferenceFrame);
|
||||
|
||||
// Then
|
||||
EXPECT_THAT(currentReferenceFrame, IsClose(nextTransform));
|
||||
}
|
||||
|
||||
TEST_F(EditorCameraFixture, ReferenceFrameReturnedToIdentityAfterClear)
|
||||
{
|
||||
// Given
|
||||
const AZ::Transform referenceFrame = AZ::Transform::CreateFromQuaternionAndTranslation(
|
||||
AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)), AZ::Vector3(1.0f, 2.0f, 3.0f));
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
|
||||
TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::SetReferenceFrame, referenceFrame);
|
||||
|
||||
// When
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
|
||||
TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::ClearReferenceFrame);
|
||||
|
||||
AZ::Transform currentReferenceFrame = AZ::Transform::CreateTranslation(AZ::Vector3(1.0f, 2.0f, 3.0f));
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
|
||||
currentReferenceFrame, TestViewportId,
|
||||
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::GetReferenceFrame);
|
||||
|
||||
// Then
|
||||
EXPECT_THAT(currentReferenceFrame, IsClose(AZ::Transform::CreateIdentity()));
|
||||
}
|
||||
|
||||
TEST_F(EditorCameraFixture, InterpolateToTransform)
|
||||
{
|
||||
// When
|
||||
AZ::Transform transformToInterpolateTo = AZ::Transform::CreateFromQuaternionAndTranslation(
|
||||
AZ::Quaternion::CreateRotationZ(AZ::DegToRad(90.0f)), AZ::Vector3(20.0f, 40.0f, 60.0f));
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
|
||||
TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform,
|
||||
transformToInterpolateTo, 0.0f);
|
||||
|
||||
// simulate interpolation
|
||||
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(0.5f), AZ::ScriptTimePoint() });
|
||||
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(0.5f), AZ::ScriptTimePoint() });
|
||||
|
||||
const auto finalTransform = m_cameraViewportContextView->GetCameraTransform();
|
||||
|
||||
// Then
|
||||
EXPECT_THAT(finalTransform, IsClose(transformToInterpolateTo));
|
||||
}
|
||||
|
||||
TEST_F(EditorCameraFixture, InterpolateToTransformWithReferenceSpaceSet)
|
||||
{
|
||||
// Given
|
||||
const AZ::Transform referenceFrame = AZ::Transform::CreateFromQuaternionAndTranslation(
|
||||
AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)), AZ::Vector3(1.0f, 2.0f, 3.0f));
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
|
||||
TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::SetReferenceFrame, referenceFrame);
|
||||
|
||||
AZ::Transform transformToInterpolateTo = AZ::Transform::CreateFromQuaternionAndTranslation(
|
||||
AZ::Quaternion::CreateRotationZ(AZ::DegToRad(90.0f)), AZ::Vector3(20.0f, 40.0f, 60.0f));
|
||||
|
||||
// When
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
|
||||
TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform,
|
||||
transformToInterpolateTo, 0.0f);
|
||||
|
||||
// simulate interpolation
|
||||
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(0.5f), AZ::ScriptTimePoint() });
|
||||
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(0.5f), AZ::ScriptTimePoint() });
|
||||
|
||||
AZ::Transform currentReferenceFrame = AZ::Transform::CreateTranslation(AZ::Vector3(1.0f, 2.0f, 3.0f));
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
|
||||
currentReferenceFrame, TestViewportId,
|
||||
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::GetReferenceFrame);
|
||||
|
||||
const auto finalTransform = m_cameraViewportContextView->GetCameraTransform();
|
||||
|
||||
// Then
|
||||
EXPECT_THAT(finalTransform, IsClose(transformToInterpolateTo));
|
||||
EXPECT_THAT(currentReferenceFrame, IsClose(AZ::Transform::CreateIdentity()));
|
||||
}
|
||||
} // namespace UnitTest
|
||||
|
||||
// required to support running integration tests with the Camera Gem
|
||||
AZTEST_EXPORT int AZ_UNIT_TEST_HOOK_NAME(int argc, char** argv)
|
||||
{
|
||||
::testing::InitGoogleMock(&argc, argv);
|
||||
AZ::Test::printUnusedParametersWarning(argc, argv);
|
||||
AZ::Test::addTestEnvironments({ new UnitTest::EditorCameraTestEnvironment() });
|
||||
int result = RUN_ALL_TESTS();
|
||||
return result;
|
||||
}
|
||||
|
||||
IMPLEMENT_TEST_EXECUTABLE_MAIN();
|
||||
@@ -58,28 +58,6 @@ namespace UnitTest
|
||||
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:
|
||||
@@ -146,7 +124,7 @@ namespace UnitTest
|
||||
controller->SetCameraViewportContextBuilderCallback(
|
||||
[this](AZStd::unique_ptr<AtomToolsFramework::ModularCameraViewportContext>& cameraViewportContext)
|
||||
{
|
||||
cameraViewportContext = AZStd::make_unique<TestModularCameraViewportContextImpl>();
|
||||
cameraViewportContext = AZStd::make_unique<AtomToolsFramework::PlaceholderModularCameraViewportContextImpl>();
|
||||
m_cameraViewportContextView = cameraViewportContext.get();
|
||||
});
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <AzToolsFramework/ToolsComponents/TransformComponent.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextComponent.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityInfoBus.h>
|
||||
#include <AzToolsFramework/FocusMode/FocusModeSystemComponent.h>
|
||||
#include <AzToolsFramework/Slice/SliceMetadataEntityContextComponent.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabSystemComponent.h>
|
||||
#include <AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h>
|
||||
@@ -248,6 +249,7 @@ namespace AzToolsFramework
|
||||
components.insert(components.end(), {
|
||||
azrtti_typeid<EditorEntityContextComponent>(),
|
||||
azrtti_typeid<Components::EditorEntityUiSystemComponent>(),
|
||||
azrtti_typeid<FocusModeSystemComponent>(),
|
||||
azrtti_typeid<SliceMetadataEntityContextComponent>(),
|
||||
azrtti_typeid<Prefab::PrefabSystemComponent>(),
|
||||
azrtti_typeid<EditorEntityFixupComponent>(),
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <AzToolsFramework/Entity/EditorEntityModelComponent.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntitySearchComponent.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntitySortComponent.h>
|
||||
#include <AzToolsFramework/FocusMode/FocusModeSystemComponent.h>
|
||||
#include <AzToolsFramework/PropertyTreeEditor/PropertyTreeEditorComponent.h>
|
||||
#include <AzToolsFramework/Render/EditorIntersectorComponent.h>
|
||||
#include <AzToolsFramework/Slice/SliceDependencyBrowserComponent.h>
|
||||
@@ -69,6 +70,7 @@ namespace AzToolsFramework
|
||||
Components::EditorSelectionAccentSystemComponent::CreateDescriptor(),
|
||||
EditorEntityContextComponent::CreateDescriptor(),
|
||||
EditorEntityFixupComponent::CreateDescriptor(),
|
||||
FocusModeSystemComponent::CreateDescriptor(),
|
||||
SliceMetadataEntityContextComponent::CreateDescriptor(),
|
||||
SliceRequestComponent::CreateDescriptor(),
|
||||
Prefab::PrefabSystemComponent::CreateDescriptor(),
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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/Interface/Interface.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
//! FocusModeInterface
|
||||
//! Interface to handle the Editor Focus Mode.
|
||||
class FocusModeInterface
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(FocusModeInterface, "{437243B0-F86B-422F-B7B8-4A21CC000702}");
|
||||
|
||||
//! Sets the root entity the Editor should focus on.
|
||||
//! The Editor will only allow the user to select entities that are descendants of the EntityId provided.
|
||||
//! @param entityId The entityId that will become the new focus root.
|
||||
virtual void SetFocusRoot(AZ::EntityId entityId) = 0;
|
||||
|
||||
//! Clears the Editor focus, allowing the user to select the whole level again.
|
||||
virtual void ClearFocusRoot() = 0;
|
||||
|
||||
//! Returns the entity id of the root of the current Editor focus.
|
||||
//! @return The entity id of the root of the Editor focus, or an invalid entity id if no focus is set.
|
||||
virtual AZ::EntityId GetFocusRoot() = 0;
|
||||
|
||||
//! Returns whether the entity id provided is part of the focused sub-tree.
|
||||
virtual bool IsInFocusSubTree(AZ::EntityId entityId) = 0;
|
||||
};
|
||||
|
||||
} // namespace AzToolsFramework
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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/Component/TransformBus.h>
|
||||
|
||||
#include <AzToolsFramework/API/ViewportEditorModeTrackerInterface.h>
|
||||
#include <AzToolsFramework/FocusMode/FocusModeSystemComponent.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
bool IsInFocusSubTree(AZ::EntityId entityId, AZ::EntityId focusRootId)
|
||||
{
|
||||
if (entityId == AZ::EntityId())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (entityId == focusRootId)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
AZ::EntityId parentId;
|
||||
AZ::TransformBus::EventResult(parentId, entityId, &AZ::TransformInterface::GetParentId);
|
||||
|
||||
return IsInFocusSubTree(parentId, focusRootId);
|
||||
}
|
||||
|
||||
void FocusModeSystemComponent::Init()
|
||||
{
|
||||
}
|
||||
|
||||
void FocusModeSystemComponent::Activate()
|
||||
{
|
||||
AZ::Interface<FocusModeInterface>::Register(this);
|
||||
}
|
||||
|
||||
void FocusModeSystemComponent::Deactivate()
|
||||
{
|
||||
AZ::Interface<FocusModeInterface>::Unregister(this);
|
||||
}
|
||||
|
||||
void FocusModeSystemComponent::Reflect([[maybe_unused]] AZ::ReflectContext* context)
|
||||
{
|
||||
}
|
||||
|
||||
void FocusModeSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
|
||||
{
|
||||
provided.push_back(AZ_CRC_CE("EditorFocusMode"));
|
||||
}
|
||||
|
||||
void FocusModeSystemComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required)
|
||||
{
|
||||
}
|
||||
|
||||
void FocusModeSystemComponent::GetIncompatibleServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& incompatible)
|
||||
{
|
||||
}
|
||||
|
||||
void FocusModeSystemComponent::SetFocusRoot(AZ::EntityId entityId)
|
||||
{
|
||||
m_focusRoot = entityId;
|
||||
|
||||
// TODO - If m_focusRoot != AZ::EntityId(), activate focus mode via ViewportEditorModeTrackerInterface; else, deactivate focus mode
|
||||
}
|
||||
|
||||
void FocusModeSystemComponent::ClearFocusRoot()
|
||||
{
|
||||
SetFocusRoot(AZ::EntityId());
|
||||
}
|
||||
|
||||
AZ::EntityId FocusModeSystemComponent::GetFocusRoot()
|
||||
{
|
||||
return m_focusRoot;
|
||||
}
|
||||
|
||||
bool FocusModeSystemComponent::IsInFocusSubTree(AZ::EntityId entityId)
|
||||
{
|
||||
if (m_focusRoot == AZ::EntityId())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return AzToolsFramework::IsInFocusSubTree(entityId, m_focusRoot);
|
||||
}
|
||||
|
||||
} // namespace AzToolsFramework
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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 <AzCore/Memory/SystemAllocator.h>
|
||||
|
||||
#include <AzToolsFramework/FocusMode/FocusModeInterface.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
bool IsInFocusSubTree(AZ::EntityId entityId, AZ::EntityId focusRootId);
|
||||
|
||||
//! System Component to handle the Editor Focus Mode system
|
||||
class FocusModeSystemComponent final
|
||||
: public AZ::Component
|
||||
, private FocusModeInterface
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(FocusModeSystemComponent, "{6CE522FE-2057-4794-BD05-61E04BD8EA30}");
|
||||
|
||||
FocusModeSystemComponent() = default;
|
||||
virtual ~FocusModeSystemComponent() = default;
|
||||
|
||||
// AZ::Component overrides ...
|
||||
void Init() override;
|
||||
void Activate() override;
|
||||
void Deactivate() override;
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
|
||||
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
|
||||
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
|
||||
|
||||
// FocusModeInterface overrides ...
|
||||
void SetFocusRoot(AZ::EntityId entityId) override;
|
||||
void ClearFocusRoot() override;
|
||||
AZ::EntityId GetFocusRoot() override;
|
||||
bool IsInFocusSubTree(AZ::EntityId entityId) override;
|
||||
|
||||
private:
|
||||
AZ::EntityId m_focusRoot;
|
||||
};
|
||||
|
||||
} // namespace AzToolsFramework
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* 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 <AzToolsFramework/Prefab/PrefabFocusHandler.h>
|
||||
|
||||
#include <AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/InstanceEntityMapperInterface.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab
|
||||
{
|
||||
PrefabFocusHandler::PrefabFocusHandler()
|
||||
{
|
||||
m_instanceEntityMapperInterface = AZ::Interface<InstanceEntityMapperInterface>::Get();
|
||||
AZ_Assert(
|
||||
m_instanceEntityMapperInterface,
|
||||
"Prefab - PrefabFocusHandler - "
|
||||
"Instance Entity Mapper Interface could not be found. "
|
||||
"Check that it is being correctly initialized.");
|
||||
|
||||
AZ::Interface<PrefabFocusInterface>::Register(this);
|
||||
}
|
||||
|
||||
PrefabFocusHandler::~PrefabFocusHandler()
|
||||
{
|
||||
AZ::Interface<PrefabFocusInterface>::Unregister(this);
|
||||
}
|
||||
|
||||
PrefabFocusOperationResult PrefabFocusHandler::FocusOnOwningPrefab(AZ::EntityId entityId)
|
||||
{
|
||||
InstanceOptionalReference focusedInstance;
|
||||
|
||||
if (entityId == AZ::EntityId())
|
||||
{
|
||||
PrefabEditorEntityOwnershipInterface* prefabEditorEntityOwnershipInterface =
|
||||
AZ::Interface<PrefabEditorEntityOwnershipInterface>::Get();
|
||||
|
||||
if(!prefabEditorEntityOwnershipInterface)
|
||||
{
|
||||
return AZ::Failure(AZStd::string("Could not focus on root prefab instance - internal error "
|
||||
"(PrefabEditorEntityOwnershipInterface unavailable)."));
|
||||
}
|
||||
|
||||
focusedInstance = prefabEditorEntityOwnershipInterface->GetRootPrefabInstance();
|
||||
}
|
||||
else
|
||||
{
|
||||
focusedInstance = m_instanceEntityMapperInterface->FindOwningInstance(entityId);
|
||||
}
|
||||
|
||||
if (!focusedInstance.has_value())
|
||||
{
|
||||
return AZ::Failure(AZStd::string(
|
||||
"Prefab Focus Handler: Couldn't find owning instance of entityId provided."));
|
||||
}
|
||||
|
||||
m_focusedInstance = focusedInstance;
|
||||
m_focusedTemplateId = focusedInstance->get().GetTemplateId();
|
||||
|
||||
FocusModeInterface* focusModeInterface = AZ::Interface<FocusModeInterface>::Get();
|
||||
if (focusModeInterface)
|
||||
{
|
||||
focusModeInterface->SetFocusRoot(focusedInstance->get().GetContainerEntityId());
|
||||
}
|
||||
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
TemplateId PrefabFocusHandler::GetFocusedPrefabTemplateId()
|
||||
{
|
||||
return m_focusedTemplateId;
|
||||
}
|
||||
|
||||
InstanceOptionalReference PrefabFocusHandler::GetFocusedPrefabInstance()
|
||||
{
|
||||
return m_focusedInstance;
|
||||
}
|
||||
|
||||
bool PrefabFocusHandler::IsOwningPrefabBeingFocused(AZ::EntityId entityId)
|
||||
{
|
||||
if (entityId == AZ::EntityId())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
InstanceOptionalReference instance = m_instanceEntityMapperInterface->FindOwningInstance(entityId);
|
||||
|
||||
return instance.has_value() && (&instance->get() == &m_focusedInstance->get());
|
||||
}
|
||||
|
||||
} // namespace AzToolsFramework::Prefab
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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/Memory/SystemAllocator.h>
|
||||
|
||||
#include <AzToolsFramework/FocusMode/FocusModeInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusInterface.h>
|
||||
#include <AzToolsFramework/Prefab/Template/Template.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab
|
||||
{
|
||||
class InstanceEntityMapperInterface;
|
||||
|
||||
//! Handles Prefab Focus mode, determining which prefab file entity changes will target.
|
||||
class PrefabFocusHandler final
|
||||
: private PrefabFocusInterface
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(PrefabFocusHandler, AZ::SystemAllocator, 0);
|
||||
|
||||
PrefabFocusHandler();
|
||||
~PrefabFocusHandler();
|
||||
|
||||
// PrefabFocusInterface override ...
|
||||
PrefabFocusOperationResult FocusOnOwningPrefab(AZ::EntityId entityId) override;
|
||||
TemplateId GetFocusedPrefabTemplateId() override;
|
||||
InstanceOptionalReference GetFocusedPrefabInstance() override;
|
||||
bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) override;
|
||||
|
||||
private:
|
||||
InstanceOptionalReference m_focusedInstance;
|
||||
TemplateId m_focusedTemplateId;
|
||||
|
||||
InstanceEntityMapperInterface* m_instanceEntityMapperInterface;
|
||||
};
|
||||
|
||||
} // namespace AzToolsFramework::Prefab
|
||||
@@ -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/Interface/Interface.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
#include <AzToolsFramework/Prefab/Template/Template.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab
|
||||
{
|
||||
using PrefabFocusOperationResult = AZ::Outcome<void, AZStd::string>;
|
||||
|
||||
//! Interface to handle operations related to the Prefab Focus system.
|
||||
class PrefabFocusInterface
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(PrefabFocusInterface, "{F3CFA37B-5FD8-436A-9C30-60EB54E350E1}");
|
||||
|
||||
//! Set the focused prefab instance to the owning instance of the entityId provided.
|
||||
//! @param entityId The entityId of the entity whose owning instance we want the prefab system to focus on.
|
||||
virtual PrefabFocusOperationResult FocusOnOwningPrefab(AZ::EntityId entityId) = 0;
|
||||
|
||||
//! Returns the template id of the instance the prefab system is focusing on.
|
||||
virtual TemplateId GetFocusedPrefabTemplateId() = 0;
|
||||
|
||||
//! Returns a reference to the instance the prefab system is focusing on.
|
||||
virtual InstanceOptionalReference GetFocusedPrefabInstance() = 0;
|
||||
|
||||
//! Returns whether the entity belongs to the instance that is being focused on, or one of its descendants.
|
||||
//! @param entityId The entityId of the queried entity.
|
||||
//! @return true if the entity belongs to the focused instance or one of its descendants, false otherwise.
|
||||
virtual bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) = 0;
|
||||
};
|
||||
|
||||
} // namespace AzToolsFramework::Prefab
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <AzCore/std/string/string_view.h>
|
||||
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusHandler.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabSystemComponentInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabUndoCache.h>
|
||||
@@ -189,6 +190,9 @@ namespace AzToolsFramework
|
||||
PrefabLoaderInterface* m_prefabLoaderInterface = nullptr;
|
||||
PrefabSystemComponentInterface* m_prefabSystemComponentInterface = nullptr;
|
||||
|
||||
// Handles the Prefab Focus API that determines what prefab is being edited.
|
||||
PrefabFocusHandler m_prefabFocusHandler;
|
||||
|
||||
// Caches entity states for undo/redo purposes
|
||||
PrefabUndoCache m_prefabUndoCache;
|
||||
|
||||
|
||||
@@ -369,7 +369,7 @@ namespace AzToolsFramework
|
||||
// A counter for generating unique Link Ids.
|
||||
AZStd::atomic<LinkId> m_linkIdCounter = 0u;
|
||||
|
||||
// Used for finding the owning instance of an arbitrary entity
|
||||
// Used for finding the owning instance of an arbitrary entity.
|
||||
InstanceEntityMapper m_instanceEntityMapper;
|
||||
|
||||
// Used for finding the Instances owned by an arbitrary Template.
|
||||
@@ -378,16 +378,16 @@ namespace AzToolsFramework
|
||||
// Used for loading/saving Prefab Template files.
|
||||
PrefabLoader m_prefabLoader;
|
||||
|
||||
// Handler the public Prefab API used by UI and scripting
|
||||
// Handles the public Prefab API used by UI and scripting.
|
||||
PrefabPublicHandler m_prefabPublicHandler;
|
||||
|
||||
// Used for updating Instances of Prefab Template.
|
||||
InstanceUpdateExecutor m_instanceUpdateExecutor;
|
||||
|
||||
// Used for updating Templates when Instances are modified
|
||||
// Used for updating Templates when Instances are modified.
|
||||
InstanceToTemplatePropagator m_instanceToTemplatePropagator;
|
||||
|
||||
// Handler of the public Prefab requests
|
||||
// Handler of the public Prefab requests.
|
||||
PrefabPublicRequestHandler m_prefabPublicRequestHandler;
|
||||
};
|
||||
} // namespace Prefab
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
#include <AzToolsFramework/UI/Prefab/LevelRootUiHandler.h>
|
||||
|
||||
#include <AzToolsFramework/UI/Prefab/PrefabEditInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
|
||||
#include <AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx>
|
||||
|
||||
@@ -24,14 +23,6 @@ namespace AzToolsFramework
|
||||
|
||||
LevelRootUiHandler::LevelRootUiHandler()
|
||||
{
|
||||
m_prefabEditInterface = AZ::Interface<Prefab::PrefabEditInterface>::Get();
|
||||
|
||||
if (m_prefabEditInterface == nullptr)
|
||||
{
|
||||
AZ_Assert(false, "LevelRootUiHandler - could not get PrefabEditInterface on LevelRootUiHandler construction.");
|
||||
return;
|
||||
}
|
||||
|
||||
m_prefabPublicInterface = AZ::Interface<Prefab::PrefabPublicInterface>::Get();
|
||||
|
||||
if (m_prefabPublicInterface == nullptr)
|
||||
|
||||
@@ -14,7 +14,6 @@ namespace AzToolsFramework
|
||||
{
|
||||
namespace Prefab
|
||||
{
|
||||
class PrefabEditInterface;
|
||||
class PrefabPublicInterface;
|
||||
};
|
||||
|
||||
@@ -36,7 +35,6 @@ namespace AzToolsFramework
|
||||
void PaintItemBackground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
|
||||
|
||||
private:
|
||||
Prefab::PrefabEditInterface* m_prefabEditInterface = nullptr;
|
||||
Prefab::PrefabPublicInterface* m_prefabPublicInterface = nullptr;
|
||||
|
||||
static constexpr int m_levelRootBorderThickness = 1;
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* 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/Interface/Interface.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace Prefab
|
||||
{
|
||||
/*!
|
||||
* PrefabEditInterface
|
||||
* Interface to expose the API to Edit Prefabs in the Editor.
|
||||
*/
|
||||
class PrefabEditInterface
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(PrefabEditInterface, "{DABB1D43-3760-420E-9F1E-5104F0AFF167}");
|
||||
|
||||
/**
|
||||
* Sets the prefab for the instance owning the entity provided as the prefab being edited.
|
||||
* @param entityId The entity whose owning prefab should be edited.
|
||||
*/
|
||||
virtual void EditOwningPrefab(AZ::EntityId entityId) = 0;
|
||||
|
||||
/**
|
||||
* Queries the Edit Manager to know if the provided entity is part of the prefab currently being edited.
|
||||
* @param entityId The entity whose prefab editing state we want to query.
|
||||
* @return True if the prefab owning this entity is being edited, false otherwise.
|
||||
*/
|
||||
virtual bool IsOwningPrefabBeingEdited(AZ::EntityId entityId) = 0;
|
||||
};
|
||||
|
||||
} // namespace Prefab
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* 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 <AzToolsFramework/UI/Prefab/PrefabEditManager.h>
|
||||
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace Prefab
|
||||
{
|
||||
PrefabEditManager::PrefabEditManager()
|
||||
{
|
||||
m_prefabPublicInterface = AZ::Interface<PrefabPublicInterface>::Get();
|
||||
|
||||
if (m_prefabPublicInterface == nullptr)
|
||||
{
|
||||
AZ_Assert(false, "Prefab - could not get PrefabPublicInterface on PrefabEditManager construction.");
|
||||
return;
|
||||
}
|
||||
|
||||
AZ::Interface<PrefabEditInterface>::Register(this);
|
||||
}
|
||||
|
||||
PrefabEditManager::~PrefabEditManager()
|
||||
{
|
||||
AZ::Interface<PrefabEditInterface>::Unregister(this);
|
||||
}
|
||||
|
||||
void PrefabEditManager::EditOwningPrefab(AZ::EntityId entityId)
|
||||
{
|
||||
m_instanceBeingEdited = m_prefabPublicInterface->GetInstanceContainerEntityId(entityId);
|
||||
}
|
||||
|
||||
bool PrefabEditManager::IsOwningPrefabBeingEdited(AZ::EntityId entityId)
|
||||
{
|
||||
AZ::EntityId containerEntity = m_prefabPublicInterface->GetInstanceContainerEntityId(entityId);
|
||||
return m_instanceBeingEdited == containerEntity;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* 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/EntityId.h>
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
|
||||
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
|
||||
#include <AzToolsFramework/UI/Prefab/PrefabEditInterface.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace Prefab
|
||||
{
|
||||
class PrefabEditManager final
|
||||
: private PrefabEditInterface
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(PrefabEditManager, AZ::SystemAllocator, 0);
|
||||
|
||||
PrefabEditManager();
|
||||
~PrefabEditManager();
|
||||
|
||||
private:
|
||||
// PrefabEditInterface...
|
||||
void EditOwningPrefab(AZ::EntityId entityId) override;
|
||||
bool IsOwningPrefabBeingEdited(AZ::EntityId entityId) override;
|
||||
|
||||
AZ::EntityId m_instanceBeingEdited;
|
||||
|
||||
PrefabPublicInterface* m_prefabPublicInterface;
|
||||
};
|
||||
}
|
||||
}
|
||||
+12
-11
@@ -22,6 +22,7 @@
|
||||
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
|
||||
#include <AzToolsFramework/AssetBrowser/AssetSelectionModel.h>
|
||||
#include <AzToolsFramework/AssetBrowser/Entries/SourceAssetBrowserEntry.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabLoaderInterface.h>
|
||||
#include <AzToolsFramework/ToolsComponents/EditorLayerComponentBus.h>
|
||||
#include <AzToolsFramework/UI/EditorEntityUi/EditorEntityUiInterface.h>
|
||||
@@ -57,9 +58,9 @@ namespace AzToolsFramework
|
||||
{
|
||||
|
||||
EditorEntityUiInterface* PrefabIntegrationManager::s_editorEntityUiInterface = nullptr;
|
||||
PrefabPublicInterface* PrefabIntegrationManager::s_prefabPublicInterface = nullptr;
|
||||
PrefabEditInterface* PrefabIntegrationManager::s_prefabEditInterface = nullptr;
|
||||
PrefabFocusInterface* PrefabIntegrationManager::s_prefabFocusInterface = nullptr;
|
||||
PrefabLoaderInterface* PrefabIntegrationManager::s_prefabLoaderInterface = nullptr;
|
||||
PrefabPublicInterface* PrefabIntegrationManager::s_prefabPublicInterface = nullptr;
|
||||
PrefabSystemComponentInterface* PrefabIntegrationManager::s_prefabSystemComponentInterface = nullptr;
|
||||
|
||||
const AZStd::string PrefabIntegrationManager::s_prefabFileExtension = ".prefab";
|
||||
@@ -102,13 +103,6 @@ namespace AzToolsFramework
|
||||
return;
|
||||
}
|
||||
|
||||
s_prefabEditInterface = AZ::Interface<PrefabEditInterface>::Get();
|
||||
if (s_prefabEditInterface == nullptr)
|
||||
{
|
||||
AZ_Assert(false, "Prefab - could not get PrefabEditInterface on PrefabIntegrationManager construction.");
|
||||
return;
|
||||
}
|
||||
|
||||
s_prefabLoaderInterface = AZ::Interface<PrefabLoaderInterface>::Get();
|
||||
if (s_prefabLoaderInterface == nullptr)
|
||||
{
|
||||
@@ -123,6 +117,13 @@ namespace AzToolsFramework
|
||||
return;
|
||||
}
|
||||
|
||||
s_prefabFocusInterface = AZ::Interface<PrefabFocusInterface>::Get();
|
||||
if (s_prefabFocusInterface == nullptr)
|
||||
{
|
||||
AZ_Assert(false, "Prefab - could not get PrefabFocusInterface on PrefabIntegrationManager construction.");
|
||||
return;
|
||||
}
|
||||
|
||||
EditorContextMenuBus::Handler::BusConnect();
|
||||
PrefabInstanceContainerNotificationBus::Handler::BusConnect();
|
||||
AZ::Interface<PrefabIntegrationInterface>::Register(this);
|
||||
@@ -224,7 +225,7 @@ namespace AzToolsFramework
|
||||
// Edit Prefab
|
||||
if (prefabWipFeaturesEnabled)
|
||||
{
|
||||
bool beingEdited = s_prefabEditInterface->IsOwningPrefabBeingEdited(selectedEntity);
|
||||
bool beingEdited = s_prefabFocusInterface->IsOwningPrefabBeingFocused(selectedEntity);
|
||||
|
||||
if (!beingEdited)
|
||||
{
|
||||
@@ -428,7 +429,7 @@ namespace AzToolsFramework
|
||||
|
||||
void PrefabIntegrationManager::ContextMenu_EditPrefab(AZ::EntityId containerEntity)
|
||||
{
|
||||
s_prefabEditInterface->EditOwningPrefab(containerEntity);
|
||||
s_prefabFocusInterface->FocusOnOwningPrefab(containerEntity);
|
||||
}
|
||||
|
||||
void PrefabIntegrationManager::ContextMenu_SavePrefab(AZ::EntityId containerEntity)
|
||||
|
||||
+4
-8
@@ -17,7 +17,7 @@
|
||||
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabSystemComponentInterface.h>
|
||||
#include <AzToolsFramework/UI/Prefab/LevelRootUiHandler.h>
|
||||
#include <AzToolsFramework/UI/Prefab/PrefabEditManager.h>
|
||||
|
||||
#include <AzToolsFramework/UI/Prefab/PrefabIntegrationBus.h>
|
||||
#include <AzToolsFramework/UI/Prefab/PrefabIntegrationInterface.h>
|
||||
#include <AzToolsFramework/UI/Prefab/PrefabUiHandler.h>
|
||||
@@ -28,7 +28,7 @@ namespace AzToolsFramework
|
||||
{
|
||||
namespace Prefab
|
||||
{
|
||||
|
||||
class PrefabFocusInterface;
|
||||
class PrefabLoaderInterface;
|
||||
|
||||
//! Structure for saving/retrieving user settings related to prefab workflows.
|
||||
@@ -80,9 +80,6 @@ namespace AzToolsFramework
|
||||
void ExecuteSavePrefabDialog(TemplateId templateId, bool useSaveAllPrefabsPreference) override;
|
||||
|
||||
private:
|
||||
// Manages the Edit Mode UI for prefabs
|
||||
PrefabEditManager m_prefabEditManager;
|
||||
|
||||
// Used to handle the UI for the level root
|
||||
LevelRootUiHandler m_levelRootUiHandler;
|
||||
|
||||
@@ -135,13 +132,12 @@ namespace AzToolsFramework
|
||||
AZStd::unique_ptr<QDialog> ConstructSavePrefabDialog(TemplateId templateId, bool useSaveAllPrefabsPreference);
|
||||
void SavePrefabsInDialog(QDialog* unsavedPrefabsDialog);
|
||||
|
||||
|
||||
static const AZStd::string s_prefabFileExtension;
|
||||
|
||||
static EditorEntityUiInterface* s_editorEntityUiInterface;
|
||||
static PrefabPublicInterface* s_prefabPublicInterface;
|
||||
static PrefabEditInterface* s_prefabEditInterface;
|
||||
static PrefabFocusInterface* s_prefabFocusInterface;
|
||||
static PrefabLoaderInterface* s_prefabLoaderInterface;
|
||||
static PrefabPublicInterface* s_prefabPublicInterface;
|
||||
static PrefabSystemComponentInterface* s_prefabSystemComponentInterface;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
#include <AzToolsFramework/UI/Prefab/PrefabUiHandler.h>
|
||||
|
||||
#include <AzToolsFramework/UI/Prefab/PrefabEditInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
|
||||
#include <AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx>
|
||||
|
||||
@@ -26,21 +26,19 @@ namespace AzToolsFramework
|
||||
|
||||
PrefabUiHandler::PrefabUiHandler()
|
||||
{
|
||||
m_prefabEditInterface = AZ::Interface<Prefab::PrefabEditInterface>::Get();
|
||||
|
||||
if (m_prefabEditInterface == nullptr)
|
||||
{
|
||||
AZ_Assert(false, "PrefabUiHandler - could not get PrefabEditInterface on PrefabUiHandler construction.");
|
||||
return;
|
||||
}
|
||||
|
||||
m_prefabPublicInterface = AZ::Interface<Prefab::PrefabPublicInterface>::Get();
|
||||
|
||||
if (m_prefabPublicInterface == nullptr)
|
||||
{
|
||||
AZ_Assert(false, "PrefabUiHandler - could not get PrefabPublicInterface on PrefabUiHandler construction.");
|
||||
return;
|
||||
}
|
||||
|
||||
m_prefabFocusInterface = AZ::Interface<Prefab::PrefabFocusInterface>::Get();
|
||||
if (m_prefabFocusInterface == nullptr)
|
||||
{
|
||||
AZ_Assert(false, "PrefabUiHandler - could not get PrefabFocusInterface on PrefabUiHandler construction.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
QString PrefabUiHandler::GenerateItemInfoString(AZ::EntityId entityId) const
|
||||
@@ -83,7 +81,7 @@ namespace AzToolsFramework
|
||||
|
||||
QIcon PrefabUiHandler::GenerateItemIcon(AZ::EntityId entityId) const
|
||||
{
|
||||
if (m_prefabEditInterface->IsOwningPrefabBeingEdited(entityId))
|
||||
if (m_prefabFocusInterface->IsOwningPrefabBeingFocused(entityId))
|
||||
{
|
||||
return QIcon(m_prefabEditIconPath);
|
||||
}
|
||||
@@ -105,7 +103,7 @@ namespace AzToolsFramework
|
||||
const bool hasVisibleChildren = index.data(EntityOutlinerListModel::ExpandedRole).value<bool>() && index.model()->hasChildren(index);
|
||||
|
||||
QColor backgroundColor = m_prefabCapsuleColor;
|
||||
if (m_prefabEditInterface->IsOwningPrefabBeingEdited(entityId))
|
||||
if (m_prefabFocusInterface->IsOwningPrefabBeingFocused(entityId))
|
||||
{
|
||||
backgroundColor = m_prefabCapsuleEditColor;
|
||||
}
|
||||
@@ -191,7 +189,7 @@ namespace AzToolsFramework
|
||||
const bool isLastColumn = descendantIndex.column() == EntityOutlinerListModel::ColumnLockToggle;
|
||||
|
||||
QColor borderColor = m_prefabCapsuleColor;
|
||||
if (m_prefabEditInterface->IsOwningPrefabBeingEdited(entityId))
|
||||
if (m_prefabFocusInterface->IsOwningPrefabBeingFocused(entityId))
|
||||
{
|
||||
borderColor = m_prefabCapsuleEditColor;
|
||||
}
|
||||
|
||||
@@ -12,9 +12,10 @@
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
|
||||
namespace Prefab
|
||||
{
|
||||
class PrefabEditInterface;
|
||||
class PrefabFocusInterface;
|
||||
class PrefabPublicInterface;
|
||||
};
|
||||
|
||||
@@ -37,7 +38,7 @@ namespace AzToolsFramework
|
||||
const QModelIndex& descendantIndex) const override;
|
||||
|
||||
private:
|
||||
Prefab::PrefabEditInterface* m_prefabEditInterface = nullptr;
|
||||
Prefab::PrefabFocusInterface* m_prefabFocusInterface = nullptr;
|
||||
Prefab::PrefabPublicInterface* m_prefabPublicInterface = nullptr;
|
||||
|
||||
static bool IsLastVisibleChild(const QModelIndex& parent, const QModelIndex& child);
|
||||
|
||||
+4
-4
@@ -3642,7 +3642,7 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
void EditorTransformComponentSelection::OnViewportViewEntityChanged(const AZ::EntityId& newViewId)
|
||||
void EditorTransformComponentSelection::OnViewportViewEntityChanged(const AZ::EntityId& viewEntityId)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzToolsFramework);
|
||||
|
||||
@@ -3650,12 +3650,12 @@ namespace AzToolsFramework
|
||||
// match the editor camera translation/orientation), record the entity id if we have
|
||||
// a manipulator tracking it (entity id exists in m_entityIdManipulator lookups)
|
||||
// and remove it when recreating manipulators (see InitializeManipulators)
|
||||
if (newViewId.IsValid())
|
||||
if (viewEntityId.IsValid())
|
||||
{
|
||||
const auto entityIdLookupIt = m_entityIdManipulators.m_lookups.find(newViewId);
|
||||
const auto entityIdLookupIt = m_entityIdManipulators.m_lookups.find(viewEntityId);
|
||||
if (entityIdLookupIt != m_entityIdManipulators.m_lookups.end())
|
||||
{
|
||||
m_editorCameraComponentEntityId = newViewId;
|
||||
m_editorCameraComponentEntityId = viewEntityId;
|
||||
RegenerateManipulators();
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -270,7 +270,7 @@ namespace AzToolsFramework
|
||||
void OnTransformChanged(const AZ::Transform& localTM, const AZ::Transform& worldTM) override;
|
||||
|
||||
// Camera::EditorCameraNotificationBus overrides ...
|
||||
void OnViewportViewEntityChanged(const AZ::EntityId& newViewId) override;
|
||||
void OnViewportViewEntityChanged(const AZ::EntityId& viewEntityId) override;
|
||||
|
||||
// EditorContextVisibilityNotificationBus overrides ...
|
||||
void OnEntityVisibilityChanged(bool visibility) override;
|
||||
|
||||
@@ -149,6 +149,9 @@ set(FILES
|
||||
Entity/SliceEditorEntityOwnershipServiceBus.h
|
||||
Fingerprinting/TypeFingerprinter.h
|
||||
Fingerprinting/TypeFingerprinter.cpp
|
||||
FocusMode/FocusModeInterface.h
|
||||
FocusMode/FocusModeSystemComponent.h
|
||||
FocusMode/FocusModeSystemComponent.cpp
|
||||
Logger/TraceLogger.cpp
|
||||
Logger/TraceLogger.h
|
||||
Manipulators/AngularManipulator.cpp
|
||||
@@ -629,6 +632,9 @@ set(FILES
|
||||
Prefab/PrefabDomTypes.h
|
||||
Prefab/PrefabDomUtils.h
|
||||
Prefab/PrefabDomUtils.cpp
|
||||
Prefab/PrefabFocusHandler.h
|
||||
Prefab/PrefabFocusHandler.cpp
|
||||
Prefab/PrefabFocusInterface.h
|
||||
Prefab/PrefabIdTypes.h
|
||||
Prefab/PrefabLoader.h
|
||||
Prefab/PrefabLoader.cpp
|
||||
@@ -721,9 +727,6 @@ set(FILES
|
||||
UI/Layer/LayerUiHandler.cpp
|
||||
UI/Prefab/LevelRootUiHandler.h
|
||||
UI/Prefab/LevelRootUiHandler.cpp
|
||||
UI/Prefab/PrefabEditInterface.h
|
||||
UI/Prefab/PrefabEditManager.h
|
||||
UI/Prefab/PrefabEditManager.cpp
|
||||
UI/Prefab/PrefabIntegrationBus.h
|
||||
UI/Prefab/PrefabIntegrationManager.h
|
||||
UI/Prefab/PrefabIntegrationManager.cpp
|
||||
|
||||
@@ -7,14 +7,69 @@
|
||||
*/
|
||||
|
||||
#include <ProjectBuilderWorker.h>
|
||||
#include <ProjectManagerDefs.h>
|
||||
#include <ProjectUtils.h>
|
||||
|
||||
#include <QDir>
|
||||
#include <QString>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
AZ::Outcome<void, QString> ProjectBuilderWorker::BuildProjectForPlatform()
|
||||
AZ::Outcome<QStringList, QString> ProjectBuilderWorker::ConstructCmakeGenerateProjectArguments(const QString& thirdPartyPath) const
|
||||
{
|
||||
QString error = tr("Automatic building on Linux not currently supported!");
|
||||
QStringToAZTracePrint(error);
|
||||
return AZ::Failure(error);
|
||||
// Attempt to use the Ninja build system if it is installed (described in the o3de documentation) if possible,
|
||||
// otherwise default to the the default for Linux (Unix Makefiles)
|
||||
auto whichNinjaResult = ProjectUtils::ExecuteCommandResult("which", QStringList{"ninja"}, QProcessEnvironment::systemEnvironment());
|
||||
QString cmakeGenerator = (whichNinjaResult.IsSuccess()) ? "Ninja Multi-Config" : "Unix Makefiles";
|
||||
bool compileProfileOnBuild = (whichNinjaResult.IsSuccess());
|
||||
|
||||
// On Linux the default compiler is gcc. For O3DE, it is clang, so we need to specify the version of clang that is detected
|
||||
// in order to get the compiler option.
|
||||
auto compilerOptionResult = ProjectUtils::FindSupportedCompilerForPlatform();
|
||||
if (!compilerOptionResult.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(compilerOptionResult.GetError());
|
||||
}
|
||||
auto clangCompilers = compilerOptionResult.GetValue().split('|');
|
||||
AZ_Assert(clangCompilers.length()==2, "Invalid clang compiler pair specification");
|
||||
|
||||
QString clangCompilerOption = clangCompilers[0];
|
||||
QString clangPPCompilerOption = clangCompilers[1];
|
||||
QString targetBuildPath = QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix);
|
||||
QStringList generateProjectArgs = QStringList{ProjectCMakeCommand,
|
||||
"-B", ProjectBuildPathPostfix,
|
||||
"-S", ".",
|
||||
QString("-G%1").arg(cmakeGenerator),
|
||||
QString("-DCMAKE_C_COMPILER=").append(clangCompilerOption),
|
||||
QString("-DCMAKE_CXX_COMPILER=").append(clangPPCompilerOption),
|
||||
QString("-DLY_3RDPARTY_PATH=").append(thirdPartyPath)};
|
||||
if (!compileProfileOnBuild)
|
||||
{
|
||||
generateProjectArgs.append("-DCMAKE_BUILD_TYPE=profile");
|
||||
}
|
||||
return AZ::Success(generateProjectArgs);
|
||||
}
|
||||
|
||||
|
||||
AZ::Outcome<QStringList, QString> ProjectBuilderWorker::ConstructCmakeBuildCommandArguments() const
|
||||
{
|
||||
auto whichNinjaResult = ProjectUtils::ExecuteCommandResult("which", QStringList{"ninja"}, QProcessEnvironment::systemEnvironment());
|
||||
bool compileProfileOnBuild = (whichNinjaResult.IsSuccess());
|
||||
QString targetBuildPath = QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix);
|
||||
QString launcherTargetName = m_projectInfo.m_projectName + ".GameLauncher";
|
||||
|
||||
QStringList buildProjectArgs = QStringList{ProjectCMakeCommand,
|
||||
"--build", ProjectBuildPathPostfix,
|
||||
"--target", launcherTargetName, ProjectCMakeBuildTargetEditor};
|
||||
if (compileProfileOnBuild)
|
||||
{
|
||||
buildProjectArgs.append(QStringList{"--config","profile"});
|
||||
}
|
||||
return AZ::Success(buildProjectArgs);
|
||||
}
|
||||
|
||||
AZ::Outcome<QStringList, QString> ProjectBuilderWorker::ConstructKillProcessCommandArguments(const QString& pidToKill) const
|
||||
{
|
||||
return AZ::Success(QStringList{"kill", "-9", pidToKill});
|
||||
}
|
||||
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -6,15 +6,48 @@
|
||||
*/
|
||||
|
||||
#include <ProjectUtils.h>
|
||||
#include <ProjectManagerDefs.h>
|
||||
#include <QProcessEnvironment>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
namespace ProjectUtils
|
||||
{
|
||||
AZ::Outcome<void, QString> FindSupportedCompilerForPlatform()
|
||||
// The list of clang C/C++ compiler command lines to validate on the host Linux system
|
||||
const QStringList SupportedClangCommands = {"clang-12|clang++-12"};
|
||||
|
||||
AZ::Outcome<QProcessEnvironment, QString> GetCommandLineProcessEnvironment()
|
||||
{
|
||||
// Compiler detection not supported on platform
|
||||
return AZ::Success();
|
||||
return AZ::Success(QProcessEnvironment(QProcessEnvironment::systemEnvironment()));
|
||||
}
|
||||
|
||||
AZ::Outcome<QString, QString> FindSupportedCompilerForPlatform()
|
||||
{
|
||||
// Validate that cmake is installed and is in the command line
|
||||
auto whichCMakeResult = ProjectUtils::ExecuteCommandResult("which", QStringList{ProjectCMakeCommand}, QProcessEnvironment::systemEnvironment());
|
||||
if (!whichCMakeResult.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(QObject::tr("CMake not found. \n\n"
|
||||
"Make sure that the minimum version of CMake is installed and available from the command prompt. "
|
||||
"Refer to the <a href='https://o3de.org/docs/welcome-guide/setup/requirements/#cmake'>O3DE requirements</a> page for more information."));
|
||||
}
|
||||
|
||||
// Look for the first compatible version of clang. The list below will contain the known clang compilers that have been tested for O3DE.
|
||||
for (const QString& supportClangCommand : SupportedClangCommands)
|
||||
{
|
||||
auto clangCompilers = supportClangCommand.split('|');
|
||||
AZ_Assert(clangCompilers.length()==2, "Invalid clang compiler pair specification");
|
||||
|
||||
auto whichClangResult = ProjectUtils::ExecuteCommandResult("which", QStringList{clangCompilers[0]}, QProcessEnvironment::systemEnvironment());
|
||||
auto whichClangPPResult = ProjectUtils::ExecuteCommandResult("which", QStringList{clangCompilers[1]}, QProcessEnvironment::systemEnvironment());
|
||||
if (whichClangResult.IsSuccess() && whichClangPPResult.IsSuccess())
|
||||
{
|
||||
return AZ::Success(supportClangCommand);
|
||||
}
|
||||
}
|
||||
return AZ::Failure(QObject::tr("Clang not found. \n\n"
|
||||
"Make sure that the clang is installed and available from the command prompt. "
|
||||
"Refer to the <a href='https://o3de.org/docs/welcome-guide/setup/requirements/#cmake'>O3DE requirements</a> page for more information."));
|
||||
}
|
||||
|
||||
} // namespace ProjectUtils
|
||||
|
||||
@@ -7,14 +7,82 @@
|
||||
*/
|
||||
|
||||
#include <ProjectBuilderWorker.h>
|
||||
#include <ProjectManagerDefs.h>
|
||||
#include <ProjectUtils.h>
|
||||
|
||||
#include <QDir>
|
||||
#include <QString>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
AZ::Outcome<void, QString> ProjectBuilderWorker::BuildProjectForPlatform()
|
||||
namespace Internal
|
||||
{
|
||||
QString error = tr("Automatic building on MacOS not currently supported!");
|
||||
QStringToAZTracePrint(error);
|
||||
return AZ::Failure(error);
|
||||
AZ::Outcome<QString, QString> QueryInstalledCmakeFullPath()
|
||||
{
|
||||
auto environmentRequest = ProjectUtils::GetCommandLineProcessEnvironment();
|
||||
if (!environmentRequest.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(environmentRequest.GetError());
|
||||
}
|
||||
auto currentEnvironment = environmentRequest.GetValue();
|
||||
|
||||
auto queryCmakeInstalled = ProjectUtils::ExecuteCommandResult("which",
|
||||
QStringList{ProjectCMakeCommand},
|
||||
currentEnvironment);
|
||||
if (!queryCmakeInstalled.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(QObject::tr("Unable to detect CMake on this host."));
|
||||
}
|
||||
QString cmakeInstalledPath = queryCmakeInstalled.GetValue().split("\n")[0];
|
||||
return AZ::Success(cmakeInstalledPath);
|
||||
}
|
||||
}
|
||||
|
||||
AZ::Outcome<QStringList, QString> ProjectBuilderWorker::ConstructCmakeGenerateProjectArguments(const QString& thirdPartyPath) const
|
||||
{
|
||||
// For Mac, we need to resolve the full path of cmake and use that in the process request. For
|
||||
// some reason, 'which' will resolve the full path, but when you just specify cmake with the same
|
||||
// environment, it is unable to resolve. To work around this, we will use 'which' to resolve the
|
||||
// full path and then use it as the command argument
|
||||
auto cmakeInstalledPathQuery = Internal::QueryInstalledCmakeFullPath();
|
||||
if (!cmakeInstalledPathQuery.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(cmakeInstalledPathQuery.GetError());
|
||||
}
|
||||
QString cmakeInstalledPath = cmakeInstalledPathQuery.GetValue();
|
||||
QString targetBuildPath = QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix);
|
||||
|
||||
return AZ::Success(QStringList{cmakeInstalledPath,
|
||||
"-B", targetBuildPath,
|
||||
"-S", m_projectInfo.m_path,
|
||||
"-GXcode"});
|
||||
}
|
||||
|
||||
AZ::Outcome<QStringList, QString> ProjectBuilderWorker::ConstructCmakeBuildCommandArguments() const
|
||||
{
|
||||
// For Mac, we need to resolve the full path of cmake and use that in the process request. For
|
||||
// some reason, 'which' will resolve the full path, but when you just specify cmake with the same
|
||||
// environment, it is unable to resolve. To work around this, we will use 'which' to resolve the
|
||||
// full path and then use it as the command argument
|
||||
auto cmakeInstalledPathQuery = Internal::QueryInstalledCmakeFullPath();
|
||||
if (!cmakeInstalledPathQuery.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(cmakeInstalledPathQuery.GetError());
|
||||
}
|
||||
|
||||
QString cmakeInstalledPath = cmakeInstalledPathQuery.GetValue();
|
||||
QString targetBuildPath = QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix);
|
||||
QString launcherTargetName = m_projectInfo.m_projectName + ".GameLauncher";
|
||||
|
||||
return AZ::Success(QStringList{cmakeInstalledPath,
|
||||
"--build", targetBuildPath,
|
||||
"--config", "profile",
|
||||
"--target", launcherTargetName, ProjectCMakeBuildTargetEditor});
|
||||
}
|
||||
|
||||
AZ::Outcome<QStringList, QString> ProjectBuilderWorker::ConstructKillProcessCommandArguments(const QString& pidToKill) const
|
||||
{
|
||||
return AZ::Success(QStringList{"kill", "-9", pidToKill});
|
||||
}
|
||||
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -7,15 +7,59 @@
|
||||
|
||||
#include <ProjectUtils.h>
|
||||
|
||||
#include <QProcess>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
namespace ProjectUtils
|
||||
{
|
||||
AZ::Outcome<void, QString> FindSupportedCompilerForPlatform()
|
||||
AZ::Outcome<QProcessEnvironment, QString> GetCommandLineProcessEnvironment()
|
||||
{
|
||||
// Compiler detection not supported on platform
|
||||
return AZ::Success();
|
||||
// For CMake on Mac, if its installed through home-brew, then it will be installed
|
||||
// under /usr/local/bin, which may not be in the system PATH environment.
|
||||
// Add that path for the command line process so that it will be able to locate
|
||||
// a home-brew installed version of CMake
|
||||
QProcessEnvironment currentEnvironment(QProcessEnvironment::systemEnvironment());
|
||||
QString pathValue = currentEnvironment.value("PATH");
|
||||
pathValue += ":/usr/local/bin";
|
||||
currentEnvironment.insert("PATH", pathValue);
|
||||
return AZ::Success(currentEnvironment);
|
||||
}
|
||||
|
||||
|
||||
AZ::Outcome<QString, QString> FindSupportedCompilerForPlatform()
|
||||
{
|
||||
QProcessEnvironment currentEnvironment(QProcessEnvironment::systemEnvironment());
|
||||
QString pathValue = currentEnvironment.value("PATH");
|
||||
pathValue += ":/usr/local/bin";
|
||||
currentEnvironment.insert("PATH", pathValue);
|
||||
|
||||
// Validate that we have cmake installed first
|
||||
auto queryCmakeInstalled = ExecuteCommandResult("which", QStringList{ProjectCMakeCommand}, currentEnvironment);
|
||||
if (!queryCmakeInstalled.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(QObject::tr("Unable to detect CMake on this host."));
|
||||
}
|
||||
QString cmakeInstalledPath = queryCmakeInstalled.GetValue().split("\n")[0];
|
||||
|
||||
// Query the version of the installed cmake
|
||||
auto queryCmakeVersionQuery = ExecuteCommandResult(cmakeInstalledPath, QStringList{"-version"}, currentEnvironment);
|
||||
if (!queryCmakeVersionQuery.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(QObject::tr("Unable to determine the version of CMake on this host."));
|
||||
}
|
||||
AZ_TracePrintf("Project Manager", "Cmake version %s detected.", queryCmakeVersionQuery.GetValue().split("\n")[0].toUtf8().constData());
|
||||
|
||||
// Query for the version of xcodebuild (if installed)
|
||||
auto queryXcodeBuildVersion = ExecuteCommandResult("xcodebuild", QStringList{"-version"}, currentEnvironment);
|
||||
if (!queryCmakeInstalled.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(QObject::tr("Unable to detect XCodeBuilder on this host."));
|
||||
}
|
||||
QString xcodeBuilderVersionNumber = queryXcodeBuildVersion.GetValue().split("\n")[0];
|
||||
AZ_TracePrintf("Project Manager", "XcodeBuilder version %s detected.", xcodeBuilderVersionNumber.toUtf8().constData());
|
||||
|
||||
|
||||
return AZ::Success(xcodeBuilderVersionNumber);
|
||||
}
|
||||
} // namespace ProjectUtils
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -8,189 +8,37 @@
|
||||
|
||||
#include <ProjectBuilderWorker.h>
|
||||
#include <ProjectManagerDefs.h>
|
||||
#include <PythonBindingsInterface.h>
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QProcess>
|
||||
#include <QProcessEnvironment>
|
||||
#include <QTextStream>
|
||||
#include <QThread>
|
||||
#include <QString>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
AZ::Outcome<void, QString> ProjectBuilderWorker::BuildProjectForPlatform()
|
||||
AZ::Outcome<QStringList, QString> ProjectBuilderWorker::ConstructCmakeGenerateProjectArguments(const QString& thirdPartyPath) const
|
||||
{
|
||||
// Check if we are trying to cancel task
|
||||
if (QThread::currentThread()->isInterruptionRequested())
|
||||
{
|
||||
QStringToAZTracePrint(BuildCancelled);
|
||||
return AZ::Failure(BuildCancelled);
|
||||
}
|
||||
QString targetBuildPath = QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix);
|
||||
|
||||
QFile logFile(GetLogFilePath());
|
||||
if (!logFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate))
|
||||
{
|
||||
QString error = tr("Failed to open log file.");
|
||||
QStringToAZTracePrint(error);
|
||||
return AZ::Failure(error);
|
||||
}
|
||||
return AZ::Success(QStringList{ ProjectCMakeCommand,
|
||||
"-B", targetBuildPath,
|
||||
"-S", m_projectInfo.m_path,
|
||||
QString("-DLY_3RDPARTY_PATH=").append(thirdPartyPath),
|
||||
"-DLY_UNITY_BUILD=ON" } );
|
||||
}
|
||||
|
||||
EngineInfo engineInfo;
|
||||
AZ::Outcome<QStringList, QString> ProjectBuilderWorker::ConstructCmakeBuildCommandArguments() const
|
||||
{
|
||||
QString targetBuildPath = QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix);
|
||||
QString launcherTargetName = m_projectInfo.m_projectName + ".GameLauncher";
|
||||
|
||||
AZ::Outcome<EngineInfo> engineInfoResult = PythonBindingsInterface::Get()->GetEngineInfo();
|
||||
if (engineInfoResult.IsSuccess())
|
||||
{
|
||||
engineInfo = engineInfoResult.GetValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
QString error = tr("Failed to get engine info.");
|
||||
QStringToAZTracePrint(error);
|
||||
return AZ::Failure(error);
|
||||
}
|
||||
return AZ::Success(QStringList{ ProjectCMakeCommand,
|
||||
"--build", targetBuildPath,
|
||||
"--config", "profile",
|
||||
"--target", launcherTargetName, ProjectCMakeBuildTargetEditor });
|
||||
}
|
||||
|
||||
QTextStream logStream(&logFile);
|
||||
if (QThread::currentThread()->isInterruptionRequested())
|
||||
{
|
||||
logFile.close();
|
||||
QStringToAZTracePrint(BuildCancelled);
|
||||
return AZ::Failure(BuildCancelled);
|
||||
}
|
||||
|
||||
// Show some kind of progress with very approximate estimates
|
||||
UpdateProgress(++m_progressEstimate);
|
||||
|
||||
QProcessEnvironment currentEnvironment(QProcessEnvironment::systemEnvironment());
|
||||
// Append cmake path to PATH incase it is missing
|
||||
QDir cmakePath(engineInfo.m_path);
|
||||
cmakePath.cd("cmake/runtime/bin");
|
||||
QString pathValue = currentEnvironment.value("PATH");
|
||||
pathValue += ";" + cmakePath.path();
|
||||
currentEnvironment.insert("PATH", pathValue);
|
||||
|
||||
m_configProjectProcess = new QProcess(this);
|
||||
m_configProjectProcess->setProcessChannelMode(QProcess::MergedChannels);
|
||||
m_configProjectProcess->setWorkingDirectory(m_projectInfo.m_path);
|
||||
m_configProjectProcess->setProcessEnvironment(currentEnvironment);
|
||||
|
||||
m_configProjectProcess->start(
|
||||
"cmake",
|
||||
QStringList
|
||||
{
|
||||
"-B",
|
||||
QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix),
|
||||
"-S",
|
||||
m_projectInfo.m_path,
|
||||
"-G",
|
||||
"Visual Studio 16",
|
||||
"-DLY_3RDPARTY_PATH=" + engineInfo.m_thirdPartyPath,
|
||||
"-DLY_UNITY_BUILD=1"
|
||||
});
|
||||
|
||||
if (!m_configProjectProcess->waitForStarted())
|
||||
{
|
||||
QString error = tr("Configuring project failed to start.");
|
||||
QStringToAZTracePrint(error);
|
||||
return AZ::Failure(error);
|
||||
}
|
||||
bool containsGeneratingDone = false;
|
||||
while (m_configProjectProcess->waitForReadyRead(MaxBuildTimeMSecs))
|
||||
{
|
||||
QString configOutput = m_configProjectProcess->readAllStandardOutput();
|
||||
|
||||
if (configOutput.contains("Generating done"))
|
||||
{
|
||||
containsGeneratingDone = true;
|
||||
}
|
||||
|
||||
logStream << configOutput;
|
||||
logStream.flush();
|
||||
|
||||
UpdateProgress(qMin(++m_progressEstimate, 19));
|
||||
|
||||
if (QThread::currentThread()->isInterruptionRequested())
|
||||
{
|
||||
logFile.close();
|
||||
m_configProjectProcess->close();
|
||||
QStringToAZTracePrint(BuildCancelled);
|
||||
return AZ::Failure(BuildCancelled);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_configProjectProcess->exitStatus() != QProcess::ExitStatus::NormalExit
|
||||
|| m_configProjectProcess->exitCode() != 0
|
||||
|| !containsGeneratingDone)
|
||||
{
|
||||
QString error = tr("Configuring project failed. See log for details.");
|
||||
QStringToAZTracePrint(error);
|
||||
return AZ::Failure(error);
|
||||
}
|
||||
|
||||
UpdateProgress(++m_progressEstimate);
|
||||
|
||||
m_buildProjectProcess = new QProcess(this);
|
||||
m_buildProjectProcess->setProcessChannelMode(QProcess::MergedChannels);
|
||||
m_buildProjectProcess->setWorkingDirectory(m_projectInfo.m_path);
|
||||
m_buildProjectProcess->setProcessEnvironment(currentEnvironment);
|
||||
|
||||
m_buildProjectProcess->start(
|
||||
"cmake",
|
||||
QStringList
|
||||
{
|
||||
"--build",
|
||||
QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix),
|
||||
"--target",
|
||||
m_projectInfo.m_projectName + ".GameLauncher",
|
||||
"Editor",
|
||||
"--config",
|
||||
"profile"
|
||||
});
|
||||
|
||||
if (!m_buildProjectProcess->waitForStarted())
|
||||
{
|
||||
QString error = tr("Building project failed to start.");
|
||||
QStringToAZTracePrint(error);
|
||||
return AZ::Failure(error);
|
||||
}
|
||||
|
||||
// There are a lot of steps when building so estimate around 800 more steps ((100 - 20) * 10) remaining
|
||||
m_progressEstimate = 200;
|
||||
while (m_buildProjectProcess->waitForReadyRead(MaxBuildTimeMSecs))
|
||||
{
|
||||
logStream << m_buildProjectProcess->readAllStandardOutput();
|
||||
logStream.flush();
|
||||
|
||||
// Show 1% progress for every 10 steps completed
|
||||
UpdateProgress(qMin(++m_progressEstimate / 10, 99));
|
||||
|
||||
if (QThread::currentThread()->isInterruptionRequested())
|
||||
{
|
||||
// QProcess is unable to kill its child processes so we need to ask the operating system to do that for us
|
||||
QProcess killBuildProcess;
|
||||
killBuildProcess.setProcessChannelMode(QProcess::MergedChannels);
|
||||
killBuildProcess.start(
|
||||
"cmd.exe", QStringList{ "/C", "taskkill", "/pid", QString::number(m_buildProjectProcess->processId()), "/f", "/t" });
|
||||
killBuildProcess.waitForFinished();
|
||||
|
||||
logStream << "Killing Project Build.";
|
||||
logStream << killBuildProcess.readAllStandardOutput();
|
||||
m_buildProjectProcess->kill();
|
||||
logFile.close();
|
||||
QStringToAZTracePrint(BuildCancelled);
|
||||
return AZ::Failure(BuildCancelled);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_configProjectProcess->exitStatus() != QProcess::ExitStatus::NormalExit
|
||||
|| m_configProjectProcess->exitCode() != 0)
|
||||
{
|
||||
QString error = tr("Building project failed. See log for details.");
|
||||
QStringToAZTracePrint(error);
|
||||
return AZ::Failure(error);
|
||||
}
|
||||
|
||||
return AZ::Success();
|
||||
AZ::Outcome<QStringList, QString> ProjectBuilderWorker::ConstructKillProcessCommandArguments(const QString& pidToKill) const
|
||||
{
|
||||
return AZ::Success(QStringList { "cmd.exe", "/C", "taskkill", "/pid", pidToKill, "/f", "/t" } );
|
||||
}
|
||||
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
#include <ProjectUtils.h>
|
||||
|
||||
#include <PythonBindingsInterface.h>
|
||||
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QProcess>
|
||||
@@ -16,8 +18,44 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
namespace ProjectUtils
|
||||
{
|
||||
AZ::Outcome<void, QString> FindSupportedCompilerForPlatform()
|
||||
AZ::Outcome<QProcessEnvironment, QString> GetCommandLineProcessEnvironment()
|
||||
{
|
||||
// Use the engine path to insert a path for cmake
|
||||
auto engineInfoResult = PythonBindingsInterface::Get()->GetEngineInfo();
|
||||
if (!engineInfoResult.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(QObject::tr("Failed to get engine info"));
|
||||
}
|
||||
auto engineInfo = engineInfoResult.GetValue();
|
||||
|
||||
QProcessEnvironment currentEnvironment(QProcessEnvironment::systemEnvironment());
|
||||
|
||||
// Append cmake path to PATH incase it is missing
|
||||
QDir cmakePath(engineInfo.m_path);
|
||||
cmakePath.cd("cmake/runtime/bin");
|
||||
QString pathValue = currentEnvironment.value("PATH");
|
||||
pathValue += ";" + cmakePath.path();
|
||||
currentEnvironment.insert("PATH", pathValue);
|
||||
return AZ::Success(currentEnvironment);
|
||||
}
|
||||
|
||||
AZ::Outcome<QString, QString> FindSupportedCompilerForPlatform()
|
||||
{
|
||||
// Validate that cmake is installed
|
||||
auto cmakeProcessEnvResult = GetCommandLineProcessEnvironment();
|
||||
if (!cmakeProcessEnvResult.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(cmakeProcessEnvResult.GetError());
|
||||
}
|
||||
auto cmakeVersionQueryResult = ExecuteCommandResult("cmake", QStringList{"--version"}, cmakeProcessEnvResult.GetValue());
|
||||
if (!cmakeVersionQueryResult.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(QObject::tr("CMake not found. \n\n"
|
||||
"Make sure that the minimum version of CMake is installed and available from the command prompt. "
|
||||
"Refer to the <a href='https://o3de.org/docs/welcome-guide/setup/requirements/#cmake'>O3DE requirements</a> for more information."));
|
||||
}
|
||||
|
||||
// Validate that the minimal version of visual studio is installed
|
||||
QProcessEnvironment environment = QProcessEnvironment::systemEnvironment();
|
||||
QString programFilesPath = environment.value("ProgramFiles(x86)");
|
||||
QString vsWherePath = QDir(programFilesPath).filePath("Microsoft Visual Studio/Installer/vswhere.exe");
|
||||
@@ -25,27 +63,31 @@ namespace O3DE::ProjectManager
|
||||
QFileInfo vsWhereFile(vsWherePath);
|
||||
if (vsWhereFile.exists() && vsWhereFile.isFile())
|
||||
{
|
||||
QProcess vsWhereProcess;
|
||||
vsWhereProcess.setProcessChannelMode(QProcess::MergedChannels);
|
||||
QStringList vsWhereBaseArguments = QStringList{"-version",
|
||||
"16.9.2",
|
||||
"-latest",
|
||||
"-requires",
|
||||
"Microsoft.VisualStudio.Component.VC.Tools.x86.x64"};
|
||||
|
||||
vsWhereProcess.start(
|
||||
vsWherePath,
|
||||
QStringList{
|
||||
"-version",
|
||||
"16.9.2",
|
||||
"-latest",
|
||||
"-requires",
|
||||
"Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
|
||||
"-property",
|
||||
"isComplete"
|
||||
});
|
||||
QProcess vsWhereIsCompleteProcess;
|
||||
vsWhereIsCompleteProcess.setProcessChannelMode(QProcess::MergedChannels);
|
||||
|
||||
if (vsWhereProcess.waitForStarted() && vsWhereProcess.waitForFinished())
|
||||
vsWhereIsCompleteProcess.start(vsWherePath, vsWhereBaseArguments + QStringList{ "-property", "isComplete" });
|
||||
|
||||
if (vsWhereIsCompleteProcess.waitForStarted() && vsWhereIsCompleteProcess.waitForFinished())
|
||||
{
|
||||
QString vsWhereOutput(vsWhereProcess.readAllStandardOutput());
|
||||
if (vsWhereOutput.startsWith("1"))
|
||||
QString vsWhereIsCompleteOutput(vsWhereIsCompleteProcess.readAllStandardOutput());
|
||||
if (vsWhereIsCompleteOutput.startsWith("1"))
|
||||
{
|
||||
return AZ::Success();
|
||||
QProcess vsWhereCompilerVersionProcess;
|
||||
vsWhereCompilerVersionProcess.setProcessChannelMode(QProcess::MergedChannels);
|
||||
vsWhereCompilerVersionProcess.start(vsWherePath, vsWhereBaseArguments + QStringList{"-property", "catalog_productDisplayVersion"});
|
||||
|
||||
if (vsWhereCompilerVersionProcess.waitForStarted() && vsWhereCompilerVersionProcess.waitForFinished())
|
||||
{
|
||||
QString vsWhereCompilerVersionOutput(vsWhereCompilerVersionProcess.readAllStandardOutput());
|
||||
return AZ::Success(vsWhereCompilerVersionOutput);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,15 @@
|
||||
|
||||
#include <ProjectBuilderWorker.h>
|
||||
#include <ProjectManagerDefs.h>
|
||||
#include <PythonBindingsInterface.h>
|
||||
#include <ProjectUtils.h>
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QProcess>
|
||||
#include <QProcessEnvironment>
|
||||
#include <QTextStream>
|
||||
#include <QThread>
|
||||
|
||||
//#define MOCK_BUILD_PROJECT true
|
||||
|
||||
@@ -67,4 +74,176 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
AZ_TracePrintf("Project Manager", error.toStdString().c_str());
|
||||
}
|
||||
|
||||
AZ::Outcome<void, QString> ProjectBuilderWorker::BuildProjectForPlatform()
|
||||
{
|
||||
// Check if we are trying to cancel task
|
||||
if (QThread::currentThread()->isInterruptionRequested())
|
||||
{
|
||||
QStringToAZTracePrint(BuildCancelled);
|
||||
return AZ::Failure(BuildCancelled);
|
||||
}
|
||||
|
||||
QFile logFile(GetLogFilePath());
|
||||
if (!logFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate))
|
||||
{
|
||||
QString error = tr("Failed to open log file.");
|
||||
QStringToAZTracePrint(error);
|
||||
return AZ::Failure(error);
|
||||
}
|
||||
|
||||
EngineInfo engineInfo;
|
||||
|
||||
AZ::Outcome<EngineInfo> engineInfoResult = PythonBindingsInterface::Get()->GetEngineInfo();
|
||||
if (engineInfoResult.IsSuccess())
|
||||
{
|
||||
engineInfo = engineInfoResult.GetValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
QString error = tr("Failed to get engine info.");
|
||||
QStringToAZTracePrint(error);
|
||||
return AZ::Failure(error);
|
||||
}
|
||||
|
||||
QTextStream logStream(&logFile);
|
||||
if (QThread::currentThread()->isInterruptionRequested())
|
||||
{
|
||||
logFile.close();
|
||||
QStringToAZTracePrint(BuildCancelled);
|
||||
return AZ::Failure(BuildCancelled);
|
||||
}
|
||||
|
||||
// Show some kind of progress with very approximate estimates
|
||||
UpdateProgress(++m_progressEstimate);
|
||||
|
||||
auto currentEnvironmentRequest = ProjectUtils::GetCommandLineProcessEnvironment();
|
||||
if (!currentEnvironmentRequest.IsSuccess())
|
||||
{
|
||||
QStringToAZTracePrint(currentEnvironmentRequest.GetError());
|
||||
return AZ::Failure(currentEnvironmentRequest.GetError());
|
||||
}
|
||||
QProcessEnvironment currentEnvironment = currentEnvironmentRequest.GetValue();
|
||||
|
||||
m_configProjectProcess = new QProcess(this);
|
||||
m_configProjectProcess->setProcessChannelMode(QProcess::MergedChannels);
|
||||
m_configProjectProcess->setWorkingDirectory(m_projectInfo.m_path);
|
||||
m_configProjectProcess->setProcessEnvironment(currentEnvironment);
|
||||
|
||||
auto cmakeGenerateArgumentsResult = ConstructCmakeGenerateProjectArguments(engineInfo.m_thirdPartyPath);
|
||||
if (!cmakeGenerateArgumentsResult.IsSuccess())
|
||||
{
|
||||
QStringToAZTracePrint(cmakeGenerateArgumentsResult.GetError());
|
||||
return AZ::Failure(cmakeGenerateArgumentsResult.GetError());
|
||||
}
|
||||
auto cmakeGenerateArguments = cmakeGenerateArgumentsResult.GetValue();
|
||||
m_configProjectProcess->start(cmakeGenerateArguments.front(), cmakeGenerateArguments.mid(1));
|
||||
if (!m_configProjectProcess->waitForStarted())
|
||||
{
|
||||
QString error = tr("Configuring project failed to start.");
|
||||
QStringToAZTracePrint(error);
|
||||
return AZ::Failure(error);
|
||||
}
|
||||
bool containsGeneratingDone = false;
|
||||
while (m_configProjectProcess->waitForReadyRead(MaxBuildTimeMSecs))
|
||||
{
|
||||
QString configOutput = m_configProjectProcess->readAllStandardOutput();
|
||||
|
||||
if (configOutput.contains("Generating done"))
|
||||
{
|
||||
containsGeneratingDone = true;
|
||||
}
|
||||
|
||||
logStream << configOutput;
|
||||
logStream.flush();
|
||||
|
||||
UpdateProgress(qMin(++m_progressEstimate, 19));
|
||||
|
||||
if (QThread::currentThread()->isInterruptionRequested())
|
||||
{
|
||||
logFile.close();
|
||||
m_configProjectProcess->close();
|
||||
QStringToAZTracePrint(BuildCancelled);
|
||||
return AZ::Failure(BuildCancelled);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_configProjectProcess->exitStatus() != QProcess::ExitStatus::NormalExit || m_configProjectProcess->exitCode() != 0 ||
|
||||
!containsGeneratingDone)
|
||||
{
|
||||
QString error = tr("Configuring project failed. See log for details.");
|
||||
QStringToAZTracePrint(error);
|
||||
return AZ::Failure(error);
|
||||
}
|
||||
|
||||
UpdateProgress(++m_progressEstimate);
|
||||
|
||||
m_buildProjectProcess = new QProcess(this);
|
||||
m_buildProjectProcess->setProcessChannelMode(QProcess::MergedChannels);
|
||||
m_buildProjectProcess->setWorkingDirectory(m_projectInfo.m_path);
|
||||
m_buildProjectProcess->setProcessEnvironment(currentEnvironment);
|
||||
|
||||
auto cmakeBuildArgumentsResult = ConstructCmakeBuildCommandArguments();
|
||||
if (!cmakeBuildArgumentsResult.IsSuccess())
|
||||
{
|
||||
QStringToAZTracePrint(cmakeBuildArgumentsResult.GetError());
|
||||
return AZ::Failure(cmakeBuildArgumentsResult.GetError());
|
||||
}
|
||||
auto cmakeBuildArguments = cmakeBuildArgumentsResult.GetValue();
|
||||
|
||||
m_buildProjectProcess->start(cmakeBuildArguments.front(), cmakeBuildArguments.mid(1));
|
||||
if (!m_buildProjectProcess->waitForStarted())
|
||||
{
|
||||
QString error = tr("Building project failed to start.");
|
||||
QStringToAZTracePrint(error);
|
||||
return AZ::Failure(error);
|
||||
}
|
||||
|
||||
// There are a lot of steps when building so estimate around 800 more steps ((100 - 20) * 10) remaining
|
||||
m_progressEstimate = 200;
|
||||
while (m_buildProjectProcess->waitForReadyRead(MaxBuildTimeMSecs))
|
||||
{
|
||||
logStream << m_buildProjectProcess->readAllStandardOutput();
|
||||
logStream.flush();
|
||||
|
||||
// Show 1% progress for every 10 steps completed
|
||||
UpdateProgress(qMin(++m_progressEstimate / 10, 99));
|
||||
|
||||
if (QThread::currentThread()->isInterruptionRequested())
|
||||
{
|
||||
// QProcess is unable to kill its child processes so we need to ask the operating system to do that for us
|
||||
auto killProcessArgumentsResult = ConstructKillProcessCommandArguments(QString::number(m_buildProjectProcess->processId()));
|
||||
if (!killProcessArgumentsResult.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(killProcessArgumentsResult.GetError());
|
||||
}
|
||||
auto killProcessArguments = killProcessArgumentsResult.GetValue();
|
||||
|
||||
|
||||
QProcess killBuildProcess;
|
||||
|
||||
|
||||
killBuildProcess.setProcessChannelMode(QProcess::MergedChannels);
|
||||
killBuildProcess.start(killProcessArguments.front(), killProcessArguments.mid(1));
|
||||
killBuildProcess.waitForFinished();
|
||||
|
||||
logStream << "Killing Project Build.";
|
||||
logStream << killBuildProcess.readAllStandardOutput();
|
||||
m_buildProjectProcess->kill();
|
||||
logFile.close();
|
||||
QStringToAZTracePrint(BuildCancelled);
|
||||
return AZ::Failure(BuildCancelled);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_buildProjectProcess->exitStatus() != QProcess::ExitStatus::NormalExit || m_buildProjectProcess->exitCode() != 0)
|
||||
{
|
||||
QString error = tr("Building project failed. See log for details.");
|
||||
QStringToAZTracePrint(error);
|
||||
return AZ::Failure(error);
|
||||
}
|
||||
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <AzCore/Outcome/Outcome.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QProcessEnvironment>
|
||||
#endif
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QProcess)
|
||||
@@ -44,6 +45,12 @@ namespace O3DE::ProjectManager
|
||||
AZ::Outcome<void, QString> BuildProjectForPlatform();
|
||||
void QStringToAZTracePrint(const QString& error);
|
||||
|
||||
// Command line argument builders
|
||||
AZ::Outcome<QStringList, QString> ConstructCmakeGenerateProjectArguments(const QString& thirdPartyPath) const;
|
||||
AZ::Outcome<QStringList, QString> ConstructCmakeBuildCommandArguments() const;
|
||||
AZ::Outcome<QStringList, QString> ConstructKillProcessCommandArguments(const QString& pidToKill) const;
|
||||
|
||||
|
||||
QProcess* m_configProjectProcess = nullptr;
|
||||
QProcess* m_buildProjectProcess = nullptr;
|
||||
ProjectInfo m_projectInfo;
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace O3DE::ProjectManager
|
||||
inline constexpr static int ProjectPreviewImageWidth = 210;
|
||||
inline constexpr static int ProjectPreviewImageHeight = 280;
|
||||
inline constexpr static int ProjectTemplateImageWidth = 92;
|
||||
inline constexpr static int ProjectCommandLineTimeoutSeconds = 30;
|
||||
|
||||
static const QString ProjectBuildDirectoryName = "build";
|
||||
extern const QString ProjectBuildPathPostfix;
|
||||
@@ -21,4 +22,8 @@ namespace O3DE::ProjectManager
|
||||
static const QString ProjectBuildErrorLogName = "CMakeProjectBuildError.log";
|
||||
static const QString ProjectCacheDirectoryName = "Cache";
|
||||
static const QString ProjectPreviewImagePath = "preview.png";
|
||||
|
||||
static const QString ProjectCMakeCommand = "cmake";
|
||||
static const QString ProjectCMakeBuildTargetEditor = "Editor";
|
||||
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
#include <QSpacerItem>
|
||||
#include <QGridLayout>
|
||||
|
||||
#include <AzCore/std/chrono/chrono.h>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
namespace ProjectUtils
|
||||
@@ -507,5 +509,33 @@ namespace O3DE::ProjectManager
|
||||
|
||||
return ProjectManagerScreen::Invalid;
|
||||
}
|
||||
|
||||
AZ::Outcome<QString, QString> ExecuteCommandResult(
|
||||
const QString& cmd,
|
||||
const QStringList& arguments,
|
||||
const QProcessEnvironment& processEnv,
|
||||
int commandTimeoutSeconds /*= ProjectCommandLineTimeoutSeconds*/)
|
||||
{
|
||||
QProcess execProcess;
|
||||
execProcess.setProcessEnvironment(processEnv);
|
||||
execProcess.setProcessChannelMode(QProcess::MergedChannels);
|
||||
execProcess.start(cmd, arguments);
|
||||
if (!execProcess.waitForStarted())
|
||||
{
|
||||
return AZ::Failure(QObject::tr("Unable to start process for command '%1'").arg(cmd));
|
||||
}
|
||||
|
||||
if (!execProcess.waitForFinished(commandTimeoutSeconds * 1000 /* Milliseconds per second */))
|
||||
{
|
||||
return AZ::Failure(QObject::tr("Process for command '%1' timed out at %2 seconds").arg(cmd).arg(commandTimeoutSeconds));
|
||||
}
|
||||
int resultCode = execProcess.exitCode();
|
||||
if (resultCode != 0)
|
||||
{
|
||||
return AZ::Failure(QObject::tr("Process for command '%1' failed (result code %2").arg(cmd).arg(resultCode));
|
||||
}
|
||||
QString resultOutput = execProcess.readAllStandardOutput();
|
||||
return AZ::Success(resultOutput);
|
||||
}
|
||||
} // namespace ProjectUtils
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -9,8 +9,11 @@
|
||||
|
||||
#include <ScreenDefs.h>
|
||||
#include <ProjectInfo.h>
|
||||
#include <ProjectManagerDefs.h>
|
||||
|
||||
#include <QWidget>
|
||||
#include <QProcessEnvironment>
|
||||
|
||||
#include <AzCore/Outcome/Outcome.h>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
@@ -28,8 +31,17 @@ namespace O3DE::ProjectManager
|
||||
bool ReplaceProjectFile(const QString& origFile, const QString& newFile, QWidget* parent = nullptr, bool interactive = true);
|
||||
|
||||
bool FindSupportedCompiler(QWidget* parent = nullptr);
|
||||
AZ::Outcome<void, QString> FindSupportedCompilerForPlatform();
|
||||
AZ::Outcome<QString, QString> FindSupportedCompilerForPlatform();
|
||||
|
||||
ProjectManagerScreen GetProjectManagerScreen(const QString& screen);
|
||||
|
||||
AZ::Outcome<QString, QString> ExecuteCommandResult(
|
||||
const QString& cmd,
|
||||
const QStringList& arguments,
|
||||
const QProcessEnvironment& processEnv,
|
||||
int commandTimeoutSeconds = ProjectCommandLineTimeoutSeconds);
|
||||
|
||||
AZ::Outcome<QProcessEnvironment, QString> GetCommandLineProcessEnvironment();
|
||||
|
||||
} // namespace ProjectUtils
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -339,7 +339,7 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
for (auto engine : allEngines)
|
||||
{
|
||||
AZ::IO::FixedMaxPath enginePath(Py_To_String(engine["path"]));
|
||||
AZ::IO::FixedMaxPath enginePath(Py_To_String(engine));
|
||||
if (enginePath.Compare(m_enginePath) == 0)
|
||||
{
|
||||
return;
|
||||
|
||||
@@ -5,9 +5,19 @@
|
||||
"origin": "Amazon Web Services, Inc.",
|
||||
"type": "Code",
|
||||
"summary": "AWS Client Auth provides client authentication and AWS authorization solution.",
|
||||
"canonical_tags": ["Gem"],
|
||||
"user_tags": ["AWS", "Network", "SDK"],
|
||||
"canonical_tags": [
|
||||
"Gem"
|
||||
],
|
||||
"user_tags": [
|
||||
"AWS",
|
||||
"Network",
|
||||
"SDK"
|
||||
],
|
||||
"icon_path": "preview.png",
|
||||
"requirements": "",
|
||||
"documentation_url": "https://o3de.org/docs/user-guide/gems/reference/aws/aws-client-auth/"
|
||||
"documentation_url": "https://o3de.org/docs/user-guide/gems/reference/aws/aws-client-auth/",
|
||||
"dependencies": [
|
||||
"AWSCore",
|
||||
"HttpRequestor"
|
||||
]
|
||||
}
|
||||
|
||||
+10
-3
@@ -5,9 +5,16 @@
|
||||
"origin": "Amazon Web Services, Inc.",
|
||||
"type": "Code",
|
||||
"summary": "The AWS Core Gem provides basic shared AWS functionality such as AWS SDK initialization and client configuration, and is automatically added when selecting any AWS feature Gem.",
|
||||
"canonical_tags": ["Gem"],
|
||||
"user_tags": ["AWS", "Network", "SDK"],
|
||||
"canonical_tags": [
|
||||
"Gem"
|
||||
],
|
||||
"user_tags": [
|
||||
"AWS",
|
||||
"Network",
|
||||
"SDK"
|
||||
],
|
||||
"icon_path": "preview.png",
|
||||
"requirements": "",
|
||||
"documentation_url": "https://o3de.org/docs/user-guide/gems/reference/aws/aws-core/"
|
||||
"documentation_url": "https://o3de.org/docs/user-guide/gems/reference/aws/aws-core/",
|
||||
"dependencies": []
|
||||
}
|
||||
|
||||
@@ -5,9 +5,18 @@
|
||||
"origin": "Amazon Web Services, Inc.",
|
||||
"type": "Code",
|
||||
"summary": "The AWS GameLift Gem provides a framework to extend O3DE networking layer to work with GameLift resources via GameLift server and client SDK.",
|
||||
"canonical_tags": ["Gem"],
|
||||
"user_tags": ["AWS", "Framework", "Network"],
|
||||
"canonical_tags": [
|
||||
"Gem"
|
||||
],
|
||||
"user_tags": [
|
||||
"AWS",
|
||||
"Framework",
|
||||
"Network"
|
||||
],
|
||||
"icon_path": "preview.png",
|
||||
"requirements": "",
|
||||
"documentation_url": "https://o3de.org/docs/user-guide/gems/reference/aws/aws-gamelift/"
|
||||
"documentation_url": "https://o3de.org/docs/user-guide/gems/reference/aws/aws-gamelift/",
|
||||
"dependencies": [
|
||||
"AWSCore"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -5,9 +5,18 @@
|
||||
"origin": "Amazon Web Services, Inc.",
|
||||
"type": "Code",
|
||||
"summary": "The AWS Metrics Gem provides a solution for AWS metrics submission and analytics.",
|
||||
"canonical_tags": ["Gem"],
|
||||
"user_tags": ["AWS", "Network", "SDK"],
|
||||
"canonical_tags": [
|
||||
"Gem"
|
||||
],
|
||||
"user_tags": [
|
||||
"AWS",
|
||||
"Network",
|
||||
"SDK"
|
||||
],
|
||||
"icon_path": "preview.png",
|
||||
"requirements": "",
|
||||
"documentation_url": "https://o3de.org/docs/user-guide/gems/reference/aws/aws-metrics/"
|
||||
"documentation_url": "https://o3de.org/docs/user-guide/gems/reference/aws/aws-metrics/",
|
||||
"dependencies": [
|
||||
"AWSCore"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -5,9 +5,15 @@
|
||||
"origin": "Open 3D Engine - o3de.org",
|
||||
"type": "Code",
|
||||
"summary": "The Achievements Gem provides a target platform agnostic interface for retrieving achievement details and unlocking achievements.",
|
||||
"canonical_tags": ["Gem"],
|
||||
"user_tags": ["Gameplay", "Achievements"],
|
||||
"canonical_tags": [
|
||||
"Gem"
|
||||
],
|
||||
"user_tags": [
|
||||
"Gameplay",
|
||||
"Achievements"
|
||||
],
|
||||
"icon_path": "preview.png",
|
||||
"requirements": "",
|
||||
"documentation_url": "https://o3de.org/docs/user-guide/gems/reference/gameplay/achievements/"
|
||||
"documentation_url": "https://o3de.org/docs/user-guide/gems/reference/gameplay/achievements/",
|
||||
"dependencies": []
|
||||
}
|
||||
|
||||
@@ -5,9 +5,18 @@
|
||||
"origin": "Open 3D Engine - o3de.org",
|
||||
"type": "Code",
|
||||
"summary": "The Asset Memory Analyzer Gem provides tools to profile asset memory usage in Open 3D Engine through ImGUI (Immediate Mode Graphical User Interface).",
|
||||
"canonical_tags": ["Gem"],
|
||||
"user_tags": ["Debug", "Utility", "Tools"],
|
||||
"canonical_tags": [
|
||||
"Gem"
|
||||
],
|
||||
"user_tags": [
|
||||
"Debug",
|
||||
"Utility",
|
||||
"Tools"
|
||||
],
|
||||
"icon_path": "preview.png",
|
||||
"requirements": "",
|
||||
"documentation_url": "https://o3de.org/docs/user-guide/gems/reference/debug/asset-memory-analyzer/"
|
||||
"documentation_url": "https://o3de.org/docs/user-guide/gems/reference/debug/asset-memory-analyzer/",
|
||||
"dependencies": [
|
||||
"ImGui"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -5,9 +5,16 @@
|
||||
"origin": "Open 3D Engine - o3de.org",
|
||||
"type": "Code",
|
||||
"summary": "The Asset Validation Gem provides seed-related commands to ensure assets have valid seeds for asset bundling.",
|
||||
"canonical_tags": ["Gem"],
|
||||
"user_tags": ["Assets", "Utility", "Scripting"],
|
||||
"canonical_tags": [
|
||||
"Gem"
|
||||
],
|
||||
"user_tags": [
|
||||
"Assets",
|
||||
"Utility",
|
||||
"Scripting"
|
||||
],
|
||||
"icon_path": "preview.png",
|
||||
"requirements": "",
|
||||
"documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/asset-validation/"
|
||||
"documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/asset-validation/",
|
||||
"dependencies": []
|
||||
}
|
||||
|
||||
@@ -248,8 +248,8 @@ namespace ImageProcessingAtom
|
||||
{
|
||||
const uint8* data = buf;
|
||||
r = U8ToF32(data[0]);
|
||||
g = 0.f;
|
||||
b = 0.f;
|
||||
g = r;
|
||||
b = r;
|
||||
a = 1.f;
|
||||
}
|
||||
|
||||
@@ -333,8 +333,8 @@ namespace ImageProcessingAtom
|
||||
{
|
||||
const uint16* data = (uint16*)(buf);
|
||||
r = U16ToF32(data[0]);
|
||||
g = 0.f;
|
||||
b = 0.f;
|
||||
g = r;
|
||||
b = r;
|
||||
a = 1.f;
|
||||
}
|
||||
|
||||
@@ -418,8 +418,8 @@ namespace ImageProcessingAtom
|
||||
{
|
||||
const float* data = (float*)(buf);
|
||||
r = data[0];
|
||||
g = 0.f;
|
||||
b = 0.f;
|
||||
g = r;
|
||||
b = r;
|
||||
a = 1.f;
|
||||
}
|
||||
|
||||
@@ -485,8 +485,8 @@ namespace ImageProcessingAtom
|
||||
{
|
||||
const SHalf* data = (SHalf*)(buf);
|
||||
r = data[0];
|
||||
g = 0.f;
|
||||
b = 0.f;
|
||||
g = r;
|
||||
b = r;
|
||||
a = 1.f;
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
"_bc",
|
||||
"_diffuse"
|
||||
],
|
||||
"PixelFormat": "ETC2",
|
||||
"PixelFormat": "ASTC_6x6",
|
||||
"DiscardAlpha": true,
|
||||
"IsPowerOf2": true,
|
||||
"MipMapSetting": {
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
"_bc",
|
||||
"_diffuse"
|
||||
],
|
||||
"PixelFormat": "ETC2a1",
|
||||
"PixelFormat": "ASTC_6x6",
|
||||
"IsPowerOf2": true,
|
||||
"MipMapSetting": {
|
||||
"MipGenType": "Box"
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
"_bc",
|
||||
"_diffuse"
|
||||
],
|
||||
"PixelFormat": "ETC2a",
|
||||
"PixelFormat": "ASTC_6x6",
|
||||
"IsPowerOf2": true,
|
||||
"MipMapSetting": {
|
||||
"MipGenType": "Box"
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
"_bc",
|
||||
"_diffuse"
|
||||
],
|
||||
"PixelFormat": "ETC2a",
|
||||
"PixelFormat": "ASTC_6x6",
|
||||
"IsPowerOf2": true,
|
||||
"MipMapSetting": {
|
||||
"MipGenType": "Box"
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
"_amb",
|
||||
"_ambientocclusion"
|
||||
],
|
||||
"PixelFormat": "EAC_R11"
|
||||
"PixelFormat": "ASTC_4x4"
|
||||
},
|
||||
"ios": {
|
||||
"UUID": "{02ED0ECE-B198-49D9-85BC-CEBA6C28546C}",
|
||||
@@ -41,7 +41,7 @@
|
||||
"_amb",
|
||||
"_ambientocclusion"
|
||||
],
|
||||
"PixelFormat": "EAC_R11"
|
||||
"PixelFormat": "ASTC_4x4"
|
||||
},
|
||||
"mac": {
|
||||
"UUID": "{02ED0ECE-B198-49D9-85BC-CEBA6C28546C}",
|
||||
|
||||
@@ -15,14 +15,14 @@
|
||||
"UUID": "{884B5F7C-44AC-4E9E-8B8A-559D098BE2C7}",
|
||||
"Name": "CloudShadows",
|
||||
"DestColor": "Linear",
|
||||
"PixelFormat": "EAC_R11",
|
||||
"PixelFormat": "ASTC_4x4",
|
||||
"IsPowerOf2": true
|
||||
},
|
||||
"ios": {
|
||||
"UUID": "{884B5F7C-44AC-4E9E-8B8A-559D098BE2C7}",
|
||||
"Name": "CloudShadows",
|
||||
"DestColor": "Linear",
|
||||
"PixelFormat": "EAC_R11",
|
||||
"PixelFormat": "ASTC_4x4",
|
||||
"IsPowerOf2": true
|
||||
},
|
||||
"mac": {
|
||||
|
||||
@@ -24,13 +24,13 @@
|
||||
"FileMasks": [
|
||||
"_decal"
|
||||
],
|
||||
"PixelFormat": "ETC2a",
|
||||
"PixelFormat": "ASTC_4x4",
|
||||
"IsPowerOf2": true,
|
||||
"MipMapSetting": {
|
||||
"MipGenType": "Box"
|
||||
},
|
||||
// Decal Texture Arrays need all mips available immediately for packing.
|
||||
"NumberResidentMips": 255
|
||||
"NumberResidentMips": 255
|
||||
},
|
||||
"ios": {
|
||||
"UUID": "{E06B5087-2640-49B6-B9BA-D40048162B90}",
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@
|
||||
"FileMasks": [
|
||||
"_detail"
|
||||
],
|
||||
"PixelFormat": "ETC2a",
|
||||
"PixelFormat": "ASTC_4x4",
|
||||
"IsPowerOf2": true,
|
||||
"MipMapSetting": {
|
||||
"MipGenType": "Box"
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
"_ht",
|
||||
"_h"
|
||||
],
|
||||
"PixelFormat": "EAC_R11",
|
||||
"PixelFormat": "ASTC_4x4",
|
||||
"DiscardAlpha": true,
|
||||
"IsPowerOf2": true,
|
||||
"SizeReduceLevel": 3,
|
||||
@@ -70,7 +70,7 @@
|
||||
"_ht",
|
||||
"_h"
|
||||
],
|
||||
"PixelFormat": "EAC_R11",
|
||||
"PixelFormat": "ASTC_4x4",
|
||||
"DiscardAlpha": true,
|
||||
"IsPowerOf2": true,
|
||||
"MipMapSetting": {
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
"_em",
|
||||
"_emit"
|
||||
],
|
||||
"PixelFormat": "ETC2",
|
||||
"PixelFormat": "ASTC_6x6",
|
||||
"DiscardAlpha": true
|
||||
},
|
||||
"ios": {
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"FileMasks": [
|
||||
"_mask"
|
||||
],
|
||||
"PixelFormat": "EAC_R11",
|
||||
"PixelFormat": "ASTC_4x4",
|
||||
"IsPowerOf2": true,
|
||||
"MipMapSetting": {
|
||||
"MipGenType": "Box"
|
||||
@@ -40,7 +40,7 @@
|
||||
"FileMasks": [
|
||||
"_mask"
|
||||
],
|
||||
"PixelFormat": "EAC_R11",
|
||||
"PixelFormat": "ASTC_4x4",
|
||||
"IsPowerOf2": true,
|
||||
"MipMapSetting": {
|
||||
"MipGenType": "Box"
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"android": {
|
||||
"UUID": "{3000A993-0A04-4E08-A813-DFB1A47A0980}",
|
||||
"Name": "LensOptics",
|
||||
"PixelFormat": "ETC2"
|
||||
"PixelFormat": "ASTC_4x4"
|
||||
},
|
||||
"ios": {
|
||||
"UUID": "{3000A993-0A04-4E08-A813-DFB1A47A0980}",
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"UUID": "{1DFEF41A-D97F-40FB-99D3-C142A3E5225E}",
|
||||
"Name": "LightProjector",
|
||||
"DestColor": "Linear",
|
||||
"PixelFormat": "EAC_RG11",
|
||||
"PixelFormat": "ASTC_4x4",
|
||||
"IsPowerOf2": true,
|
||||
"MipMapSetting": {
|
||||
"MipGenType": "Box"
|
||||
@@ -28,7 +28,7 @@
|
||||
"UUID": "{1DFEF41A-D97F-40FB-99D3-C142A3E5225E}",
|
||||
"Name": "LightProjector",
|
||||
"DestColor": "Linear",
|
||||
"PixelFormat": "EAC_RG11",
|
||||
"PixelFormat": "ASTC_4x4",
|
||||
"IsPowerOf2": true,
|
||||
"MipMapSetting": {
|
||||
"MipGenType": "Box"
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"UUID": "{0D2F4C31-A665-4862-9C63-9E49A58E9A37}",
|
||||
"Name": "Minimap",
|
||||
"SuppressEngineReduce": true,
|
||||
"PixelFormat": "ETC2",
|
||||
"PixelFormat": "ASTC_6x6",
|
||||
"IsPowerOf2": true,
|
||||
"SizeReduceLevel": 1,
|
||||
"MipMapSetting": {
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"UUID": "{8BCC23A5-D08E-458E-B0B3-087C65FA1D31}",
|
||||
"Name": "MuzzleFlash",
|
||||
"SuppressEngineReduce": true,
|
||||
"PixelFormat": "ETC2",
|
||||
"PixelFormat": "ASTC_6x6",
|
||||
"IsPowerOf2": true,
|
||||
"MipMapSetting": {
|
||||
"MipGenType": "Box"
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
"_msk",
|
||||
"_blend"
|
||||
],
|
||||
"PixelFormat": "EAC_R11",
|
||||
"PixelFormat": "ASTC_4x4",
|
||||
"IsPowerOf2": true,
|
||||
"MipMapSetting": {
|
||||
"MipGenType": "Box"
|
||||
@@ -67,7 +67,7 @@
|
||||
"_msk",
|
||||
"_blend"
|
||||
],
|
||||
"PixelFormat": "EAC_R11",
|
||||
"PixelFormat": "ASTC_4x4",
|
||||
"IsPowerOf2": true,
|
||||
"MipMapSetting": {
|
||||
"MipGenType": "Box"
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
"_roughness",
|
||||
"_rough"
|
||||
],
|
||||
"PixelFormat": "ETC2",
|
||||
"PixelFormat": "ASTC_6x6",
|
||||
"IsPowerOf2": true,
|
||||
"MipMapSetting": {
|
||||
"MipGenType": "Box"
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
"FileMasks": [
|
||||
"_spec"
|
||||
],
|
||||
"PixelFormat": "ETC2a",
|
||||
"PixelFormat": "ASTC_4x4",
|
||||
"IsPowerOf2": true,
|
||||
"MipMapSetting": {
|
||||
"MipGenType": "Box"
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"_spec",
|
||||
"_refl"
|
||||
],
|
||||
"PixelFormat": "ETC2",
|
||||
"PixelFormat": "ASTC_4x4",
|
||||
"IsPowerOf2": true,
|
||||
"MipMapSetting": {
|
||||
"MipGenType": "Box"
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"SourceColor": "Linear",
|
||||
"DestColor": "Linear",
|
||||
"SuppressEngineReduce": true,
|
||||
"PixelFormat": "ETC2",
|
||||
"PixelFormat": "ASTC_4x4",
|
||||
"IsPowerOf2": true
|
||||
},
|
||||
"ios": {
|
||||
@@ -28,7 +28,7 @@
|
||||
"SourceColor": "Linear",
|
||||
"DestColor": "Linear",
|
||||
"SuppressEngineReduce": true,
|
||||
"PixelFormat": "PVRTC4",
|
||||
"PixelFormat": "ASTC_4x4",
|
||||
"IsPowerOf2": true
|
||||
},
|
||||
"mac": {
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"SourceColor": "Linear",
|
||||
"DestColor": "Linear",
|
||||
"SuppressEngineReduce": true,
|
||||
"PixelFormat": "ETC2"
|
||||
"PixelFormat": "ASTC_4x4"
|
||||
},
|
||||
"ios": {
|
||||
"UUID": "{C456B8AB-C360-4822-BCDD-225252D0E697}",
|
||||
@@ -26,7 +26,7 @@
|
||||
"SourceColor": "Linear",
|
||||
"DestColor": "Linear",
|
||||
"SuppressEngineReduce": true,
|
||||
"PixelFormat": "PVRTC4"
|
||||
"PixelFormat": "ASTC_4x4"
|
||||
},
|
||||
"mac": {
|
||||
"UUID": "{C456B8AB-C360-4822-BCDD-225252D0E697}",
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"Name": "Terrain_Albedo",
|
||||
"SourceColor": "Linear",
|
||||
"DestColor": "Linear",
|
||||
"PixelFormat": "ETC2",
|
||||
"PixelFormat": "ASTC_6x6",
|
||||
"IsPowerOf2": true,
|
||||
"HighPassMip": 5,
|
||||
"MipMapSetting": {
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"Name": "Terrain_Albedo_HighPassed",
|
||||
"SourceColor": "Linear",
|
||||
"DestColor": "Linear",
|
||||
"PixelFormat": "ETC2",
|
||||
"PixelFormat": "ASTC_6x6",
|
||||
"IsPowerOf2": true,
|
||||
"MipMapSetting": {
|
||||
"MipGenType": "Box"
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"UUID": "{2828FBFE-BDF9-45A7-9370-F93822719CCF}",
|
||||
"Name": "UserInterface_Compressed",
|
||||
"SuppressEngineReduce": true,
|
||||
"PixelFormat": "ETC2"
|
||||
"PixelFormat": "ASTC_6x6"
|
||||
},
|
||||
"ios": {
|
||||
"UUID": "{2828FBFE-BDF9-45A7-9370-F93822719CCF}",
|
||||
|
||||
@@ -8,7 +8,11 @@
|
||||
"canonical_tags": [
|
||||
"Gem"
|
||||
],
|
||||
"user_tags": [
|
||||
],
|
||||
"requirements": ""
|
||||
"user_tags": [],
|
||||
"requirements": "",
|
||||
"dependencies": [
|
||||
"Atom_RPI",
|
||||
"Atom_RHI",
|
||||
"Atom"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -8,7 +8,10 @@
|
||||
"canonical_tags": [
|
||||
"Gem"
|
||||
],
|
||||
"user_tags": [
|
||||
],
|
||||
"requirements": ""
|
||||
"user_tags": [],
|
||||
"requirements": "",
|
||||
"dependencies": [
|
||||
"Atom_RHI",
|
||||
"Atom_RPI"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -176,7 +176,10 @@ namespace AZ
|
||||
|
||||
m_isAssetCatalogLoaded = true;
|
||||
|
||||
RPI::RPISystemInterface::Get()->InitializeSystemAssets();
|
||||
if (!RPI::RPISystemInterface::Get()->IsInitialized())
|
||||
{
|
||||
RPI::RPISystemInterface::Get()->InitializeSystemAssets();
|
||||
}
|
||||
|
||||
if (!RPI::RPISystemInterface::Get()->IsInitialized())
|
||||
{
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
"canonical_tags": [
|
||||
"Gem"
|
||||
],
|
||||
"user_tags": [
|
||||
],
|
||||
"requirements": ""
|
||||
"user_tags": [],
|
||||
"requirements": "",
|
||||
"dependencies": [
|
||||
"Atom_RPI"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
"canonical_tags": [
|
||||
"Gem"
|
||||
],
|
||||
"user_tags": [
|
||||
],
|
||||
"requirements": ""
|
||||
"user_tags": [],
|
||||
"requirements": "",
|
||||
"dependencies": [
|
||||
"Atom_RPI"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -40,11 +40,10 @@ ShaderResourceGroup ObjectSrg : SRG_PerObject
|
||||
//! Reflection Probe (smallest probe volume that overlaps the object position)
|
||||
struct ReflectionProbeData
|
||||
{
|
||||
float3 m_aabbPos;
|
||||
float3 m_outerAabbMin;
|
||||
float3 m_outerAabbMax;
|
||||
float3 m_innerAabbMin;
|
||||
float3 m_innerAabbMax;
|
||||
row_major float3x4 m_modelToWorld;
|
||||
row_major float3x4 m_modelToWorldInverse; // does not include extents
|
||||
float3 m_outerObbHalfLengths;
|
||||
float3 m_innerObbHalfLengths;
|
||||
float m_padding;
|
||||
bool m_useReflectionProbe;
|
||||
bool m_useParallaxCorrection;
|
||||
@@ -52,4 +51,32 @@ ShaderResourceGroup ObjectSrg : SRG_PerObject
|
||||
|
||||
ReflectionProbeData m_reflectionProbeData;
|
||||
TextureCube m_reflectionProbeCubeMap;
|
||||
|
||||
float4x4 GetReflectionProbeWorldMatrix()
|
||||
{
|
||||
float4x4 modelToWorld = float4x4(
|
||||
float4(1, 0, 0, 0),
|
||||
float4(0, 1, 0, 0),
|
||||
float4(0, 0, 1, 0),
|
||||
float4(0, 0, 0, 1));
|
||||
|
||||
modelToWorld[0] = m_reflectionProbeData.m_modelToWorld[0];
|
||||
modelToWorld[1] = m_reflectionProbeData.m_modelToWorld[1];
|
||||
modelToWorld[2] = m_reflectionProbeData.m_modelToWorld[2];
|
||||
return modelToWorld;
|
||||
}
|
||||
|
||||
float4x4 GetReflectionProbeWorldMatrixInverse()
|
||||
{
|
||||
float4x4 modelToWorldInverse = float4x4(
|
||||
float4(1, 0, 0, 0),
|
||||
float4(0, 1, 0, 0),
|
||||
float4(0, 0, 1, 0),
|
||||
float4(0, 0, 0, 1));
|
||||
|
||||
modelToWorldInverse[0] = m_reflectionProbeData.m_modelToWorldInverse[0];
|
||||
modelToWorldInverse[1] = m_reflectionProbeData.m_modelToWorldInverse[1];
|
||||
modelToWorldInverse[2] = m_reflectionProbeData.m_modelToWorldInverse[2];
|
||||
return modelToWorldInverse;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,10 +50,10 @@ float GetRoughnessMip(float roughness)
|
||||
return roughness * maxRoughnessMip;
|
||||
}
|
||||
|
||||
// compute parallax corrected reflection vector
|
||||
// compute parallax corrected reflection vector, AABB version
|
||||
// we do this by finding the intersection with the volume and adjusting the reflection vector for the surface position
|
||||
// https://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/
|
||||
float3 ApplyParallaxCorrection(float3 aabbMin, float3 aabbMax, float3 aabbPos, float3 positionWS, float3 reflectDir)
|
||||
float3 ApplyParallaxCorrectionAABB(float3 aabbMin, float3 aabbMax, float3 aabbPos, float3 positionWS, float3 reflectDir)
|
||||
{
|
||||
float3 rcpReflectDir = 1.0f / reflectDir;
|
||||
float3 intersectA = (aabbMax - positionWS) * rcpReflectDir;
|
||||
@@ -63,3 +63,10 @@ float3 ApplyParallaxCorrection(float3 aabbMin, float3 aabbMax, float3 aabbPos, f
|
||||
float3 intersectPos = reflectDir * distance + positionWS;
|
||||
return (intersectPos - aabbPos);
|
||||
}
|
||||
|
||||
// compute parallax corrected reflection vector, OBB version
|
||||
float3 ApplyParallaxCorrectionOBB(float4x4 obbTransformInverse, float3 obbHalfExtents, float3 positionWS, float3 reflectDir)
|
||||
{
|
||||
float4 p = mul(obbTransformInverse, float4(positionWS, 1.0f));
|
||||
return ApplyParallaxCorrectionAABB(-obbHalfExtents, obbHalfExtents, float3(0.0f, 0.0f, 0.0f), p, reflectDir);
|
||||
}
|
||||
|
||||
@@ -48,10 +48,9 @@ float3 GetIblSpecular(
|
||||
{
|
||||
if (ObjectSrg::m_reflectionProbeData.m_useParallaxCorrection)
|
||||
{
|
||||
reflectDir = ApplyParallaxCorrection(
|
||||
ObjectSrg::m_reflectionProbeData.m_outerAabbMin,
|
||||
ObjectSrg::m_reflectionProbeData.m_outerAabbMax,
|
||||
ObjectSrg::m_reflectionProbeData.m_aabbPos,
|
||||
reflectDir = ApplyParallaxCorrectionOBB(
|
||||
ObjectSrg::GetReflectionProbeWorldMatrixInverse(),
|
||||
ObjectSrg::m_reflectionProbeData.m_outerObbHalfLengths,
|
||||
position,
|
||||
reflectDir);
|
||||
}
|
||||
@@ -60,11 +59,10 @@ float3 GetIblSpecular(
|
||||
probeSpecular *= (specularF0 * brdf.x + brdf.y);
|
||||
|
||||
// compute blend amount based on world position in the reflection probe volume
|
||||
float blendAmount = ComputeLerpBetweenInnerOuterAABBs(
|
||||
ObjectSrg::m_reflectionProbeData.m_innerAabbMin,
|
||||
ObjectSrg::m_reflectionProbeData.m_innerAabbMax,
|
||||
ObjectSrg::m_reflectionProbeData.m_outerAabbMax,
|
||||
ObjectSrg::m_reflectionProbeData.m_aabbPos,
|
||||
float blendAmount = ComputeLerpBetweenInnerOuterOBBs(
|
||||
ObjectSrg::GetReflectionProbeWorldMatrixInverse(),
|
||||
ObjectSrg::m_reflectionProbeData.m_innerObbHalfLengths,
|
||||
ObjectSrg::m_reflectionProbeData.m_outerObbHalfLengths,
|
||||
position);
|
||||
|
||||
outSpecular = lerp(outSpecular, probeSpecular, blendAmount);
|
||||
|
||||
@@ -63,7 +63,7 @@ PSOutput MainPS(VSOutput IN, in uint sampleIndex : SV_SampleIndex)
|
||||
// make sure the pixel belongs to this probe volume
|
||||
// this is necessary since it could have the correct stencil value but actually reside
|
||||
// in another volume that's in between the camera and the volume we're rendering
|
||||
if (!AabbContainsPoint(ObjectSrg::m_outerAabbMin, ObjectSrg::m_outerAabbMax, positionWS))
|
||||
if (!ObbContainsPoint(ObjectSrg::GetWorldMatrixInverse(), ObjectSrg::m_outerObbHalfLengths, positionWS))
|
||||
{
|
||||
discard;
|
||||
}
|
||||
@@ -71,11 +71,15 @@ PSOutput MainPS(VSOutput IN, in uint sampleIndex : SV_SampleIndex)
|
||||
// determine blend based on position with respect to the inner and outer AABBs
|
||||
// if it's inside the inner AABB it blends at 100%, otherwise it's the percentage of the distance between the inner/outer AABB
|
||||
float blendWeight = 1.0f;
|
||||
if (!AabbContainsPoint(ObjectSrg::m_innerAabbMin, ObjectSrg::m_innerAabbMax, positionWS))
|
||||
if (!ObbContainsPoint(ObjectSrg::GetWorldMatrixInverse(), ObjectSrg::m_innerObbHalfLengths, positionWS))
|
||||
{
|
||||
// not inside the inner AABB, so it's in between the inner and outer AABBs
|
||||
// compute blend amount based on the distance to the outer AABB
|
||||
blendWeight = ComputeLerpBetweenInnerOuterAABBs(ObjectSrg::m_innerAabbMin, ObjectSrg::m_innerAabbMax, ObjectSrg::m_outerAabbMax, ObjectSrg::m_aabbPos, positionWS);
|
||||
blendWeight = ComputeLerpBetweenInnerOuterOBBs(
|
||||
ObjectSrg::GetWorldMatrixInverse(),
|
||||
ObjectSrg::m_innerObbHalfLengths,
|
||||
ObjectSrg::m_outerObbHalfLengths,
|
||||
positionWS);
|
||||
}
|
||||
|
||||
// write the blend weight (additive) at this position for the probe volume
|
||||
|
||||
+7
-3
@@ -12,12 +12,12 @@
|
||||
#include <Atom/Features/PBR/Microfacet/Fresnel.azsli>
|
||||
|
||||
// compute final probe specular using the probe cubemap and the roughness, normals, and specularF0 for the surface
|
||||
bool ComputeProbeSpecular(float2 screenCoords, float3 positionWS, float3 aabbMin, float3 aabbMax, uint sampleIndex, out float3 specular)
|
||||
bool ComputeProbeSpecular(float2 screenCoords, float3 positionWS, float4x4 obbTransformInverse, float3 outerObbHalfLengths, uint sampleIndex, out float3 specular)
|
||||
{
|
||||
// make sure the pixel belongs to this probe volume
|
||||
// this is necessary since it could have the correct stencil value but actually reside
|
||||
// in another volume that's in between the camera and the volume we're rendering
|
||||
if (!AabbContainsPoint(aabbMin, aabbMax, positionWS))
|
||||
if (!ObbContainsPoint(obbTransformInverse, outerObbHalfLengths, positionWS))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -47,7 +47,11 @@ bool ComputeProbeSpecular(float2 screenCoords, float3 positionWS, float3 aabbMin
|
||||
float3 localReflectDir = reflectDir;
|
||||
if (ObjectSrg::m_useParallaxCorrection)
|
||||
{
|
||||
localReflectDir = ApplyParallaxCorrection(ObjectSrg::m_outerAabbMin, ObjectSrg::m_outerAabbMax, ObjectSrg::m_aabbPos, positionWS, reflectDir);
|
||||
localReflectDir = ApplyParallaxCorrectionOBB(
|
||||
ObjectSrg::GetWorldMatrixInverse(),
|
||||
ObjectSrg::m_outerObbHalfLengths,
|
||||
positionWS,
|
||||
reflectDir);
|
||||
}
|
||||
|
||||
// sample reflection cubemap with the appropriate roughness mip
|
||||
|
||||
@@ -75,7 +75,7 @@ PSOutput MainPS(VSOutput IN, in uint sampleIndex : SV_SampleIndex)
|
||||
|
||||
// compute specular using the probe cubemap and the roughness, normals, and specularF0 for the surface
|
||||
float3 specular = float3(0.0f, 0.0f, 0.0f);
|
||||
if (!ComputeProbeSpecular(IN.m_position.xy, positionWS, ObjectSrg::m_innerAabbMin, ObjectSrg::m_innerAabbMax, sampleIndex, specular))
|
||||
if (!ComputeProbeSpecular(IN.m_position.xy, positionWS, ObjectSrg::GetWorldMatrixInverse(), ObjectSrg::m_innerObbHalfLengths, sampleIndex, specular))
|
||||
{
|
||||
discard;
|
||||
}
|
||||
|
||||
+17
-6
@@ -13,12 +13,9 @@
|
||||
ShaderResourceGroup ObjectSrg : SRG_PerObject
|
||||
{
|
||||
row_major float3x4 m_modelToWorld;
|
||||
|
||||
float3 m_aabbPos;
|
||||
float3 m_outerAabbMin;
|
||||
float3 m_outerAabbMax;
|
||||
float3 m_innerAabbMin;
|
||||
float3 m_innerAabbMax;
|
||||
row_major float3x4 m_modelToWorldInverse; // does not include extents
|
||||
float3 m_outerObbHalfLengths;
|
||||
float3 m_innerObbHalfLengths;
|
||||
bool m_useParallaxCorrection;
|
||||
TextureCube m_reflectionCubeMap;
|
||||
|
||||
@@ -35,4 +32,18 @@ ShaderResourceGroup ObjectSrg : SRG_PerObject
|
||||
modelToWorld[2] = ObjectSrg::m_modelToWorld[2];
|
||||
return modelToWorld;
|
||||
}
|
||||
|
||||
float4x4 GetWorldMatrixInverse()
|
||||
{
|
||||
float4x4 modelToWorldInverse = float4x4(
|
||||
float4(1, 0, 0, 0),
|
||||
float4(0, 1, 0, 0),
|
||||
float4(0, 0, 1, 0),
|
||||
float4(0, 0, 0, 1));
|
||||
|
||||
modelToWorldInverse[0] = ObjectSrg::m_modelToWorldInverse[0];
|
||||
modelToWorldInverse[1] = ObjectSrg::m_modelToWorldInverse[1];
|
||||
modelToWorldInverse[2] = ObjectSrg::m_modelToWorldInverse[2];
|
||||
return modelToWorldInverse;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ PSOutput MainPS(VSOutput IN, in uint sampleIndex : SV_SampleIndex)
|
||||
|
||||
// compute specular using the probe cubemap and the roughness, normals, and specularF0 for the surface
|
||||
float3 specular = float3(0.0f, 0.0f, 0.0f);
|
||||
if (!ComputeProbeSpecular(IN.m_position.xy, positionWS, ObjectSrg::m_outerAabbMin, ObjectSrg::m_outerAabbMax, sampleIndex, specular))
|
||||
if (!ComputeProbeSpecular(IN.m_position.xy, positionWS, ObjectSrg::GetWorldMatrixInverse(), ObjectSrg::m_outerObbHalfLengths, sampleIndex, specular))
|
||||
{
|
||||
discard;
|
||||
}
|
||||
@@ -85,13 +85,17 @@ PSOutput MainPS(VSOutput IN, in uint sampleIndex : SV_SampleIndex)
|
||||
// determine blend based on position with respect to the inner and outer AABBs
|
||||
// if it's inside the inner AABB it blends at 100%, otherwise it's the percentage of the distance between the inner/outer AABB
|
||||
float blendWeight = 1.0f;
|
||||
if (!AabbContainsPoint(ObjectSrg::m_innerAabbMin, ObjectSrg::m_innerAabbMax, positionWS))
|
||||
if (!ObbContainsPoint(ObjectSrg::GetWorldMatrixInverse(), ObjectSrg::m_innerObbHalfLengths, positionWS))
|
||||
{
|
||||
// not inside the inner AABB, so it's in between the inner and outer AABBs
|
||||
// compute blend amount based on the distance to the outer AABB
|
||||
blendWeight = ComputeLerpBetweenInnerOuterAABBs(ObjectSrg::m_innerAabbMin, ObjectSrg::m_innerAabbMax, ObjectSrg::m_outerAabbMax, ObjectSrg::m_aabbPos, positionWS);
|
||||
blendWeight = ComputeLerpBetweenInnerOuterOBBs(
|
||||
ObjectSrg::GetWorldMatrixInverse(),
|
||||
ObjectSrg::m_innerObbHalfLengths,
|
||||
ObjectSrg::m_outerObbHalfLengths,
|
||||
positionWS);
|
||||
}
|
||||
|
||||
|
||||
// retrieve the blend weight of all probes at this location
|
||||
float blendWeightAllProbes = PassSrg::m_blendWeight.Load(IN.m_position.xy, sampleIndex).r;
|
||||
|
||||
|
||||
@@ -1111,20 +1111,17 @@ namespace AZ
|
||||
if (reflectionProbeFeatureProcessor && (m_descriptor.m_useForwardPassIblSpecular || m_hasForwardPassIblSpecularMaterial))
|
||||
{
|
||||
// retrieve probe constant indices
|
||||
AZ::RHI::ShaderInputConstantIndex posConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_aabbPos"));
|
||||
AZ_Error("MeshDataInstance", posConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index");
|
||||
AZ::RHI::ShaderInputConstantIndex modelToWorldConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_modelToWorld"));
|
||||
AZ_Error("MeshDataInstance", modelToWorldConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index");
|
||||
|
||||
AZ::RHI::ShaderInputConstantIndex outerAabbMinConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_outerAabbMin"));
|
||||
AZ_Error("MeshDataInstance", outerAabbMinConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index");
|
||||
AZ::RHI::ShaderInputConstantIndex modelToWorldInverseConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_modelToWorldInverse"));
|
||||
AZ_Error("MeshDataInstance", modelToWorldInverseConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index");
|
||||
|
||||
AZ::RHI::ShaderInputConstantIndex outerAabbMaxConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_outerAabbMax"));
|
||||
AZ_Error("MeshDataInstance", outerAabbMaxConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index");
|
||||
AZ::RHI::ShaderInputConstantIndex outerObbHalfLengthsConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_outerObbHalfLengths"));
|
||||
AZ_Error("MeshDataInstance", outerObbHalfLengthsConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index");
|
||||
|
||||
AZ::RHI::ShaderInputConstantIndex innerAabbMinConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_innerAabbMin"));
|
||||
AZ_Error("MeshDataInstance", innerAabbMinConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index");
|
||||
|
||||
AZ::RHI::ShaderInputConstantIndex innerAabbMaxConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_innerAabbMax"));
|
||||
AZ_Error("MeshDataInstance", innerAabbMaxConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index");
|
||||
AZ::RHI::ShaderInputConstantIndex innerObbHalfLengthsConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_innerObbHalfLengths"));
|
||||
AZ_Error("MeshDataInstance", innerObbHalfLengthsConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index");
|
||||
|
||||
AZ::RHI::ShaderInputConstantIndex useReflectionProbeConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_useReflectionProbe"));
|
||||
AZ_Error("MeshDataInstance", useReflectionProbeConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index");
|
||||
@@ -1146,11 +1143,10 @@ namespace AZ
|
||||
|
||||
if (!reflectionProbes.empty() && reflectionProbes[0])
|
||||
{
|
||||
m_shaderResourceGroup->SetConstant(posConstantIndex, reflectionProbes[0]->GetPosition());
|
||||
m_shaderResourceGroup->SetConstant(outerAabbMinConstantIndex, reflectionProbes[0]->GetOuterAabbWs().GetMin());
|
||||
m_shaderResourceGroup->SetConstant(outerAabbMaxConstantIndex, reflectionProbes[0]->GetOuterAabbWs().GetMax());
|
||||
m_shaderResourceGroup->SetConstant(innerAabbMinConstantIndex, reflectionProbes[0]->GetInnerAabbWs().GetMin());
|
||||
m_shaderResourceGroup->SetConstant(innerAabbMaxConstantIndex, reflectionProbes[0]->GetInnerAabbWs().GetMax());
|
||||
m_shaderResourceGroup->SetConstant(modelToWorldConstantIndex, reflectionProbes[0]->GetTransform());
|
||||
m_shaderResourceGroup->SetConstant(modelToWorldInverseConstantIndex, Matrix3x4::CreateFromTransform(reflectionProbes[0]->GetTransform()).GetInverseFull());
|
||||
m_shaderResourceGroup->SetConstant(outerObbHalfLengthsConstantIndex, reflectionProbes[0]->GetOuterObbWs().GetHalfLengths());
|
||||
m_shaderResourceGroup->SetConstant(innerObbHalfLengthsConstantIndex, reflectionProbes[0]->GetInnerObbWs().GetHalfLengths());
|
||||
m_shaderResourceGroup->SetConstant(useReflectionProbeConstantIndex, true);
|
||||
m_shaderResourceGroup->SetConstant(useParallaxCorrectionConstantIndex, reflectionProbes[0]->GetUseParallaxCorrection());
|
||||
|
||||
|
||||
@@ -138,43 +138,39 @@ namespace AZ
|
||||
if (m_updateSrg)
|
||||
{
|
||||
// stencil Srg
|
||||
// Note: the stencil pass uses a slightly reduced inner AABB to avoid seams
|
||||
// Note: the stencil pass uses a slightly reduced inner OBB to avoid seams
|
||||
Vector3 innerExtentsReduced = m_innerExtents - Vector3(0.1f, 0.1f, 0.1f);
|
||||
Matrix3x4 modelToWorldStencil = Matrix3x4::CreateFromMatrix3x3AndTranslation(Matrix3x3::CreateIdentity(), m_transform.GetTranslation()) * Matrix3x4::CreateScale(innerExtentsReduced);
|
||||
Matrix3x4 modelToWorldStencil = Matrix3x4::CreateFromQuaternionAndTranslation(m_transform.GetRotation(), m_transform.GetTranslation()) * Matrix3x4::CreateScale(innerExtentsReduced);
|
||||
m_stencilSrg->SetConstant(m_reflectionRenderData->m_modelToWorldStencilConstantIndex, modelToWorldStencil);
|
||||
m_stencilSrg->Compile();
|
||||
|
||||
Matrix3x4 modelToWorldInverse = Matrix3x4::CreateFromTransform(m_transform).GetInverseFull();
|
||||
|
||||
// blend weight Srg
|
||||
Matrix3x4 modelToWorldOuter = Matrix3x4::CreateFromMatrix3x3AndTranslation(Matrix3x3::CreateIdentity(), m_transform.GetTranslation()) * Matrix3x4::CreateScale(m_outerExtents);
|
||||
Matrix3x4 modelToWorldOuter = Matrix3x4::CreateFromQuaternionAndTranslation(m_transform.GetRotation(), m_transform.GetTranslation()) * Matrix3x4::CreateScale(m_outerExtents);
|
||||
m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_modelToWorldRenderConstantIndex, modelToWorldOuter);
|
||||
m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_aabbPosRenderConstantIndex, m_outerAabbWs.GetCenter());
|
||||
m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_outerAabbMinRenderConstantIndex, m_outerAabbWs.GetMin());
|
||||
m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_outerAabbMaxRenderConstantIndex, m_outerAabbWs.GetMax());
|
||||
m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_innerAabbMinRenderConstantIndex, m_innerAabbWs.GetMin());
|
||||
m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_innerAabbMaxRenderConstantIndex, m_innerAabbWs.GetMax());
|
||||
m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_modelToWorldInverseRenderConstantIndex, modelToWorldInverse);
|
||||
m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_outerObbHalfLengthsRenderConstantIndex, m_outerObbWs.GetHalfLengths());
|
||||
m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_innerObbHalfLengthsRenderConstantIndex, m_innerObbWs.GetHalfLengths());
|
||||
m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_useParallaxCorrectionRenderConstantIndex, m_useParallaxCorrection);
|
||||
m_blendWeightSrg->SetImage(m_reflectionRenderData->m_reflectionCubeMapRenderImageIndex, m_cubeMapImage);
|
||||
m_blendWeightSrg->Compile();
|
||||
|
||||
// render outer Srg
|
||||
m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_modelToWorldRenderConstantIndex, modelToWorldOuter);
|
||||
m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_aabbPosRenderConstantIndex, m_outerAabbWs.GetCenter());
|
||||
m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_outerAabbMinRenderConstantIndex, m_outerAabbWs.GetMin());
|
||||
m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_outerAabbMaxRenderConstantIndex, m_outerAabbWs.GetMax());
|
||||
m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_innerAabbMinRenderConstantIndex, m_innerAabbWs.GetMin());
|
||||
m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_innerAabbMaxRenderConstantIndex, m_innerAabbWs.GetMax());
|
||||
m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_modelToWorldInverseRenderConstantIndex, modelToWorldInverse);
|
||||
m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_outerObbHalfLengthsRenderConstantIndex, m_outerObbWs.GetHalfLengths());
|
||||
m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_innerObbHalfLengthsRenderConstantIndex, m_innerObbWs.GetHalfLengths());
|
||||
m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_useParallaxCorrectionRenderConstantIndex, m_useParallaxCorrection);
|
||||
m_renderOuterSrg->SetImage(m_reflectionRenderData->m_reflectionCubeMapRenderImageIndex, m_cubeMapImage);
|
||||
m_renderOuterSrg->Compile();
|
||||
|
||||
// render inner Srg
|
||||
Matrix3x4 modelToWorldInner = Matrix3x4::CreateFromMatrix3x3AndTranslation(Matrix3x3::CreateIdentity(), m_transform.GetTranslation()) * Matrix3x4::CreateScale(m_innerExtents);
|
||||
Matrix3x4 modelToWorldInner = Matrix3x4::CreateFromQuaternionAndTranslation(m_transform.GetRotation(), m_transform.GetTranslation()) * Matrix3x4::CreateScale(m_innerExtents);
|
||||
m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_modelToWorldRenderConstantIndex, modelToWorldInner);
|
||||
m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_aabbPosRenderConstantIndex, m_outerAabbWs.GetCenter());
|
||||
m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_outerAabbMinRenderConstantIndex, m_outerAabbWs.GetMin());
|
||||
m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_outerAabbMaxRenderConstantIndex, m_outerAabbWs.GetMax());
|
||||
m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_innerAabbMinRenderConstantIndex, m_innerAabbWs.GetMin());
|
||||
m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_innerAabbMaxRenderConstantIndex, m_innerAabbWs.GetMax());
|
||||
m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_modelToWorldInverseRenderConstantIndex, modelToWorldInverse);
|
||||
m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_outerObbHalfLengthsRenderConstantIndex, m_outerObbWs.GetHalfLengths());
|
||||
m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_innerObbHalfLengthsRenderConstantIndex, m_innerObbWs.GetHalfLengths());
|
||||
m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_useParallaxCorrectionRenderConstantIndex, m_useParallaxCorrection);
|
||||
m_renderInnerSrg->SetImage(m_reflectionRenderData->m_reflectionCubeMapRenderImageIndex, m_cubeMapImage);
|
||||
m_renderInnerSrg->Compile();
|
||||
@@ -244,22 +240,22 @@ namespace AZ
|
||||
m_outerExtents *= m_transform.GetUniformScale();
|
||||
m_innerExtents *= m_transform.GetUniformScale();
|
||||
|
||||
m_outerAabbWs = Aabb::CreateCenterHalfExtents(m_transform.GetTranslation(), m_outerExtents / 2.0f);
|
||||
m_innerAabbWs = Aabb::CreateCenterHalfExtents(m_transform.GetTranslation(), m_innerExtents / 2.0f);
|
||||
m_outerObbWs = Obb::CreateFromPositionRotationAndHalfLengths(m_transform.GetTranslation(), m_transform.GetRotation(), m_outerExtents / 2.0f);
|
||||
m_innerObbWs = Obb::CreateFromPositionRotationAndHalfLengths(m_transform.GetTranslation(), m_transform.GetRotation(), m_innerExtents / 2.0f);
|
||||
m_updateSrg = true;
|
||||
}
|
||||
|
||||
void ReflectionProbe::SetOuterExtents(const AZ::Vector3& outerExtents)
|
||||
{
|
||||
m_outerExtents = outerExtents * m_transform.GetUniformScale();
|
||||
m_outerAabbWs = Aabb::CreateCenterHalfExtents(m_transform.GetTranslation(), m_outerExtents / 2.0f);
|
||||
m_outerObbWs = Obb::CreateFromPositionRotationAndHalfLengths(m_transform.GetTranslation(), m_transform.GetRotation(), m_outerExtents / 2.0f);
|
||||
m_updateSrg = true;
|
||||
}
|
||||
|
||||
void ReflectionProbe::SetInnerExtents(const AZ::Vector3& innerExtents)
|
||||
{
|
||||
m_innerExtents = innerExtents * m_transform.GetUniformScale();
|
||||
m_innerAabbWs = Aabb::CreateCenterHalfExtents(m_transform.GetTranslation(), m_innerExtents / 2.0f);
|
||||
m_innerObbWs = Obb::CreateFromPositionRotationAndHalfLengths(m_transform.GetTranslation(), m_transform.GetRotation(), m_innerExtents / 2.0f);
|
||||
m_updateSrg = true;
|
||||
}
|
||||
|
||||
@@ -410,13 +406,14 @@ namespace AZ
|
||||
lod.m_screenCoverageMax = 1.0f;
|
||||
|
||||
// update cullable bounds
|
||||
Aabb outerAabb = Aabb::CreateFromObb(m_outerObbWs);
|
||||
Vector3 center;
|
||||
float radius;
|
||||
m_outerAabbWs.GetAsSphere(center, radius);
|
||||
outerAabb.GetAsSphere(center, radius);
|
||||
|
||||
m_cullable.m_cullData.m_boundingSphere = Sphere(center, radius);
|
||||
m_cullable.m_cullData.m_boundingObb = m_outerAabbWs.GetTransformedObb(AZ::Transform::CreateIdentity());
|
||||
m_cullable.m_cullData.m_visibilityEntry.m_boundingVolume = m_outerAabbWs;
|
||||
m_cullable.m_cullData.m_boundingObb = m_outerObbWs;
|
||||
m_cullable.m_cullData.m_visibilityEntry.m_boundingVolume = outerAabb;
|
||||
m_cullable.m_cullData.m_visibilityEntry.m_userData = &m_cullable;
|
||||
m_cullable.m_cullData.m_visibilityEntry.m_typeFlags = AzFramework::VisibilityEntry::TYPE_RPI_Cullable;
|
||||
|
||||
|
||||
@@ -55,15 +55,13 @@ namespace AZ
|
||||
RHI::DrawListTag m_renderOuterDrawListTag;
|
||||
RHI::DrawListTag m_renderInnerDrawListTag;
|
||||
|
||||
RHI::ShaderInputConstantIndex m_modelToWorldStencilConstantIndex;
|
||||
RHI::ShaderInputConstantIndex m_modelToWorldRenderConstantIndex;
|
||||
RHI::ShaderInputConstantIndex m_aabbPosRenderConstantIndex;
|
||||
RHI::ShaderInputConstantIndex m_outerAabbMinRenderConstantIndex;
|
||||
RHI::ShaderInputConstantIndex m_outerAabbMaxRenderConstantIndex;
|
||||
RHI::ShaderInputConstantIndex m_innerAabbMinRenderConstantIndex;
|
||||
RHI::ShaderInputConstantIndex m_innerAabbMaxRenderConstantIndex;
|
||||
RHI::ShaderInputConstantIndex m_useParallaxCorrectionRenderConstantIndex;
|
||||
RHI::ShaderInputImageIndex m_reflectionCubeMapRenderImageIndex;
|
||||
RHI::ShaderInputNameIndex m_modelToWorldStencilConstantIndex = "m_modelToWorld";
|
||||
RHI::ShaderInputNameIndex m_modelToWorldRenderConstantIndex = "m_modelToWorld";
|
||||
RHI::ShaderInputNameIndex m_modelToWorldInverseRenderConstantIndex = "m_modelToWorldInverse";
|
||||
RHI::ShaderInputNameIndex m_outerObbHalfLengthsRenderConstantIndex = "m_outerObbHalfLengths";
|
||||
RHI::ShaderInputNameIndex m_innerObbHalfLengthsRenderConstantIndex = "m_innerObbHalfLengths";
|
||||
RHI::ShaderInputNameIndex m_useParallaxCorrectionRenderConstantIndex = "m_useParallaxCorrection";
|
||||
RHI::ShaderInputNameIndex m_reflectionCubeMapRenderImageIndex = "m_reflectionCubeMap";
|
||||
};
|
||||
|
||||
// ReflectionProbe manages all aspects of a single probe, including rendering, visualization, and cubemap generation
|
||||
@@ -78,6 +76,7 @@ namespace AZ
|
||||
void Simulate(uint32_t probeIndex);
|
||||
|
||||
const Vector3& GetPosition() const { return m_transform.GetTranslation(); }
|
||||
const AZ::Transform& GetTransform() const { return m_transform; }
|
||||
void SetTransform(const AZ::Transform& transform);
|
||||
|
||||
const AZ::Vector3& GetOuterExtents() const { return m_outerExtents; }
|
||||
@@ -86,8 +85,8 @@ namespace AZ
|
||||
const AZ::Vector3& GetInnerExtents() const { return m_innerExtents; }
|
||||
void SetInnerExtents(const AZ::Vector3& innerExtents);
|
||||
|
||||
const Aabb& GetOuterAabbWs() const { return m_outerAabbWs; }
|
||||
const Aabb& GetInnerAabbWs() const { return m_innerAabbWs; }
|
||||
const Obb& GetOuterObbWs() const { return m_outerObbWs; }
|
||||
const Obb& GetInnerObbWs() const { return m_innerObbWs; }
|
||||
|
||||
const Data::Instance<RPI::Image>& GetCubeMapImage() const { return m_cubeMapImage; }
|
||||
void SetCubeMapImage(const Data::Instance<RPI::Image>& cubeMapImage, const AZStd::string& relativePath);
|
||||
@@ -133,9 +132,9 @@ namespace AZ
|
||||
AZ::Vector3 m_outerExtents = AZ::Vector3(0.0f, 0.0f, 0.0f);
|
||||
AZ::Vector3 m_innerExtents = AZ::Vector3(0.0f, 0.0f, 0.0f);
|
||||
|
||||
// probe volume AABBs (world space), built from position and extents
|
||||
Aabb m_outerAabbWs;
|
||||
Aabb m_innerAabbWs;
|
||||
// probe volume OBBs (world space), built from position and extents
|
||||
Obb m_outerObbWs;
|
||||
Obb m_innerObbWs;
|
||||
|
||||
// cubemap
|
||||
Data::Instance<RPI::Image> m_cubeMapImage;
|
||||
|
||||
+6
-60
@@ -77,61 +77,6 @@ namespace AZ
|
||||
m_reflectionRenderData.m_renderInnerSrgLayout,
|
||||
m_reflectionRenderData.m_renderInnerDrawListTag);
|
||||
|
||||
// create ShaderResourceGroups here so we can get the layout and cache the indices
|
||||
// Note: the SRGs are not needed beyond this method since each probe creates its own SRGs, we are just interested in the indices
|
||||
|
||||
// cache probe stencil shader indices
|
||||
Data::Instance<RPI::ShaderResourceGroup> stencilSrg = RPI::ShaderResourceGroup::Create(
|
||||
m_reflectionRenderData.m_stencilShader->GetAsset(),
|
||||
m_reflectionRenderData.m_stencilShader->GetSupervariantIndex(),
|
||||
m_reflectionRenderData.m_stencilSrgLayout->GetName());
|
||||
AZ_Error("ReflectionProbeFeatureProcessor", stencilSrg.get(), "Failed to create stencil back face shader resource group");
|
||||
|
||||
const RHI::ShaderResourceGroupLayout* stencilSrgLayout = stencilSrg->GetLayout();
|
||||
Name modelToWorldConstantName = Name("m_modelToWorld");
|
||||
m_reflectionRenderData.m_modelToWorldStencilConstantIndex = stencilSrgLayout->FindShaderInputConstantIndex(modelToWorldConstantName);
|
||||
AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_modelToWorldStencilConstantIndex.IsValid(), "Failed to find stencil shader input constant [%s]", modelToWorldConstantName.GetCStr());
|
||||
|
||||
// cache probe render shader indices
|
||||
// Note: the outer and inner render shaders use the same Srg
|
||||
Data::Instance<RPI::ShaderResourceGroup> renderReflectionSrg = RPI::ShaderResourceGroup::Create(
|
||||
m_reflectionRenderData.m_renderOuterShader->GetAsset(),
|
||||
m_reflectionRenderData.m_renderOuterShader->GetSupervariantIndex(),
|
||||
m_reflectionRenderData.m_renderOuterSrgLayout->GetName());
|
||||
AZ_Error("ReflectionProbeFeatureProcessor", renderReflectionSrg.get(), "Failed to create render reflection shader resource group");
|
||||
|
||||
const RHI::ShaderResourceGroupLayout* renderReflectionSrgLayout = renderReflectionSrg->GetLayout();
|
||||
m_reflectionRenderData.m_modelToWorldRenderConstantIndex = renderReflectionSrgLayout->FindShaderInputConstantIndex(modelToWorldConstantName);
|
||||
AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_modelToWorldRenderConstantIndex.IsValid(), "Failed to find render shader input constant [%s]", modelToWorldConstantName.GetCStr());
|
||||
|
||||
Name aabbPosConstantName = Name("m_aabbPos");
|
||||
m_reflectionRenderData.m_aabbPosRenderConstantIndex = renderReflectionSrgLayout->FindShaderInputConstantIndex(aabbPosConstantName);
|
||||
AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_aabbPosRenderConstantIndex.IsValid(), "Failed to find render shader input constant [%s]", aabbPosConstantName.GetCStr());
|
||||
|
||||
Name outerAabbMinConstantName = Name("m_outerAabbMin");
|
||||
m_reflectionRenderData.m_outerAabbMinRenderConstantIndex = renderReflectionSrgLayout->FindShaderInputConstantIndex(outerAabbMinConstantName);
|
||||
AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_outerAabbMinRenderConstantIndex.IsValid(), "Failed to find render shader input constant [%s]", outerAabbMinConstantName.GetCStr());
|
||||
|
||||
Name outerAabbMaxConstantName = Name("m_outerAabbMax");
|
||||
m_reflectionRenderData.m_outerAabbMaxRenderConstantIndex = renderReflectionSrgLayout->FindShaderInputConstantIndex(outerAabbMaxConstantName);
|
||||
AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_outerAabbMaxRenderConstantIndex.IsValid(), "Failed to find render shader input constant [%s]", outerAabbMaxConstantName.GetCStr());
|
||||
|
||||
Name innerAabbMinConstantName = Name("m_innerAabbMin");
|
||||
m_reflectionRenderData.m_innerAabbMinRenderConstantIndex = renderReflectionSrgLayout->FindShaderInputConstantIndex(innerAabbMinConstantName);
|
||||
AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_innerAabbMinRenderConstantIndex.IsValid(), "Failed to find render shader input constant [%s]", innerAabbMinConstantName.GetCStr());
|
||||
|
||||
Name innerAabbMaxConstantName = Name("m_innerAabbMax");
|
||||
m_reflectionRenderData.m_innerAabbMaxRenderConstantIndex = renderReflectionSrgLayout->FindShaderInputConstantIndex(innerAabbMaxConstantName);
|
||||
AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_innerAabbMaxRenderConstantIndex.IsValid(), "Failed to find render shader input constant [%s]", innerAabbMaxConstantName.GetCStr());
|
||||
|
||||
Name useParallaxCorrectionConstantName = Name("m_useParallaxCorrection");
|
||||
m_reflectionRenderData.m_useParallaxCorrectionRenderConstantIndex = renderReflectionSrgLayout->FindShaderInputConstantIndex(useParallaxCorrectionConstantName);
|
||||
AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_useParallaxCorrectionRenderConstantIndex.IsValid(), "Failed to find render shader input constant [%s]", useParallaxCorrectionConstantName.GetCStr());
|
||||
|
||||
Name reflectionCubeMapImageName = Name("m_reflectionCubeMap");
|
||||
m_reflectionRenderData.m_reflectionCubeMapRenderImageIndex = renderReflectionSrgLayout->FindShaderInputImageIndex(reflectionCubeMapImageName);
|
||||
AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_reflectionCubeMapRenderImageIndex.IsValid(), "Failed to find render shader input image [%s]", reflectionCubeMapImageName.GetCStr());
|
||||
|
||||
EnableSceneNotification();
|
||||
}
|
||||
|
||||
@@ -197,10 +142,11 @@ namespace AZ
|
||||
// sort the probes by descending inner volume size, so the smallest volumes are rendered last
|
||||
auto sortFn = [](AZStd::shared_ptr<ReflectionProbe> const& probe1, AZStd::shared_ptr<ReflectionProbe> const& probe2) -> bool
|
||||
{
|
||||
const Aabb& aabb1 = probe1->GetInnerAabbWs();
|
||||
const Aabb& aabb2 = probe2->GetInnerAabbWs();
|
||||
float size1 = aabb1.GetXExtent() * aabb1.GetZExtent() * aabb1.GetYExtent();
|
||||
float size2 = aabb2.GetXExtent() * aabb2.GetZExtent() * aabb2.GetYExtent();
|
||||
const Obb& obb1 = probe1->GetInnerObbWs();
|
||||
const Obb& obb2 = probe2->GetInnerObbWs();
|
||||
float size1 = obb1.GetHalfLengthX() * obb1.GetHalfLengthZ() * obb1.GetHalfLengthY();
|
||||
float size2 = obb2.GetHalfLengthX() * obb2.GetHalfLengthZ() * obb2.GetHalfLengthY();
|
||||
|
||||
return (size1 > size2);
|
||||
};
|
||||
|
||||
@@ -345,7 +291,7 @@ namespace AZ
|
||||
// simple AABB check to find the reflection probes that contain the position
|
||||
for (auto& reflectionProbe : m_reflectionProbes)
|
||||
{
|
||||
if (reflectionProbe->GetOuterAabbWs().Contains(position)
|
||||
if (reflectionProbe->GetOuterObbWs().Contains(position)
|
||||
&& reflectionProbe->GetCubeMapImage()
|
||||
&& reflectionProbe->GetCubeMapImage()->IsInitialized())
|
||||
{
|
||||
|
||||
@@ -8,7 +8,12 @@
|
||||
"canonical_tags": [
|
||||
"Gem"
|
||||
],
|
||||
"user_tags": [
|
||||
],
|
||||
"requirements": ""
|
||||
"user_tags": [],
|
||||
"requirements": "",
|
||||
"dependencies": [
|
||||
"Atom_RPI",
|
||||
"Atom",
|
||||
"ImGui",
|
||||
"Atom_RHI"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
"canonical_tags": [
|
||||
"Gem"
|
||||
],
|
||||
"user_tags": [
|
||||
],
|
||||
"requirements": ""
|
||||
"user_tags": [],
|
||||
"requirements": "",
|
||||
"dependencies": [
|
||||
"Atom_RHI"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
"canonical_tags": [
|
||||
"Gem"
|
||||
],
|
||||
"user_tags": [
|
||||
],
|
||||
"requirements": ""
|
||||
"user_tags": [],
|
||||
"requirements": "",
|
||||
"dependencies": [
|
||||
"Atom_RHI"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
"canonical_tags": [
|
||||
"Gem"
|
||||
],
|
||||
"user_tags": [
|
||||
],
|
||||
"requirements": ""
|
||||
"user_tags": [],
|
||||
"requirements": "",
|
||||
"dependencies": [
|
||||
"Atom_RHI"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
"canonical_tags": [
|
||||
"Gem"
|
||||
],
|
||||
"user_tags": [
|
||||
],
|
||||
"requirements": ""
|
||||
"user_tags": [],
|
||||
"requirements": "",
|
||||
"dependencies": [
|
||||
"Atom_RHI"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -8,7 +8,13 @@
|
||||
"canonical_tags": [
|
||||
"Gem"
|
||||
],
|
||||
"user_tags": [
|
||||
],
|
||||
"requirements": ""
|
||||
"user_tags": [],
|
||||
"requirements": "",
|
||||
"dependencies": [
|
||||
"Atom_RHI_DX12",
|
||||
"Atom_RHI_Metal",
|
||||
"Atom_RHI_Vulkan",
|
||||
"Atom_RHI_Null",
|
||||
"Atom_Feature_Common"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -116,6 +116,22 @@ float ComputeLerpBetweenInnerOuterAABBs(float3 innerAabbMin, float3 innerAabbMax
|
||||
return totalDistance > 0.0f ? saturate(shortestDistance / totalDistance) : 1.0f;
|
||||
}
|
||||
|
||||
// returns true if the Obb contains the specified point
|
||||
bool ObbContainsPoint(float4x4 obbTransformInverse, float3 obbHalfExtents, float3 testPoint)
|
||||
{
|
||||
// get the position in Obb local space, force to positive quadrant with abs()
|
||||
float4 p = abs(mul(obbTransformInverse, float4(testPoint, 1.0f)));
|
||||
return AabbContainsPoint(-obbHalfExtents, obbHalfExtents, p);
|
||||
}
|
||||
|
||||
// computes [0..1] percentage of a point that's in between the inner and outer OBBs
|
||||
float ComputeLerpBetweenInnerOuterOBBs(float3x4 obbTransformInverse, float3 innerObbHalfExtents, float3 outerObbHalfExtents, float3 position)
|
||||
{
|
||||
// get the position in Obb local space, force to positive quadrant with abs()
|
||||
float3 p = abs(mul(obbTransformInverse, float4(position, 1.0f)));
|
||||
return ComputeLerpBetweenInnerOuterAABBs(-innerObbHalfExtents, innerObbHalfExtents, outerObbHalfExtents, float3(0.0f, 0.0f, 0.0f), p);
|
||||
}
|
||||
|
||||
// ---------- Normal Encoding -----------
|
||||
|
||||
// Encode/Decode functions for Signed Octahedron normals
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <AzCore/IO/IOUtils.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/Serialization/Json/JsonSerialization.h>
|
||||
#include <AzCore/Settings/SettingsRegistry.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
@@ -46,7 +47,7 @@ namespace AZ
|
||||
{
|
||||
AssetBuilderSDK::AssetBuilderDesc materialBuilderDescriptor;
|
||||
materialBuilderDescriptor.m_name = JobKey;
|
||||
materialBuilderDescriptor.m_version = 107; // ATOM-14918
|
||||
materialBuilderDescriptor.m_version = 108; // Set materialtype dependency to OrderOnce
|
||||
materialBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern("*.material", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
|
||||
materialBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern("*.materialtype", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
|
||||
materialBuilderDescriptor.m_busId = azrtti_typeid<MaterialBuilder>();
|
||||
@@ -66,21 +67,19 @@ namespace AZ
|
||||
//! Adds all relevant dependencies for a referenced source file, considering that the path might be relative to the original file location or a full asset path.
|
||||
//! This will usually include multiple source dependencies and a single job dependency, but will include only source dependencies if the file is not found.
|
||||
//! Note the AssetBuilderSDK::JobDependency::m_platformIdentifier will not be set by this function. The calling code must set this value before passing back
|
||||
//! to the AssetBuilderSDK::CreateJobsResponse.
|
||||
void AddPossibleDependencies(
|
||||
AZStd::string_view currentFilePath, AZStd::string_view referencedParentPath,
|
||||
AZStd::vector<AssetBuilderSDK::SourceFileDependency>& sourceFileDependencies,
|
||||
const char* jobKey, AZStd::vector<AssetBuilderSDK::JobDependency>& jobDependencies)
|
||||
//! to the AssetBuilderSDK::CreateJobsResponse. If isOrderedOnceForMaterialTypes is true and the dependency is a materialtype file, the job dependency type
|
||||
//! will be set to JobDependencyType::OrderOnce.
|
||||
void AddPossibleDependencies(AZStd::string_view currentFilePath,
|
||||
AZStd::string_view referencedParentPath,
|
||||
const char* jobKey,
|
||||
AZStd::vector<AssetBuilderSDK::JobDependency>& jobDependencies,
|
||||
bool isOrderedOnceForMaterialTypes = false)
|
||||
{
|
||||
bool dependencyFileFound = false;
|
||||
|
||||
AZStd::vector<AZStd::string> possibleDependencies = RPI::AssetUtils::GetPossibleDepenencyPaths(currentFilePath, referencedParentPath);
|
||||
for (auto& file : possibleDependencies)
|
||||
{
|
||||
AssetBuilderSDK::SourceFileDependency sourceFileDependency;
|
||||
sourceFileDependency.m_sourceFileDependencyPath = file;
|
||||
sourceFileDependencies.push_back(sourceFileDependency);
|
||||
|
||||
// The first path found is the highest priority, and will have a job dependency, as this is the one
|
||||
// the builder will actually use
|
||||
if (!dependencyFileFound)
|
||||
@@ -93,8 +92,11 @@ namespace AZ
|
||||
{
|
||||
AssetBuilderSDK::JobDependency jobDependency;
|
||||
jobDependency.m_jobKey = jobKey;
|
||||
jobDependency.m_type = AssetBuilderSDK::JobDependencyType::Order;
|
||||
jobDependency.m_sourceFile.m_sourceFileDependencyPath = file;
|
||||
|
||||
const bool isMaterialTypeFile = AzFramework::StringFunc::Path::IsExtension(file.c_str(), MaterialTypeSourceData::Extension);
|
||||
jobDependency.m_type = (isMaterialTypeFile && isOrderedOnceForMaterialTypes) ? AssetBuilderSDK::JobDependencyType::OrderOnce : AssetBuilderSDK::JobDependencyType::Order;
|
||||
|
||||
jobDependencies.push_back(jobDependency);
|
||||
}
|
||||
}
|
||||
@@ -173,8 +175,9 @@ namespace AZ
|
||||
|
||||
for (auto& shader : materialTypeSourceData.GetValue().m_shaderCollection)
|
||||
{
|
||||
AddPossibleDependencies(request.m_sourceFile, shader.m_shaderFilePath,
|
||||
response.m_sourceFileDependencyList, "Shader Asset",
|
||||
AddPossibleDependencies(request.m_sourceFile,
|
||||
shader.m_shaderFilePath,
|
||||
"Shader Asset",
|
||||
outputJobDescriptor.m_jobDependencyList);
|
||||
}
|
||||
|
||||
@@ -184,9 +187,10 @@ namespace AZ
|
||||
|
||||
for (const MaterialFunctorSourceData::AssetDependency& dependency : dependencies)
|
||||
{
|
||||
AddPossibleDependencies(request.m_sourceFile, dependency.m_sourceFilePath,
|
||||
response.m_sourceFileDependencyList,
|
||||
dependency.m_jobKey.c_str(), outputJobDescriptor.m_jobDependencyList);
|
||||
AddPossibleDependencies(request.m_sourceFile,
|
||||
dependency.m_sourceFilePath,
|
||||
dependency.m_jobKey.c_str(),
|
||||
outputJobDescriptor.m_jobDependencyList);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -219,11 +223,24 @@ namespace AZ
|
||||
parentMaterialPath = materialTypePath;
|
||||
}
|
||||
|
||||
// If includeMaterialPropertyNames is false, then a job dependency is needed so the material builder can validate MaterialAsset properties
|
||||
// against the MaterialTypeAsset at asset build time.
|
||||
// If includeMaterialPropertyNames is true, the material properties will be validated at runtime when the material is loaded, so the job dependency
|
||||
// is needed only for first-time processing to set up the initial MaterialAsset. This speeds up AP processing time when a materialtype file
|
||||
// is edited (e.g. 10s when editing StandardPBR.materialtype on AtomTest project from 45s).
|
||||
bool includeMaterialPropertyNames = true;
|
||||
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
|
||||
{
|
||||
settingsRegistry->Get(includeMaterialPropertyNames, "/O3DE/Atom/RPI/MaterialBuilder/IncludeMaterialPropertyNames");
|
||||
}
|
||||
|
||||
// Register dependency on the parent material source file so we can load it and use it's data to build this variant material.
|
||||
// Note, we don't need a direct dependency on the material type because the parent material will depend on it.
|
||||
AddPossibleDependencies(request.m_sourceFile, parentMaterialPath,
|
||||
response.m_sourceFileDependencyList,
|
||||
JobKey, outputJobDescriptor.m_jobDependencyList);
|
||||
AddPossibleDependencies(request.m_sourceFile,
|
||||
parentMaterialPath,
|
||||
JobKey,
|
||||
outputJobDescriptor.m_jobDependencyList,
|
||||
includeMaterialPropertyNames);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace AZ
|
||||
if (auto* serialize = azrtti_cast<SerializeContext*>(context))
|
||||
{
|
||||
serialize->Class<MaterialAssetDependenciesComponent, Component>()
|
||||
->Version(4)
|
||||
->Version(5) // Set materialtype dependency to OrderOnce
|
||||
->Attribute(Edit::Attributes::SystemComponentTags, AZStd::vector<Crc32>({ AssetBuilderSDK::ComponentTags::AssetBuilder }));
|
||||
}
|
||||
}
|
||||
@@ -78,10 +78,7 @@ namespace AZ
|
||||
AZStd::string materialTypePath;
|
||||
RPI::MaterialConverterBus::BroadcastResult(materialTypePath, &RPI::MaterialConverterBus::Events::GetMaterialTypePath);
|
||||
|
||||
bool includeMaterialPropertyNames = true;
|
||||
RPI::MaterialConverterBus::BroadcastResult(includeMaterialPropertyNames, &RPI::MaterialConverterBus::Events::ShouldIncludeMaterialPropertyNames);
|
||||
// TODO: Use includeMaterialPropertyNames to break materialtype dependency on fbx files. Materialasset's dependency on materialtypeasset will need to be decoupled first
|
||||
if (conversionEnabled && !materialTypePath.empty() /*&& !includeMaterialPropertyNames*/)
|
||||
if (conversionEnabled && !materialTypePath.empty())
|
||||
{
|
||||
AssetBuilderSDK::SourceFileDependency materialTypeSource;
|
||||
materialTypeSource.m_sourceFileDependencyPath = materialTypePath;
|
||||
@@ -90,7 +87,15 @@ namespace AZ
|
||||
jobDependency.m_jobKey = "Atom Material Builder";
|
||||
jobDependency.m_sourceFile = materialTypeSource;
|
||||
jobDependency.m_platformIdentifier = platformIdentifier;
|
||||
jobDependency.m_type = AssetBuilderSDK::JobDependencyType::Order;
|
||||
|
||||
// If includeMaterialPropertyNames is false, then a job dependency is needed so the material builder can validate
|
||||
// MaterialAsset properties against the MaterialTypeAsset at asset build time. If includeMaterialPropertyNames is true, the
|
||||
// material properties will be validated at runtime when the material is loaded, so the job dependency is needed only for
|
||||
// first-time processing to set up the initial MaterialAsset. This speeds up AP processing time when a materialtype file is
|
||||
// edited (e.g. 10s when editing StandardPBR.materialtype on AtomTest project from 45s).
|
||||
bool includeMaterialPropertyNames = true;
|
||||
RPI::MaterialConverterBus::BroadcastResult(includeMaterialPropertyNames, &RPI::MaterialConverterBus::Events::ShouldIncludeMaterialPropertyNames);
|
||||
jobDependency.m_type = includeMaterialPropertyNames ? AssetBuilderSDK::JobDependencyType::OrderOnce : AssetBuilderSDK::JobDependencyType::Order;
|
||||
|
||||
jobDependencyList.push_back(jobDependency);
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace AZ
|
||||
// Only allocate buffer if initial data is not empty
|
||||
if (initialData != nullptr && initialDataSize > 0)
|
||||
{
|
||||
bufferAsset->m_buffer.resize(descriptor.m_byteCount);
|
||||
bufferAsset->m_buffer.resize_no_construct(descriptor.m_byteCount);
|
||||
memcpy(bufferAsset->m_buffer.data(), initialData, initialDataSize);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
"canonical_tags": [
|
||||
"Gem"
|
||||
],
|
||||
"user_tags": [
|
||||
],
|
||||
"requirements": ""
|
||||
"user_tags": [],
|
||||
"requirements": "",
|
||||
"dependencies": [
|
||||
"Atom_RHI"
|
||||
]
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user