Merge branch 'development' into o3de_sdk/installer_configs

Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com>
This commit is contained in:
Esteban Papp
2021-09-22 19:29:29 -07:00
276 changed files with 6254 additions and 1756 deletions
+31
View File
@@ -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;
@@ -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
@@ -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;
};
}
}
@@ -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)
@@ -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);
@@ -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();
}
}
@@ -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;
+13 -3
View File
@@ -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
View File
@@ -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": []
}
+12 -3
View File
@@ -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"
]
}
+12 -3
View File
@@ -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"
]
}
+9 -3
View File
@@ -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": []
}
+12 -3
View File
@@ -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"
]
}
+10 -3
View File
@@ -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}",
@@ -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}",
+7 -3
View File
@@ -8,7 +8,11 @@
"canonical_tags": [
"Gem"
],
"user_tags": [
],
"requirements": ""
"user_tags": [],
"requirements": "",
"dependencies": [
"Atom_RPI",
"Atom_RHI",
"Atom"
]
}
+6 -3
View File
@@ -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())
{
+5 -3
View File
@@ -8,7 +8,9 @@
"canonical_tags": [
"Gem"
],
"user_tags": [
],
"requirements": ""
"user_tags": [],
"requirements": "",
"dependencies": [
"Atom_RPI"
]
}
+5 -3
View File
@@ -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
@@ -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;
}
@@ -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;
@@ -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 -3
View File
@@ -8,7 +8,12 @@
"canonical_tags": [
"Gem"
],
"user_tags": [
],
"requirements": ""
"user_tags": [],
"requirements": "",
"dependencies": [
"Atom_RPI",
"Atom",
"ImGui",
"Atom_RHI"
]
}
+5 -3
View File
@@ -8,7 +8,9 @@
"canonical_tags": [
"Gem"
],
"user_tags": [
],
"requirements": ""
"user_tags": [],
"requirements": "",
"dependencies": [
"Atom_RHI"
]
}
+5 -3
View File
@@ -8,7 +8,9 @@
"canonical_tags": [
"Gem"
],
"user_tags": [
],
"requirements": ""
"user_tags": [],
"requirements": "",
"dependencies": [
"Atom_RHI"
]
}
+5 -3
View File
@@ -8,7 +8,9 @@
"canonical_tags": [
"Gem"
],
"user_tags": [
],
"requirements": ""
"user_tags": [],
"requirements": "",
"dependencies": [
"Atom_RHI"
]
}
+5 -3
View File
@@ -8,7 +8,9 @@
"canonical_tags": [
"Gem"
],
"user_tags": [
],
"requirements": ""
"user_tags": [],
"requirements": "",
"dependencies": [
"Atom_RHI"
]
}
+9 -3
View File
@@ -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);
}
+5 -3
View File
@@ -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