Merge branch 'development' of https://github.com/o3de/o3de into daimini/FocusMode/setup
This commit is contained in:
@@ -254,4 +254,35 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
ly_add_googletest(
|
||||
NAME Legacy::EditorLib.Tests
|
||||
)
|
||||
|
||||
ly_add_target(
|
||||
NAME EditorLib.Camera.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
|
||||
NAMESPACE Legacy
|
||||
FILES_CMAKE
|
||||
Lib/Tests/Camera/editor_lib_camera_test_files.cmake
|
||||
INCLUDE_DIRECTORIES
|
||||
PRIVATE
|
||||
.
|
||||
BUILD_DEPENDENCIES
|
||||
PRIVATE
|
||||
AZ::AzCore
|
||||
AZ::AzTest
|
||||
AZ::AzToolsFramework
|
||||
AZ::AzTestShared
|
||||
Legacy::EditorLib
|
||||
Gem::Camera.Editor
|
||||
Gem::AtomToolsFramework.Static
|
||||
RUNTIME_DEPENDENCIES
|
||||
Legacy::EditorLib
|
||||
)
|
||||
|
||||
ly_add_source_properties(
|
||||
SOURCES Lib/Tests/Camera/test_EditorCamera.cpp
|
||||
PROPERTY COMPILE_DEFINITIONS
|
||||
VALUES CAMERA_EDITOR_MODULE="$<TARGET_FILE_BASE_NAME:Camera.Editor>"
|
||||
)
|
||||
|
||||
ly_add_googletest(
|
||||
NAME Legacy::EditorLib.Camera.Tests
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <EditorModularViewportCameraComposer.h>
|
||||
|
||||
#include <AtomToolsFramework/Viewport/ModularViewportCameraControllerRequestBus.h>
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
#include <AzFramework/Render/IntersectorInterface.h>
|
||||
#include <AzToolsFramework/Viewport/ViewportMessages.h>
|
||||
@@ -34,10 +35,12 @@ namespace SandboxEditor
|
||||
: m_viewportId(viewportId)
|
||||
{
|
||||
EditorModularViewportCameraComposerNotificationBus::Handler::BusConnect(viewportId);
|
||||
Camera::EditorCameraNotificationBus::Handler::BusConnect();
|
||||
}
|
||||
|
||||
EditorModularViewportCameraComposer::~EditorModularViewportCameraComposer()
|
||||
{
|
||||
Camera::EditorCameraNotificationBus::Handler::BusDisconnect();
|
||||
EditorModularViewportCameraComposerNotificationBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
@@ -283,4 +286,22 @@ namespace SandboxEditor
|
||||
m_orbitCamera->SetOrbitInputChannelId(SandboxEditor::CameraOrbitChannelId());
|
||||
m_orbitDollyMoveCamera->SetDollyInputChannelId(SandboxEditor::CameraOrbitDollyChannelId());
|
||||
}
|
||||
|
||||
void EditorModularViewportCameraComposer::OnViewportViewEntityChanged(const AZ::EntityId& viewEntityId)
|
||||
{
|
||||
if (viewEntityId.IsValid())
|
||||
{
|
||||
AZ::Transform worldFromLocal = AZ::Transform::CreateIdentity();
|
||||
AZ::TransformBus::EventResult(worldFromLocal, viewEntityId, &AZ::TransformBus::Events::GetWorldTM);
|
||||
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
|
||||
m_viewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::SetReferenceFrame,
|
||||
worldFromLocal);
|
||||
}
|
||||
else
|
||||
{
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
|
||||
m_viewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::ClearReferenceFrame);
|
||||
}
|
||||
}
|
||||
} // namespace SandboxEditor
|
||||
|
||||
@@ -10,13 +10,16 @@
|
||||
|
||||
#include <AtomToolsFramework/Viewport/ModularViewportCameraController.h>
|
||||
#include <AzFramework/Viewport/CameraInput.h>
|
||||
#include <AzToolsFramework/API/EditorCameraBus.h>
|
||||
#include <EditorModularViewportCameraComposerBus.h>
|
||||
#include <SandboxAPI.h>
|
||||
|
||||
namespace SandboxEditor
|
||||
{
|
||||
//! Type responsible for building the editor's modular viewport camera controller.
|
||||
class EditorModularViewportCameraComposer : private EditorModularViewportCameraComposerNotificationBus::Handler
|
||||
class EditorModularViewportCameraComposer
|
||||
: private EditorModularViewportCameraComposerNotificationBus::Handler
|
||||
, private Camera::EditorCameraNotificationBus::Handler
|
||||
{
|
||||
public:
|
||||
SANDBOX_API explicit EditorModularViewportCameraComposer(AzFramework::ViewportId viewportId);
|
||||
@@ -32,6 +35,9 @@ namespace SandboxEditor
|
||||
// EditorModularViewportCameraComposerNotificationBus overrides ...
|
||||
void OnEditorModularViewportCameraComposerSettingsChanged() override;
|
||||
|
||||
// EditorCameraNotificationBus overrides ...
|
||||
void OnViewportViewEntityChanged(const AZ::EntityId& viewEntityId) override;
|
||||
|
||||
AZStd::shared_ptr<AzFramework::RotateCameraInput> m_firstPersonRotateCamera;
|
||||
AZStd::shared_ptr<AzFramework::PanCameraInput> m_firstPersonPanCamera;
|
||||
AZStd::shared_ptr<AzFramework::TranslateCameraInput> m_firstPersonTranslateCamera;
|
||||
|
||||
+2
-1
@@ -6,5 +6,6 @@
|
||||
#
|
||||
#
|
||||
|
||||
set(GEM_DEPENDENCIES
|
||||
set(FILES
|
||||
test_EditorCamera.cpp
|
||||
)
|
||||
@@ -0,0 +1,225 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AZTestShared/Math/MathTestHelpers.h>
|
||||
#include <AtomToolsFramework/Viewport/ModularViewportCameraController.h>
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
#include <AzFramework/Viewport/ViewportControllerList.h>
|
||||
#include <AzTest/GemTestEnvironment.h>
|
||||
#include <AzToolsFramework/API/EditorCameraBus.h>
|
||||
#include <AzToolsFramework/ToolsComponents/TransformComponent.h>
|
||||
#include <EditorModularViewportCameraComposer.h>
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
class EditorCameraTestEnvironment : public AZ::Test::GemTestEnvironment
|
||||
{
|
||||
// AZ::Test::GemTestEnvironment overrides ...
|
||||
void AddGemsAndComponents() override;
|
||||
};
|
||||
|
||||
void EditorCameraTestEnvironment::AddGemsAndComponents()
|
||||
{
|
||||
AddDynamicModulePaths({ CAMERA_EDITOR_MODULE });
|
||||
AddComponentDescriptors({ AzToolsFramework::Components::TransformComponent::CreateDescriptor() });
|
||||
}
|
||||
|
||||
class EditorCameraFixture : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
AtomToolsFramework::ModularCameraViewportContext* m_cameraViewportContextView = nullptr;
|
||||
AZStd::unique_ptr<SandboxEditor::EditorModularViewportCameraComposer> m_editorModularViewportCameraComposer;
|
||||
AZStd::unique_ptr<AZ::DynamicModuleHandle> m_editorLibHandle;
|
||||
AzFramework::ViewportControllerListPtr m_controllerList;
|
||||
AZStd::unique_ptr<AZ::Entity> m_entity;
|
||||
|
||||
static const AzFramework::ViewportId TestViewportId;
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
m_editorLibHandle = AZ::DynamicModuleHandle::Create("EditorLib");
|
||||
[[maybe_unused]] const bool loaded = m_editorLibHandle->Load(true);
|
||||
AZ_Assert(loaded, "EditorLib could not be loaded");
|
||||
|
||||
m_controllerList = AZStd::make_shared<AzFramework::ViewportControllerList>();
|
||||
m_controllerList->RegisterViewportContext(TestViewportId);
|
||||
|
||||
m_entity = AZStd::make_unique<AZ::Entity>();
|
||||
m_entity->Init();
|
||||
m_entity->CreateComponent<AzToolsFramework::Components::TransformComponent>();
|
||||
m_entity->Activate();
|
||||
|
||||
m_editorModularViewportCameraComposer = AZStd::make_unique<SandboxEditor::EditorModularViewportCameraComposer>(TestViewportId);
|
||||
|
||||
auto controller = m_editorModularViewportCameraComposer->CreateModularViewportCameraController();
|
||||
// set some overrides for the test
|
||||
controller->SetCameraViewportContextBuilderCallback(
|
||||
[this](AZStd::unique_ptr<AtomToolsFramework::ModularCameraViewportContext>& cameraViewportContext) mutable
|
||||
{
|
||||
cameraViewportContext = AZStd::make_unique<AtomToolsFramework::PlaceholderModularCameraViewportContextImpl>();
|
||||
m_cameraViewportContextView = cameraViewportContext.get();
|
||||
});
|
||||
|
||||
m_controllerList->Add(controller);
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
m_editorModularViewportCameraComposer.reset();
|
||||
m_cameraViewportContextView = nullptr;
|
||||
m_entity.reset();
|
||||
m_editorLibHandle = {};
|
||||
}
|
||||
};
|
||||
|
||||
const AzFramework::ViewportId EditorCameraFixture::TestViewportId = AzFramework::ViewportId(1337);
|
||||
|
||||
TEST_F(EditorCameraFixture, ModularViewportCameraControllerReferenceFrameUpdatedWhenViewportEntityisChanged)
|
||||
{
|
||||
// Given
|
||||
const auto entityTransform = AZ::Transform::CreateFromQuaternionAndTranslation(
|
||||
AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)), AZ::Vector3(10.0f, 5.0f, -2.0f));
|
||||
AZ::TransformBus::Event(m_entity->GetId(), &AZ::TransformBus::Events::SetWorldTM, entityTransform);
|
||||
|
||||
// When
|
||||
// imitate viewport entity changing
|
||||
Camera::EditorCameraNotificationBus::Broadcast(
|
||||
&Camera::EditorCameraNotificationBus::Events::OnViewportViewEntityChanged, m_entity->GetId());
|
||||
|
||||
// ensure the viewport updates after the viewport view entity change
|
||||
const float deltaTime = 1.0f / 60.0f;
|
||||
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(deltaTime), AZ::ScriptTimePoint() });
|
||||
|
||||
// retrieve updated camera transform
|
||||
const AZ::Transform cameraTransform = m_cameraViewportContextView->GetCameraTransform();
|
||||
|
||||
// Then
|
||||
// camera transform matches that of the entity
|
||||
EXPECT_THAT(cameraTransform, IsClose(entityTransform));
|
||||
}
|
||||
|
||||
TEST_F(EditorCameraFixture, ReferenceFrameRemainsIdentityAfterExternalCameraTransformChangeWhenNotSet)
|
||||
{
|
||||
// Given
|
||||
m_cameraViewportContextView->SetCameraTransform(AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 20.0f, 30.0f)));
|
||||
|
||||
// When
|
||||
AZ::Transform referenceFrame = AZ::Transform::CreateTranslation(AZ::Vector3(1.0f, 2.0f, 3.0f));
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
|
||||
referenceFrame, TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::GetReferenceFrame);
|
||||
|
||||
// Then
|
||||
// reference frame is still the identity
|
||||
EXPECT_THAT(referenceFrame, IsClose(AZ::Transform::CreateIdentity()));
|
||||
}
|
||||
|
||||
TEST_F(EditorCameraFixture, ExternalCameraTransformChangeWhenReferenceFrameIsSetUpdatesReferenceFrame)
|
||||
{
|
||||
// Given
|
||||
const AZ::Transform referenceFrame = AZ::Transform::CreateFromQuaternionAndTranslation(
|
||||
AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)), AZ::Vector3(1.0f, 2.0f, 3.0f));
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
|
||||
TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::SetReferenceFrame, referenceFrame);
|
||||
|
||||
const AZ::Transform nextTransform = AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 20.0f, 30.0f));
|
||||
m_cameraViewportContextView->SetCameraTransform(nextTransform);
|
||||
|
||||
// When
|
||||
AZ::Transform currentReferenceFrame = AZ::Transform::CreateTranslation(AZ::Vector3(1.0f, 2.0f, 3.0f));
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
|
||||
currentReferenceFrame, TestViewportId,
|
||||
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::GetReferenceFrame);
|
||||
|
||||
// Then
|
||||
EXPECT_THAT(currentReferenceFrame, IsClose(nextTransform));
|
||||
}
|
||||
|
||||
TEST_F(EditorCameraFixture, ReferenceFrameReturnedToIdentityAfterClear)
|
||||
{
|
||||
// Given
|
||||
const AZ::Transform referenceFrame = AZ::Transform::CreateFromQuaternionAndTranslation(
|
||||
AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)), AZ::Vector3(1.0f, 2.0f, 3.0f));
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
|
||||
TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::SetReferenceFrame, referenceFrame);
|
||||
|
||||
// When
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
|
||||
TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::ClearReferenceFrame);
|
||||
|
||||
AZ::Transform currentReferenceFrame = AZ::Transform::CreateTranslation(AZ::Vector3(1.0f, 2.0f, 3.0f));
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
|
||||
currentReferenceFrame, TestViewportId,
|
||||
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::GetReferenceFrame);
|
||||
|
||||
// Then
|
||||
EXPECT_THAT(currentReferenceFrame, IsClose(AZ::Transform::CreateIdentity()));
|
||||
}
|
||||
|
||||
TEST_F(EditorCameraFixture, InterpolateToTransform)
|
||||
{
|
||||
// When
|
||||
AZ::Transform transformToInterpolateTo = AZ::Transform::CreateFromQuaternionAndTranslation(
|
||||
AZ::Quaternion::CreateRotationZ(AZ::DegToRad(90.0f)), AZ::Vector3(20.0f, 40.0f, 60.0f));
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
|
||||
TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform,
|
||||
transformToInterpolateTo, 0.0f);
|
||||
|
||||
// simulate interpolation
|
||||
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(0.5f), AZ::ScriptTimePoint() });
|
||||
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(0.5f), AZ::ScriptTimePoint() });
|
||||
|
||||
const auto finalTransform = m_cameraViewportContextView->GetCameraTransform();
|
||||
|
||||
// Then
|
||||
EXPECT_THAT(finalTransform, IsClose(transformToInterpolateTo));
|
||||
}
|
||||
|
||||
TEST_F(EditorCameraFixture, InterpolateToTransformWithReferenceSpaceSet)
|
||||
{
|
||||
// Given
|
||||
const AZ::Transform referenceFrame = AZ::Transform::CreateFromQuaternionAndTranslation(
|
||||
AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)), AZ::Vector3(1.0f, 2.0f, 3.0f));
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
|
||||
TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::SetReferenceFrame, referenceFrame);
|
||||
|
||||
AZ::Transform transformToInterpolateTo = AZ::Transform::CreateFromQuaternionAndTranslation(
|
||||
AZ::Quaternion::CreateRotationZ(AZ::DegToRad(90.0f)), AZ::Vector3(20.0f, 40.0f, 60.0f));
|
||||
|
||||
// When
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
|
||||
TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform,
|
||||
transformToInterpolateTo, 0.0f);
|
||||
|
||||
// simulate interpolation
|
||||
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(0.5f), AZ::ScriptTimePoint() });
|
||||
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(0.5f), AZ::ScriptTimePoint() });
|
||||
|
||||
AZ::Transform currentReferenceFrame = AZ::Transform::CreateTranslation(AZ::Vector3(1.0f, 2.0f, 3.0f));
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
|
||||
currentReferenceFrame, TestViewportId,
|
||||
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::GetReferenceFrame);
|
||||
|
||||
const auto finalTransform = m_cameraViewportContextView->GetCameraTransform();
|
||||
|
||||
// Then
|
||||
EXPECT_THAT(finalTransform, IsClose(transformToInterpolateTo));
|
||||
EXPECT_THAT(currentReferenceFrame, IsClose(AZ::Transform::CreateIdentity()));
|
||||
}
|
||||
} // namespace UnitTest
|
||||
|
||||
// required to support running integration tests with the Camera Gem
|
||||
AZTEST_EXPORT int AZ_UNIT_TEST_HOOK_NAME(int argc, char** argv)
|
||||
{
|
||||
::testing::InitGoogleMock(&argc, argv);
|
||||
AZ::Test::printUnusedParametersWarning(argc, argv);
|
||||
AZ::Test::addTestEnvironments({ new UnitTest::EditorCameraTestEnvironment() });
|
||||
int result = RUN_ALL_TESTS();
|
||||
return result;
|
||||
}
|
||||
|
||||
IMPLEMENT_TEST_EXECUTABLE_MAIN();
|
||||
@@ -58,28 +58,6 @@ namespace UnitTest
|
||||
return true;
|
||||
}
|
||||
|
||||
class TestModularCameraViewportContextImpl : public AtomToolsFramework::ModularCameraViewportContext
|
||||
{
|
||||
public:
|
||||
AZ::Transform GetCameraTransform() const override
|
||||
{
|
||||
return m_cameraTransform;
|
||||
}
|
||||
|
||||
void SetCameraTransform(const AZ::Transform& transform) override
|
||||
{
|
||||
m_cameraTransform = transform;
|
||||
}
|
||||
|
||||
void ConnectViewMatrixChangedHandler(AZ::RPI::ViewportContext::MatrixChangedEvent::Handler&) override
|
||||
{
|
||||
// noop
|
||||
}
|
||||
|
||||
private:
|
||||
AZ::Transform m_cameraTransform = AZ::Transform::CreateIdentity();
|
||||
};
|
||||
|
||||
class ModularViewportCameraControllerFixture : public AllocatorsTestFixture
|
||||
{
|
||||
public:
|
||||
@@ -146,7 +124,7 @@ namespace UnitTest
|
||||
controller->SetCameraViewportContextBuilderCallback(
|
||||
[this](AZStd::unique_ptr<AtomToolsFramework::ModularCameraViewportContext>& cameraViewportContext)
|
||||
{
|
||||
cameraViewportContext = AZStd::make_unique<TestModularCameraViewportContextImpl>();
|
||||
cameraViewportContext = AZStd::make_unique<AtomToolsFramework::PlaceholderModularCameraViewportContextImpl>();
|
||||
m_cameraViewportContextView = cameraViewportContext.get();
|
||||
});
|
||||
|
||||
|
||||
+4
-4
@@ -3642,7 +3642,7 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
void EditorTransformComponentSelection::OnViewportViewEntityChanged(const AZ::EntityId& newViewId)
|
||||
void EditorTransformComponentSelection::OnViewportViewEntityChanged(const AZ::EntityId& viewEntityId)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzToolsFramework);
|
||||
|
||||
@@ -3650,12 +3650,12 @@ namespace AzToolsFramework
|
||||
// match the editor camera translation/orientation), record the entity id if we have
|
||||
// a manipulator tracking it (entity id exists in m_entityIdManipulator lookups)
|
||||
// and remove it when recreating manipulators (see InitializeManipulators)
|
||||
if (newViewId.IsValid())
|
||||
if (viewEntityId.IsValid())
|
||||
{
|
||||
const auto entityIdLookupIt = m_entityIdManipulators.m_lookups.find(newViewId);
|
||||
const auto entityIdLookupIt = m_entityIdManipulators.m_lookups.find(viewEntityId);
|
||||
if (entityIdLookupIt != m_entityIdManipulators.m_lookups.end())
|
||||
{
|
||||
m_editorCameraComponentEntityId = newViewId;
|
||||
m_editorCameraComponentEntityId = viewEntityId;
|
||||
RegenerateManipulators();
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -270,7 +270,7 @@ namespace AzToolsFramework
|
||||
void OnTransformChanged(const AZ::Transform& localTM, const AZ::Transform& worldTM) override;
|
||||
|
||||
// Camera::EditorCameraNotificationBus overrides ...
|
||||
void OnViewportViewEntityChanged(const AZ::EntityId& newViewId) override;
|
||||
void OnViewportViewEntityChanged(const AZ::EntityId& viewEntityId) override;
|
||||
|
||||
// EditorContextVisibilityNotificationBus overrides ...
|
||||
void OnEntityVisibilityChanged(bool visibility) override;
|
||||
|
||||
@@ -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())
|
||||
{
|
||||
|
||||
@@ -40,11 +40,10 @@ ShaderResourceGroup ObjectSrg : SRG_PerObject
|
||||
//! Reflection Probe (smallest probe volume that overlaps the object position)
|
||||
struct ReflectionProbeData
|
||||
{
|
||||
float3 m_aabbPos;
|
||||
float3 m_outerAabbMin;
|
||||
float3 m_outerAabbMax;
|
||||
float3 m_innerAabbMin;
|
||||
float3 m_innerAabbMax;
|
||||
row_major float3x4 m_modelToWorld;
|
||||
row_major float3x4 m_modelToWorldInverse; // does not include extents
|
||||
float3 m_outerObbHalfLengths;
|
||||
float3 m_innerObbHalfLengths;
|
||||
float m_padding;
|
||||
bool m_useReflectionProbe;
|
||||
bool m_useParallaxCorrection;
|
||||
@@ -52,4 +51,32 @@ ShaderResourceGroup ObjectSrg : SRG_PerObject
|
||||
|
||||
ReflectionProbeData m_reflectionProbeData;
|
||||
TextureCube m_reflectionProbeCubeMap;
|
||||
|
||||
float4x4 GetReflectionProbeWorldMatrix()
|
||||
{
|
||||
float4x4 modelToWorld = float4x4(
|
||||
float4(1, 0, 0, 0),
|
||||
float4(0, 1, 0, 0),
|
||||
float4(0, 0, 1, 0),
|
||||
float4(0, 0, 0, 1));
|
||||
|
||||
modelToWorld[0] = m_reflectionProbeData.m_modelToWorld[0];
|
||||
modelToWorld[1] = m_reflectionProbeData.m_modelToWorld[1];
|
||||
modelToWorld[2] = m_reflectionProbeData.m_modelToWorld[2];
|
||||
return modelToWorld;
|
||||
}
|
||||
|
||||
float4x4 GetReflectionProbeWorldMatrixInverse()
|
||||
{
|
||||
float4x4 modelToWorldInverse = float4x4(
|
||||
float4(1, 0, 0, 0),
|
||||
float4(0, 1, 0, 0),
|
||||
float4(0, 0, 1, 0),
|
||||
float4(0, 0, 0, 1));
|
||||
|
||||
modelToWorldInverse[0] = m_reflectionProbeData.m_modelToWorldInverse[0];
|
||||
modelToWorldInverse[1] = m_reflectionProbeData.m_modelToWorldInverse[1];
|
||||
modelToWorldInverse[2] = m_reflectionProbeData.m_modelToWorldInverse[2];
|
||||
return modelToWorldInverse;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,10 +50,10 @@ float GetRoughnessMip(float roughness)
|
||||
return roughness * maxRoughnessMip;
|
||||
}
|
||||
|
||||
// compute parallax corrected reflection vector
|
||||
// compute parallax corrected reflection vector, AABB version
|
||||
// we do this by finding the intersection with the volume and adjusting the reflection vector for the surface position
|
||||
// https://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/
|
||||
float3 ApplyParallaxCorrection(float3 aabbMin, float3 aabbMax, float3 aabbPos, float3 positionWS, float3 reflectDir)
|
||||
float3 ApplyParallaxCorrectionAABB(float3 aabbMin, float3 aabbMax, float3 aabbPos, float3 positionWS, float3 reflectDir)
|
||||
{
|
||||
float3 rcpReflectDir = 1.0f / reflectDir;
|
||||
float3 intersectA = (aabbMax - positionWS) * rcpReflectDir;
|
||||
@@ -63,3 +63,10 @@ float3 ApplyParallaxCorrection(float3 aabbMin, float3 aabbMax, float3 aabbPos, f
|
||||
float3 intersectPos = reflectDir * distance + positionWS;
|
||||
return (intersectPos - aabbPos);
|
||||
}
|
||||
|
||||
// compute parallax corrected reflection vector, OBB version
|
||||
float3 ApplyParallaxCorrectionOBB(float4x4 obbTransformInverse, float3 obbHalfExtents, float3 positionWS, float3 reflectDir)
|
||||
{
|
||||
float4 p = mul(obbTransformInverse, float4(positionWS, 1.0f));
|
||||
return ApplyParallaxCorrectionAABB(-obbHalfExtents, obbHalfExtents, float3(0.0f, 0.0f, 0.0f), p, reflectDir);
|
||||
}
|
||||
|
||||
@@ -48,10 +48,9 @@ float3 GetIblSpecular(
|
||||
{
|
||||
if (ObjectSrg::m_reflectionProbeData.m_useParallaxCorrection)
|
||||
{
|
||||
reflectDir = ApplyParallaxCorrection(
|
||||
ObjectSrg::m_reflectionProbeData.m_outerAabbMin,
|
||||
ObjectSrg::m_reflectionProbeData.m_outerAabbMax,
|
||||
ObjectSrg::m_reflectionProbeData.m_aabbPos,
|
||||
reflectDir = ApplyParallaxCorrectionOBB(
|
||||
ObjectSrg::GetReflectionProbeWorldMatrixInverse(),
|
||||
ObjectSrg::m_reflectionProbeData.m_outerObbHalfLengths,
|
||||
position,
|
||||
reflectDir);
|
||||
}
|
||||
@@ -60,11 +59,10 @@ float3 GetIblSpecular(
|
||||
probeSpecular *= (specularF0 * brdf.x + brdf.y);
|
||||
|
||||
// compute blend amount based on world position in the reflection probe volume
|
||||
float blendAmount = ComputeLerpBetweenInnerOuterAABBs(
|
||||
ObjectSrg::m_reflectionProbeData.m_innerAabbMin,
|
||||
ObjectSrg::m_reflectionProbeData.m_innerAabbMax,
|
||||
ObjectSrg::m_reflectionProbeData.m_outerAabbMax,
|
||||
ObjectSrg::m_reflectionProbeData.m_aabbPos,
|
||||
float blendAmount = ComputeLerpBetweenInnerOuterOBBs(
|
||||
ObjectSrg::GetReflectionProbeWorldMatrixInverse(),
|
||||
ObjectSrg::m_reflectionProbeData.m_innerObbHalfLengths,
|
||||
ObjectSrg::m_reflectionProbeData.m_outerObbHalfLengths,
|
||||
position);
|
||||
|
||||
outSpecular = lerp(outSpecular, probeSpecular, blendAmount);
|
||||
|
||||
@@ -63,7 +63,7 @@ PSOutput MainPS(VSOutput IN, in uint sampleIndex : SV_SampleIndex)
|
||||
// make sure the pixel belongs to this probe volume
|
||||
// this is necessary since it could have the correct stencil value but actually reside
|
||||
// in another volume that's in between the camera and the volume we're rendering
|
||||
if (!AabbContainsPoint(ObjectSrg::m_outerAabbMin, ObjectSrg::m_outerAabbMax, positionWS))
|
||||
if (!ObbContainsPoint(ObjectSrg::GetWorldMatrixInverse(), ObjectSrg::m_outerObbHalfLengths, positionWS))
|
||||
{
|
||||
discard;
|
||||
}
|
||||
@@ -71,11 +71,15 @@ PSOutput MainPS(VSOutput IN, in uint sampleIndex : SV_SampleIndex)
|
||||
// determine blend based on position with respect to the inner and outer AABBs
|
||||
// if it's inside the inner AABB it blends at 100%, otherwise it's the percentage of the distance between the inner/outer AABB
|
||||
float blendWeight = 1.0f;
|
||||
if (!AabbContainsPoint(ObjectSrg::m_innerAabbMin, ObjectSrg::m_innerAabbMax, positionWS))
|
||||
if (!ObbContainsPoint(ObjectSrg::GetWorldMatrixInverse(), ObjectSrg::m_innerObbHalfLengths, positionWS))
|
||||
{
|
||||
// not inside the inner AABB, so it's in between the inner and outer AABBs
|
||||
// compute blend amount based on the distance to the outer AABB
|
||||
blendWeight = ComputeLerpBetweenInnerOuterAABBs(ObjectSrg::m_innerAabbMin, ObjectSrg::m_innerAabbMax, ObjectSrg::m_outerAabbMax, ObjectSrg::m_aabbPos, positionWS);
|
||||
blendWeight = ComputeLerpBetweenInnerOuterOBBs(
|
||||
ObjectSrg::GetWorldMatrixInverse(),
|
||||
ObjectSrg::m_innerObbHalfLengths,
|
||||
ObjectSrg::m_outerObbHalfLengths,
|
||||
positionWS);
|
||||
}
|
||||
|
||||
// write the blend weight (additive) at this position for the probe volume
|
||||
|
||||
+7
-3
@@ -12,12 +12,12 @@
|
||||
#include <Atom/Features/PBR/Microfacet/Fresnel.azsli>
|
||||
|
||||
// compute final probe specular using the probe cubemap and the roughness, normals, and specularF0 for the surface
|
||||
bool ComputeProbeSpecular(float2 screenCoords, float3 positionWS, float3 aabbMin, float3 aabbMax, uint sampleIndex, out float3 specular)
|
||||
bool ComputeProbeSpecular(float2 screenCoords, float3 positionWS, float4x4 obbTransformInverse, float3 outerObbHalfLengths, uint sampleIndex, out float3 specular)
|
||||
{
|
||||
// make sure the pixel belongs to this probe volume
|
||||
// this is necessary since it could have the correct stencil value but actually reside
|
||||
// in another volume that's in between the camera and the volume we're rendering
|
||||
if (!AabbContainsPoint(aabbMin, aabbMax, positionWS))
|
||||
if (!ObbContainsPoint(obbTransformInverse, outerObbHalfLengths, positionWS))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -47,7 +47,11 @@ bool ComputeProbeSpecular(float2 screenCoords, float3 positionWS, float3 aabbMin
|
||||
float3 localReflectDir = reflectDir;
|
||||
if (ObjectSrg::m_useParallaxCorrection)
|
||||
{
|
||||
localReflectDir = ApplyParallaxCorrection(ObjectSrg::m_outerAabbMin, ObjectSrg::m_outerAabbMax, ObjectSrg::m_aabbPos, positionWS, reflectDir);
|
||||
localReflectDir = ApplyParallaxCorrectionOBB(
|
||||
ObjectSrg::GetWorldMatrixInverse(),
|
||||
ObjectSrg::m_outerObbHalfLengths,
|
||||
positionWS,
|
||||
reflectDir);
|
||||
}
|
||||
|
||||
// sample reflection cubemap with the appropriate roughness mip
|
||||
|
||||
@@ -75,7 +75,7 @@ PSOutput MainPS(VSOutput IN, in uint sampleIndex : SV_SampleIndex)
|
||||
|
||||
// compute specular using the probe cubemap and the roughness, normals, and specularF0 for the surface
|
||||
float3 specular = float3(0.0f, 0.0f, 0.0f);
|
||||
if (!ComputeProbeSpecular(IN.m_position.xy, positionWS, ObjectSrg::m_innerAabbMin, ObjectSrg::m_innerAabbMax, sampleIndex, specular))
|
||||
if (!ComputeProbeSpecular(IN.m_position.xy, positionWS, ObjectSrg::GetWorldMatrixInverse(), ObjectSrg::m_innerObbHalfLengths, sampleIndex, specular))
|
||||
{
|
||||
discard;
|
||||
}
|
||||
|
||||
+17
-6
@@ -13,12 +13,9 @@
|
||||
ShaderResourceGroup ObjectSrg : SRG_PerObject
|
||||
{
|
||||
row_major float3x4 m_modelToWorld;
|
||||
|
||||
float3 m_aabbPos;
|
||||
float3 m_outerAabbMin;
|
||||
float3 m_outerAabbMax;
|
||||
float3 m_innerAabbMin;
|
||||
float3 m_innerAabbMax;
|
||||
row_major float3x4 m_modelToWorldInverse; // does not include extents
|
||||
float3 m_outerObbHalfLengths;
|
||||
float3 m_innerObbHalfLengths;
|
||||
bool m_useParallaxCorrection;
|
||||
TextureCube m_reflectionCubeMap;
|
||||
|
||||
@@ -35,4 +32,18 @@ ShaderResourceGroup ObjectSrg : SRG_PerObject
|
||||
modelToWorld[2] = ObjectSrg::m_modelToWorld[2];
|
||||
return modelToWorld;
|
||||
}
|
||||
|
||||
float4x4 GetWorldMatrixInverse()
|
||||
{
|
||||
float4x4 modelToWorldInverse = float4x4(
|
||||
float4(1, 0, 0, 0),
|
||||
float4(0, 1, 0, 0),
|
||||
float4(0, 0, 1, 0),
|
||||
float4(0, 0, 0, 1));
|
||||
|
||||
modelToWorldInverse[0] = ObjectSrg::m_modelToWorldInverse[0];
|
||||
modelToWorldInverse[1] = ObjectSrg::m_modelToWorldInverse[1];
|
||||
modelToWorldInverse[2] = ObjectSrg::m_modelToWorldInverse[2];
|
||||
return modelToWorldInverse;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ PSOutput MainPS(VSOutput IN, in uint sampleIndex : SV_SampleIndex)
|
||||
|
||||
// compute specular using the probe cubemap and the roughness, normals, and specularF0 for the surface
|
||||
float3 specular = float3(0.0f, 0.0f, 0.0f);
|
||||
if (!ComputeProbeSpecular(IN.m_position.xy, positionWS, ObjectSrg::m_outerAabbMin, ObjectSrg::m_outerAabbMax, sampleIndex, specular))
|
||||
if (!ComputeProbeSpecular(IN.m_position.xy, positionWS, ObjectSrg::GetWorldMatrixInverse(), ObjectSrg::m_outerObbHalfLengths, sampleIndex, specular))
|
||||
{
|
||||
discard;
|
||||
}
|
||||
@@ -85,13 +85,17 @@ PSOutput MainPS(VSOutput IN, in uint sampleIndex : SV_SampleIndex)
|
||||
// determine blend based on position with respect to the inner and outer AABBs
|
||||
// if it's inside the inner AABB it blends at 100%, otherwise it's the percentage of the distance between the inner/outer AABB
|
||||
float blendWeight = 1.0f;
|
||||
if (!AabbContainsPoint(ObjectSrg::m_innerAabbMin, ObjectSrg::m_innerAabbMax, positionWS))
|
||||
if (!ObbContainsPoint(ObjectSrg::GetWorldMatrixInverse(), ObjectSrg::m_innerObbHalfLengths, positionWS))
|
||||
{
|
||||
// not inside the inner AABB, so it's in between the inner and outer AABBs
|
||||
// compute blend amount based on the distance to the outer AABB
|
||||
blendWeight = ComputeLerpBetweenInnerOuterAABBs(ObjectSrg::m_innerAabbMin, ObjectSrg::m_innerAabbMax, ObjectSrg::m_outerAabbMax, ObjectSrg::m_aabbPos, positionWS);
|
||||
blendWeight = ComputeLerpBetweenInnerOuterOBBs(
|
||||
ObjectSrg::GetWorldMatrixInverse(),
|
||||
ObjectSrg::m_innerObbHalfLengths,
|
||||
ObjectSrg::m_outerObbHalfLengths,
|
||||
positionWS);
|
||||
}
|
||||
|
||||
|
||||
// retrieve the blend weight of all probes at this location
|
||||
float blendWeightAllProbes = PassSrg::m_blendWeight.Load(IN.m_position.xy, sampleIndex).r;
|
||||
|
||||
|
||||
@@ -1111,20 +1111,17 @@ namespace AZ
|
||||
if (reflectionProbeFeatureProcessor && (m_descriptor.m_useForwardPassIblSpecular || m_hasForwardPassIblSpecularMaterial))
|
||||
{
|
||||
// retrieve probe constant indices
|
||||
AZ::RHI::ShaderInputConstantIndex posConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_aabbPos"));
|
||||
AZ_Error("MeshDataInstance", posConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index");
|
||||
AZ::RHI::ShaderInputConstantIndex modelToWorldConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_modelToWorld"));
|
||||
AZ_Error("MeshDataInstance", modelToWorldConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index");
|
||||
|
||||
AZ::RHI::ShaderInputConstantIndex outerAabbMinConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_outerAabbMin"));
|
||||
AZ_Error("MeshDataInstance", outerAabbMinConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index");
|
||||
AZ::RHI::ShaderInputConstantIndex modelToWorldInverseConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_modelToWorldInverse"));
|
||||
AZ_Error("MeshDataInstance", modelToWorldInverseConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index");
|
||||
|
||||
AZ::RHI::ShaderInputConstantIndex outerAabbMaxConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_outerAabbMax"));
|
||||
AZ_Error("MeshDataInstance", outerAabbMaxConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index");
|
||||
AZ::RHI::ShaderInputConstantIndex outerObbHalfLengthsConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_outerObbHalfLengths"));
|
||||
AZ_Error("MeshDataInstance", outerObbHalfLengthsConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index");
|
||||
|
||||
AZ::RHI::ShaderInputConstantIndex innerAabbMinConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_innerAabbMin"));
|
||||
AZ_Error("MeshDataInstance", innerAabbMinConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index");
|
||||
|
||||
AZ::RHI::ShaderInputConstantIndex innerAabbMaxConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_innerAabbMax"));
|
||||
AZ_Error("MeshDataInstance", innerAabbMaxConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index");
|
||||
AZ::RHI::ShaderInputConstantIndex innerObbHalfLengthsConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_innerObbHalfLengths"));
|
||||
AZ_Error("MeshDataInstance", innerObbHalfLengthsConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index");
|
||||
|
||||
AZ::RHI::ShaderInputConstantIndex useReflectionProbeConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_useReflectionProbe"));
|
||||
AZ_Error("MeshDataInstance", useReflectionProbeConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index");
|
||||
@@ -1146,11 +1143,10 @@ namespace AZ
|
||||
|
||||
if (!reflectionProbes.empty() && reflectionProbes[0])
|
||||
{
|
||||
m_shaderResourceGroup->SetConstant(posConstantIndex, reflectionProbes[0]->GetPosition());
|
||||
m_shaderResourceGroup->SetConstant(outerAabbMinConstantIndex, reflectionProbes[0]->GetOuterAabbWs().GetMin());
|
||||
m_shaderResourceGroup->SetConstant(outerAabbMaxConstantIndex, reflectionProbes[0]->GetOuterAabbWs().GetMax());
|
||||
m_shaderResourceGroup->SetConstant(innerAabbMinConstantIndex, reflectionProbes[0]->GetInnerAabbWs().GetMin());
|
||||
m_shaderResourceGroup->SetConstant(innerAabbMaxConstantIndex, reflectionProbes[0]->GetInnerAabbWs().GetMax());
|
||||
m_shaderResourceGroup->SetConstant(modelToWorldConstantIndex, reflectionProbes[0]->GetTransform());
|
||||
m_shaderResourceGroup->SetConstant(modelToWorldInverseConstantIndex, Matrix3x4::CreateFromTransform(reflectionProbes[0]->GetTransform()).GetInverseFull());
|
||||
m_shaderResourceGroup->SetConstant(outerObbHalfLengthsConstantIndex, reflectionProbes[0]->GetOuterObbWs().GetHalfLengths());
|
||||
m_shaderResourceGroup->SetConstant(innerObbHalfLengthsConstantIndex, reflectionProbes[0]->GetInnerObbWs().GetHalfLengths());
|
||||
m_shaderResourceGroup->SetConstant(useReflectionProbeConstantIndex, true);
|
||||
m_shaderResourceGroup->SetConstant(useParallaxCorrectionConstantIndex, reflectionProbes[0]->GetUseParallaxCorrection());
|
||||
|
||||
|
||||
@@ -138,43 +138,39 @@ namespace AZ
|
||||
if (m_updateSrg)
|
||||
{
|
||||
// stencil Srg
|
||||
// Note: the stencil pass uses a slightly reduced inner AABB to avoid seams
|
||||
// Note: the stencil pass uses a slightly reduced inner OBB to avoid seams
|
||||
Vector3 innerExtentsReduced = m_innerExtents - Vector3(0.1f, 0.1f, 0.1f);
|
||||
Matrix3x4 modelToWorldStencil = Matrix3x4::CreateFromMatrix3x3AndTranslation(Matrix3x3::CreateIdentity(), m_transform.GetTranslation()) * Matrix3x4::CreateScale(innerExtentsReduced);
|
||||
Matrix3x4 modelToWorldStencil = Matrix3x4::CreateFromQuaternionAndTranslation(m_transform.GetRotation(), m_transform.GetTranslation()) * Matrix3x4::CreateScale(innerExtentsReduced);
|
||||
m_stencilSrg->SetConstant(m_reflectionRenderData->m_modelToWorldStencilConstantIndex, modelToWorldStencil);
|
||||
m_stencilSrg->Compile();
|
||||
|
||||
Matrix3x4 modelToWorldInverse = Matrix3x4::CreateFromTransform(m_transform).GetInverseFull();
|
||||
|
||||
// blend weight Srg
|
||||
Matrix3x4 modelToWorldOuter = Matrix3x4::CreateFromMatrix3x3AndTranslation(Matrix3x3::CreateIdentity(), m_transform.GetTranslation()) * Matrix3x4::CreateScale(m_outerExtents);
|
||||
Matrix3x4 modelToWorldOuter = Matrix3x4::CreateFromQuaternionAndTranslation(m_transform.GetRotation(), m_transform.GetTranslation()) * Matrix3x4::CreateScale(m_outerExtents);
|
||||
m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_modelToWorldRenderConstantIndex, modelToWorldOuter);
|
||||
m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_aabbPosRenderConstantIndex, m_outerAabbWs.GetCenter());
|
||||
m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_outerAabbMinRenderConstantIndex, m_outerAabbWs.GetMin());
|
||||
m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_outerAabbMaxRenderConstantIndex, m_outerAabbWs.GetMax());
|
||||
m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_innerAabbMinRenderConstantIndex, m_innerAabbWs.GetMin());
|
||||
m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_innerAabbMaxRenderConstantIndex, m_innerAabbWs.GetMax());
|
||||
m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_modelToWorldInverseRenderConstantIndex, modelToWorldInverse);
|
||||
m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_outerObbHalfLengthsRenderConstantIndex, m_outerObbWs.GetHalfLengths());
|
||||
m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_innerObbHalfLengthsRenderConstantIndex, m_innerObbWs.GetHalfLengths());
|
||||
m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_useParallaxCorrectionRenderConstantIndex, m_useParallaxCorrection);
|
||||
m_blendWeightSrg->SetImage(m_reflectionRenderData->m_reflectionCubeMapRenderImageIndex, m_cubeMapImage);
|
||||
m_blendWeightSrg->Compile();
|
||||
|
||||
// render outer Srg
|
||||
m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_modelToWorldRenderConstantIndex, modelToWorldOuter);
|
||||
m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_aabbPosRenderConstantIndex, m_outerAabbWs.GetCenter());
|
||||
m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_outerAabbMinRenderConstantIndex, m_outerAabbWs.GetMin());
|
||||
m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_outerAabbMaxRenderConstantIndex, m_outerAabbWs.GetMax());
|
||||
m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_innerAabbMinRenderConstantIndex, m_innerAabbWs.GetMin());
|
||||
m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_innerAabbMaxRenderConstantIndex, m_innerAabbWs.GetMax());
|
||||
m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_modelToWorldInverseRenderConstantIndex, modelToWorldInverse);
|
||||
m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_outerObbHalfLengthsRenderConstantIndex, m_outerObbWs.GetHalfLengths());
|
||||
m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_innerObbHalfLengthsRenderConstantIndex, m_innerObbWs.GetHalfLengths());
|
||||
m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_useParallaxCorrectionRenderConstantIndex, m_useParallaxCorrection);
|
||||
m_renderOuterSrg->SetImage(m_reflectionRenderData->m_reflectionCubeMapRenderImageIndex, m_cubeMapImage);
|
||||
m_renderOuterSrg->Compile();
|
||||
|
||||
// render inner Srg
|
||||
Matrix3x4 modelToWorldInner = Matrix3x4::CreateFromMatrix3x3AndTranslation(Matrix3x3::CreateIdentity(), m_transform.GetTranslation()) * Matrix3x4::CreateScale(m_innerExtents);
|
||||
Matrix3x4 modelToWorldInner = Matrix3x4::CreateFromQuaternionAndTranslation(m_transform.GetRotation(), m_transform.GetTranslation()) * Matrix3x4::CreateScale(m_innerExtents);
|
||||
m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_modelToWorldRenderConstantIndex, modelToWorldInner);
|
||||
m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_aabbPosRenderConstantIndex, m_outerAabbWs.GetCenter());
|
||||
m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_outerAabbMinRenderConstantIndex, m_outerAabbWs.GetMin());
|
||||
m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_outerAabbMaxRenderConstantIndex, m_outerAabbWs.GetMax());
|
||||
m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_innerAabbMinRenderConstantIndex, m_innerAabbWs.GetMin());
|
||||
m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_innerAabbMaxRenderConstantIndex, m_innerAabbWs.GetMax());
|
||||
m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_modelToWorldInverseRenderConstantIndex, modelToWorldInverse);
|
||||
m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_outerObbHalfLengthsRenderConstantIndex, m_outerObbWs.GetHalfLengths());
|
||||
m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_innerObbHalfLengthsRenderConstantIndex, m_innerObbWs.GetHalfLengths());
|
||||
m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_useParallaxCorrectionRenderConstantIndex, m_useParallaxCorrection);
|
||||
m_renderInnerSrg->SetImage(m_reflectionRenderData->m_reflectionCubeMapRenderImageIndex, m_cubeMapImage);
|
||||
m_renderInnerSrg->Compile();
|
||||
@@ -244,22 +240,22 @@ namespace AZ
|
||||
m_outerExtents *= m_transform.GetUniformScale();
|
||||
m_innerExtents *= m_transform.GetUniformScale();
|
||||
|
||||
m_outerAabbWs = Aabb::CreateCenterHalfExtents(m_transform.GetTranslation(), m_outerExtents / 2.0f);
|
||||
m_innerAabbWs = Aabb::CreateCenterHalfExtents(m_transform.GetTranslation(), m_innerExtents / 2.0f);
|
||||
m_outerObbWs = Obb::CreateFromPositionRotationAndHalfLengths(m_transform.GetTranslation(), m_transform.GetRotation(), m_outerExtents / 2.0f);
|
||||
m_innerObbWs = Obb::CreateFromPositionRotationAndHalfLengths(m_transform.GetTranslation(), m_transform.GetRotation(), m_innerExtents / 2.0f);
|
||||
m_updateSrg = true;
|
||||
}
|
||||
|
||||
void ReflectionProbe::SetOuterExtents(const AZ::Vector3& outerExtents)
|
||||
{
|
||||
m_outerExtents = outerExtents * m_transform.GetUniformScale();
|
||||
m_outerAabbWs = Aabb::CreateCenterHalfExtents(m_transform.GetTranslation(), m_outerExtents / 2.0f);
|
||||
m_outerObbWs = Obb::CreateFromPositionRotationAndHalfLengths(m_transform.GetTranslation(), m_transform.GetRotation(), m_outerExtents / 2.0f);
|
||||
m_updateSrg = true;
|
||||
}
|
||||
|
||||
void ReflectionProbe::SetInnerExtents(const AZ::Vector3& innerExtents)
|
||||
{
|
||||
m_innerExtents = innerExtents * m_transform.GetUniformScale();
|
||||
m_innerAabbWs = Aabb::CreateCenterHalfExtents(m_transform.GetTranslation(), m_innerExtents / 2.0f);
|
||||
m_innerObbWs = Obb::CreateFromPositionRotationAndHalfLengths(m_transform.GetTranslation(), m_transform.GetRotation(), m_innerExtents / 2.0f);
|
||||
m_updateSrg = true;
|
||||
}
|
||||
|
||||
@@ -410,13 +406,14 @@ namespace AZ
|
||||
lod.m_screenCoverageMax = 1.0f;
|
||||
|
||||
// update cullable bounds
|
||||
Aabb outerAabb = Aabb::CreateFromObb(m_outerObbWs);
|
||||
Vector3 center;
|
||||
float radius;
|
||||
m_outerAabbWs.GetAsSphere(center, radius);
|
||||
outerAabb.GetAsSphere(center, radius);
|
||||
|
||||
m_cullable.m_cullData.m_boundingSphere = Sphere(center, radius);
|
||||
m_cullable.m_cullData.m_boundingObb = m_outerAabbWs.GetTransformedObb(AZ::Transform::CreateIdentity());
|
||||
m_cullable.m_cullData.m_visibilityEntry.m_boundingVolume = m_outerAabbWs;
|
||||
m_cullable.m_cullData.m_boundingObb = m_outerObbWs;
|
||||
m_cullable.m_cullData.m_visibilityEntry.m_boundingVolume = outerAabb;
|
||||
m_cullable.m_cullData.m_visibilityEntry.m_userData = &m_cullable;
|
||||
m_cullable.m_cullData.m_visibilityEntry.m_typeFlags = AzFramework::VisibilityEntry::TYPE_RPI_Cullable;
|
||||
|
||||
|
||||
@@ -55,15 +55,13 @@ namespace AZ
|
||||
RHI::DrawListTag m_renderOuterDrawListTag;
|
||||
RHI::DrawListTag m_renderInnerDrawListTag;
|
||||
|
||||
RHI::ShaderInputConstantIndex m_modelToWorldStencilConstantIndex;
|
||||
RHI::ShaderInputConstantIndex m_modelToWorldRenderConstantIndex;
|
||||
RHI::ShaderInputConstantIndex m_aabbPosRenderConstantIndex;
|
||||
RHI::ShaderInputConstantIndex m_outerAabbMinRenderConstantIndex;
|
||||
RHI::ShaderInputConstantIndex m_outerAabbMaxRenderConstantIndex;
|
||||
RHI::ShaderInputConstantIndex m_innerAabbMinRenderConstantIndex;
|
||||
RHI::ShaderInputConstantIndex m_innerAabbMaxRenderConstantIndex;
|
||||
RHI::ShaderInputConstantIndex m_useParallaxCorrectionRenderConstantIndex;
|
||||
RHI::ShaderInputImageIndex m_reflectionCubeMapRenderImageIndex;
|
||||
RHI::ShaderInputNameIndex m_modelToWorldStencilConstantIndex = "m_modelToWorld";
|
||||
RHI::ShaderInputNameIndex m_modelToWorldRenderConstantIndex = "m_modelToWorld";
|
||||
RHI::ShaderInputNameIndex m_modelToWorldInverseRenderConstantIndex = "m_modelToWorldInverse";
|
||||
RHI::ShaderInputNameIndex m_outerObbHalfLengthsRenderConstantIndex = "m_outerObbHalfLengths";
|
||||
RHI::ShaderInputNameIndex m_innerObbHalfLengthsRenderConstantIndex = "m_innerObbHalfLengths";
|
||||
RHI::ShaderInputNameIndex m_useParallaxCorrectionRenderConstantIndex = "m_useParallaxCorrection";
|
||||
RHI::ShaderInputNameIndex m_reflectionCubeMapRenderImageIndex = "m_reflectionCubeMap";
|
||||
};
|
||||
|
||||
// ReflectionProbe manages all aspects of a single probe, including rendering, visualization, and cubemap generation
|
||||
@@ -78,6 +76,7 @@ namespace AZ
|
||||
void Simulate(uint32_t probeIndex);
|
||||
|
||||
const Vector3& GetPosition() const { return m_transform.GetTranslation(); }
|
||||
const AZ::Transform& GetTransform() const { return m_transform; }
|
||||
void SetTransform(const AZ::Transform& transform);
|
||||
|
||||
const AZ::Vector3& GetOuterExtents() const { return m_outerExtents; }
|
||||
@@ -86,8 +85,8 @@ namespace AZ
|
||||
const AZ::Vector3& GetInnerExtents() const { return m_innerExtents; }
|
||||
void SetInnerExtents(const AZ::Vector3& innerExtents);
|
||||
|
||||
const Aabb& GetOuterAabbWs() const { return m_outerAabbWs; }
|
||||
const Aabb& GetInnerAabbWs() const { return m_innerAabbWs; }
|
||||
const Obb& GetOuterObbWs() const { return m_outerObbWs; }
|
||||
const Obb& GetInnerObbWs() const { return m_innerObbWs; }
|
||||
|
||||
const Data::Instance<RPI::Image>& GetCubeMapImage() const { return m_cubeMapImage; }
|
||||
void SetCubeMapImage(const Data::Instance<RPI::Image>& cubeMapImage, const AZStd::string& relativePath);
|
||||
@@ -133,9 +132,9 @@ namespace AZ
|
||||
AZ::Vector3 m_outerExtents = AZ::Vector3(0.0f, 0.0f, 0.0f);
|
||||
AZ::Vector3 m_innerExtents = AZ::Vector3(0.0f, 0.0f, 0.0f);
|
||||
|
||||
// probe volume AABBs (world space), built from position and extents
|
||||
Aabb m_outerAabbWs;
|
||||
Aabb m_innerAabbWs;
|
||||
// probe volume OBBs (world space), built from position and extents
|
||||
Obb m_outerObbWs;
|
||||
Obb m_innerObbWs;
|
||||
|
||||
// cubemap
|
||||
Data::Instance<RPI::Image> m_cubeMapImage;
|
||||
|
||||
+6
-60
@@ -77,61 +77,6 @@ namespace AZ
|
||||
m_reflectionRenderData.m_renderInnerSrgLayout,
|
||||
m_reflectionRenderData.m_renderInnerDrawListTag);
|
||||
|
||||
// create ShaderResourceGroups here so we can get the layout and cache the indices
|
||||
// Note: the SRGs are not needed beyond this method since each probe creates its own SRGs, we are just interested in the indices
|
||||
|
||||
// cache probe stencil shader indices
|
||||
Data::Instance<RPI::ShaderResourceGroup> stencilSrg = RPI::ShaderResourceGroup::Create(
|
||||
m_reflectionRenderData.m_stencilShader->GetAsset(),
|
||||
m_reflectionRenderData.m_stencilShader->GetSupervariantIndex(),
|
||||
m_reflectionRenderData.m_stencilSrgLayout->GetName());
|
||||
AZ_Error("ReflectionProbeFeatureProcessor", stencilSrg.get(), "Failed to create stencil back face shader resource group");
|
||||
|
||||
const RHI::ShaderResourceGroupLayout* stencilSrgLayout = stencilSrg->GetLayout();
|
||||
Name modelToWorldConstantName = Name("m_modelToWorld");
|
||||
m_reflectionRenderData.m_modelToWorldStencilConstantIndex = stencilSrgLayout->FindShaderInputConstantIndex(modelToWorldConstantName);
|
||||
AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_modelToWorldStencilConstantIndex.IsValid(), "Failed to find stencil shader input constant [%s]", modelToWorldConstantName.GetCStr());
|
||||
|
||||
// cache probe render shader indices
|
||||
// Note: the outer and inner render shaders use the same Srg
|
||||
Data::Instance<RPI::ShaderResourceGroup> renderReflectionSrg = RPI::ShaderResourceGroup::Create(
|
||||
m_reflectionRenderData.m_renderOuterShader->GetAsset(),
|
||||
m_reflectionRenderData.m_renderOuterShader->GetSupervariantIndex(),
|
||||
m_reflectionRenderData.m_renderOuterSrgLayout->GetName());
|
||||
AZ_Error("ReflectionProbeFeatureProcessor", renderReflectionSrg.get(), "Failed to create render reflection shader resource group");
|
||||
|
||||
const RHI::ShaderResourceGroupLayout* renderReflectionSrgLayout = renderReflectionSrg->GetLayout();
|
||||
m_reflectionRenderData.m_modelToWorldRenderConstantIndex = renderReflectionSrgLayout->FindShaderInputConstantIndex(modelToWorldConstantName);
|
||||
AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_modelToWorldRenderConstantIndex.IsValid(), "Failed to find render shader input constant [%s]", modelToWorldConstantName.GetCStr());
|
||||
|
||||
Name aabbPosConstantName = Name("m_aabbPos");
|
||||
m_reflectionRenderData.m_aabbPosRenderConstantIndex = renderReflectionSrgLayout->FindShaderInputConstantIndex(aabbPosConstantName);
|
||||
AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_aabbPosRenderConstantIndex.IsValid(), "Failed to find render shader input constant [%s]", aabbPosConstantName.GetCStr());
|
||||
|
||||
Name outerAabbMinConstantName = Name("m_outerAabbMin");
|
||||
m_reflectionRenderData.m_outerAabbMinRenderConstantIndex = renderReflectionSrgLayout->FindShaderInputConstantIndex(outerAabbMinConstantName);
|
||||
AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_outerAabbMinRenderConstantIndex.IsValid(), "Failed to find render shader input constant [%s]", outerAabbMinConstantName.GetCStr());
|
||||
|
||||
Name outerAabbMaxConstantName = Name("m_outerAabbMax");
|
||||
m_reflectionRenderData.m_outerAabbMaxRenderConstantIndex = renderReflectionSrgLayout->FindShaderInputConstantIndex(outerAabbMaxConstantName);
|
||||
AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_outerAabbMaxRenderConstantIndex.IsValid(), "Failed to find render shader input constant [%s]", outerAabbMaxConstantName.GetCStr());
|
||||
|
||||
Name innerAabbMinConstantName = Name("m_innerAabbMin");
|
||||
m_reflectionRenderData.m_innerAabbMinRenderConstantIndex = renderReflectionSrgLayout->FindShaderInputConstantIndex(innerAabbMinConstantName);
|
||||
AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_innerAabbMinRenderConstantIndex.IsValid(), "Failed to find render shader input constant [%s]", innerAabbMinConstantName.GetCStr());
|
||||
|
||||
Name innerAabbMaxConstantName = Name("m_innerAabbMax");
|
||||
m_reflectionRenderData.m_innerAabbMaxRenderConstantIndex = renderReflectionSrgLayout->FindShaderInputConstantIndex(innerAabbMaxConstantName);
|
||||
AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_innerAabbMaxRenderConstantIndex.IsValid(), "Failed to find render shader input constant [%s]", innerAabbMaxConstantName.GetCStr());
|
||||
|
||||
Name useParallaxCorrectionConstantName = Name("m_useParallaxCorrection");
|
||||
m_reflectionRenderData.m_useParallaxCorrectionRenderConstantIndex = renderReflectionSrgLayout->FindShaderInputConstantIndex(useParallaxCorrectionConstantName);
|
||||
AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_useParallaxCorrectionRenderConstantIndex.IsValid(), "Failed to find render shader input constant [%s]", useParallaxCorrectionConstantName.GetCStr());
|
||||
|
||||
Name reflectionCubeMapImageName = Name("m_reflectionCubeMap");
|
||||
m_reflectionRenderData.m_reflectionCubeMapRenderImageIndex = renderReflectionSrgLayout->FindShaderInputImageIndex(reflectionCubeMapImageName);
|
||||
AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_reflectionCubeMapRenderImageIndex.IsValid(), "Failed to find render shader input image [%s]", reflectionCubeMapImageName.GetCStr());
|
||||
|
||||
EnableSceneNotification();
|
||||
}
|
||||
|
||||
@@ -197,10 +142,11 @@ namespace AZ
|
||||
// sort the probes by descending inner volume size, so the smallest volumes are rendered last
|
||||
auto sortFn = [](AZStd::shared_ptr<ReflectionProbe> const& probe1, AZStd::shared_ptr<ReflectionProbe> const& probe2) -> bool
|
||||
{
|
||||
const Aabb& aabb1 = probe1->GetInnerAabbWs();
|
||||
const Aabb& aabb2 = probe2->GetInnerAabbWs();
|
||||
float size1 = aabb1.GetXExtent() * aabb1.GetZExtent() * aabb1.GetYExtent();
|
||||
float size2 = aabb2.GetXExtent() * aabb2.GetZExtent() * aabb2.GetYExtent();
|
||||
const Obb& obb1 = probe1->GetInnerObbWs();
|
||||
const Obb& obb2 = probe2->GetInnerObbWs();
|
||||
float size1 = obb1.GetHalfLengthX() * obb1.GetHalfLengthZ() * obb1.GetHalfLengthY();
|
||||
float size2 = obb2.GetHalfLengthX() * obb2.GetHalfLengthZ() * obb2.GetHalfLengthY();
|
||||
|
||||
return (size1 > size2);
|
||||
};
|
||||
|
||||
@@ -345,7 +291,7 @@ namespace AZ
|
||||
// simple AABB check to find the reflection probes that contain the position
|
||||
for (auto& reflectionProbe : m_reflectionProbes)
|
||||
{
|
||||
if (reflectionProbe->GetOuterAabbWs().Contains(position)
|
||||
if (reflectionProbe->GetOuterObbWs().Contains(position)
|
||||
&& reflectionProbe->GetCubeMapImage()
|
||||
&& reflectionProbe->GetCubeMapImage()->IsInitialized())
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
+23
@@ -116,11 +116,18 @@ namespace AtomToolsFramework
|
||||
// ModularViewportCameraControllerRequestBus overrides ...
|
||||
void InterpolateToTransform(const AZ::Transform& worldFromLocal, float lookAtDistance) override;
|
||||
AZStd::optional<AZ::Vector3> LookAtAfterInterpolation() const override;
|
||||
AZ::Transform GetReferenceFrame() const override;
|
||||
void SetReferenceFrame(const AZ::Transform& worldFromLocal) override;
|
||||
void ClearReferenceFrame() override;
|
||||
|
||||
private:
|
||||
// AzFramework::ViewportDebugDisplayEventBus overrides ...
|
||||
void DisplayViewport(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) override;
|
||||
|
||||
//! Update the reference frame after a change has been made to the camera
|
||||
//! view without updating the internal camera via user input.
|
||||
void RefreshReferenceFrame();
|
||||
|
||||
//! The current mode the camera controller is in.
|
||||
enum class CameraMode
|
||||
{
|
||||
@@ -139,6 +146,8 @@ namespace AtomToolsFramework
|
||||
|
||||
AzFramework::Camera m_camera; //!< The current camera state (pitch/yaw/position/look-distance).
|
||||
AzFramework::Camera m_targetCamera; //!< The target (next) camera state that m_camera is catching up to.
|
||||
AzFramework::Camera m_previousCamera; //!< The state of the camera from the previous frame.
|
||||
AZStd::optional<AzFramework::Camera> m_storedCamera; //!< A potentially stored camera for when a custom reference frame is set.
|
||||
AzFramework::CameraSystem m_cameraSystem; //!< The camera system responsible for managing all CameraInputs.
|
||||
AzFramework::CameraProps m_cameraProps; //!< Camera properties to control rotate and translate smoothness.
|
||||
CameraControllerPriorityFn m_priorityFn; //!< Controls at what priority the camera controller should respond to events.
|
||||
@@ -147,6 +156,7 @@ namespace AtomToolsFramework
|
||||
CameraMode m_cameraMode = CameraMode::Control; //!< The current mode the camera is operating in.
|
||||
AZStd::optional<AZ::Vector3> m_lookAtAfterInterpolation; //!< The look at point after an interpolation has finished.
|
||||
//!< Will be cleared when the view changes (camera looks away).
|
||||
AZ::Transform m_referenceFrameOverride = AZ::Transform::CreateIdentity(); //!<
|
||||
//! Flag to prevent circular updates of the camera transform (while the viewport transform is being updated internally).
|
||||
bool m_updatingTransformInternally = false;
|
||||
//! Listen for camera view changes outside of the camera controller.
|
||||
@@ -154,4 +164,17 @@ namespace AtomToolsFramework
|
||||
//! The current instance of the modular camera viewport context.
|
||||
AZStd::unique_ptr<ModularCameraViewportContext> m_modularCameraViewportContext;
|
||||
};
|
||||
|
||||
//! Placeholder implementation for ModularCameraViewportContext (useful for verifying the interface).
|
||||
class PlaceholderModularCameraViewportContextImpl : public AtomToolsFramework::ModularCameraViewportContext
|
||||
{
|
||||
public:
|
||||
AZ::Transform GetCameraTransform() const override;
|
||||
void SetCameraTransform(const AZ::Transform& transform) override;
|
||||
void ConnectViewMatrixChangedHandler(AZ::RPI::ViewportContext::MatrixChangedEvent::Handler& handler) override;
|
||||
|
||||
private:
|
||||
AZ::Transform m_cameraTransform = AZ::Transform::CreateIdentity();
|
||||
AZ::RPI::ViewportContext::MatrixChangedEvent m_viewMatrixChangedEvent;
|
||||
};
|
||||
} // namespace AtomToolsFramework
|
||||
|
||||
+10
@@ -35,6 +35,16 @@ namespace AtomToolsFramework
|
||||
//! Look at point after an interpolation has finished and no translation has occurred.
|
||||
virtual AZStd::optional<AZ::Vector3> LookAtAfterInterpolation() const = 0;
|
||||
|
||||
//! Return the current reference frame.
|
||||
//! @note If a reference frame has not been set or a frame has been cleared, this is just the identity.
|
||||
virtual AZ::Transform GetReferenceFrame() const = 0;
|
||||
|
||||
//! Set a new reference frame other than the identity for the camera controller.
|
||||
virtual void SetReferenceFrame(const AZ::Transform& worldFromLocal) = 0;
|
||||
|
||||
//! Clear the current reference frame to restore the identity.
|
||||
virtual void ClearReferenceFrame() = 0;
|
||||
|
||||
protected:
|
||||
~ModularViewportCameraControllerRequests() = default;
|
||||
};
|
||||
|
||||
@@ -87,6 +87,7 @@ namespace AtomToolsFramework
|
||||
|
||||
AtomToolsApplication ::~AtomToolsApplication()
|
||||
{
|
||||
m_styleManager.reset();
|
||||
AtomToolsMainWindowNotificationBus::Handler::BusDisconnect();
|
||||
AzToolsFramework::AssetDatabase::AssetDatabaseRequestsBus::Handler::BusDisconnect();
|
||||
AzToolsFramework::EditorPythonConsoleNotificationBus::Handler::BusDisconnect();
|
||||
@@ -174,12 +175,14 @@ namespace AtomToolsFramework
|
||||
|
||||
AZ::Data::AssetCatalogRequestBus::Broadcast(&AZ::Data::AssetCatalogRequestBus::Events::LoadCatalog, "@assets@/assetcatalog.xml");
|
||||
|
||||
AZ::RPI::RPISystemInterface::Get()->InitializeSystemAssets();
|
||||
if (!AZ::RPI::RPISystemInterface::Get()->IsInitialized())
|
||||
{
|
||||
AZ::RPI::RPISystemInterface::Get()->InitializeSystemAssets();
|
||||
}
|
||||
|
||||
LoadSettings();
|
||||
|
||||
AtomToolsMainWindowNotificationBus::Handler::BusConnect();
|
||||
|
||||
AtomToolsMainWindowFactoryRequestBus::Broadcast(&AtomToolsMainWindowFactoryRequestBus::Handler::CreateMainWindow);
|
||||
|
||||
auto editorPythonEventsInterface = AZ::Interface<AzToolsFramework::EditorPythonEventsInterface>::Get();
|
||||
@@ -206,6 +209,7 @@ namespace AtomToolsFramework
|
||||
{
|
||||
// before modules are unloaded, destroy UI to free up any assets it cached
|
||||
AtomToolsMainWindowFactoryRequestBus::Broadcast(&AtomToolsMainWindowFactoryRequestBus::Handler::DestroyMainWindow);
|
||||
m_styleManager.reset();
|
||||
|
||||
AzToolsFramework::EditorPythonConsoleNotificationBus::Handler::BusDisconnect();
|
||||
AzToolsFramework::AssetDatabase::AssetDatabaseRequestsBus::Handler::BusDisconnect();
|
||||
@@ -461,6 +465,7 @@ namespace AtomToolsFramework
|
||||
void AtomToolsApplication::Stop()
|
||||
{
|
||||
AtomToolsMainWindowFactoryRequestBus::Broadcast(&AtomToolsMainWindowFactoryRequestBus::Handler::DestroyMainWindow);
|
||||
m_styleManager.reset();
|
||||
|
||||
UnloadSettings();
|
||||
Base::Stop();
|
||||
@@ -468,7 +473,7 @@ namespace AtomToolsFramework
|
||||
|
||||
void AtomToolsApplication::QueryApplicationType(AZ::ApplicationTypeQuery& appType) const
|
||||
{
|
||||
appType.m_maskValue = AZ::ApplicationTypeQuery::Masks::Game;
|
||||
appType.m_maskValue = AZ::ApplicationTypeQuery::Masks::Tool;
|
||||
}
|
||||
|
||||
void AtomToolsApplication::OnTraceMessage([[maybe_unused]] AZStd::string_view message)
|
||||
|
||||
+83
-7
@@ -30,6 +30,18 @@ namespace AtomToolsFramework
|
||||
"");
|
||||
AZ_CVAR(float, ed_cameraSystemOrbitPointSize, 0.1f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
|
||||
|
||||
AZ::Transform TransformFromMatrix4x4(const AZ::Matrix4x4& matrix)
|
||||
{
|
||||
const auto rotation = AZ::Matrix3x3::CreateFromMatrix4x4(matrix);
|
||||
const auto translation = matrix.GetTranslation();
|
||||
return AZ::Transform::CreateFromMatrix3x3AndTranslation(rotation, translation);
|
||||
}
|
||||
|
||||
AZ::Matrix4x4 Matrix4x4FromTransform(const AZ::Transform& transform)
|
||||
{
|
||||
return AZ::Matrix4x4::CreateFromQuaternionAndTranslation(transform.GetRotation(), transform.GetTranslation());
|
||||
}
|
||||
|
||||
// debug
|
||||
void DrawPreviewAxis(AzFramework::DebugDisplayRequests& display, const AZ::Transform& transform, const float axisLength)
|
||||
{
|
||||
@@ -167,11 +179,19 @@ namespace AtomToolsFramework
|
||||
controller->SetupCameraControllerPriority(m_priorityFn);
|
||||
controller->SetupCameraControllerViewportContext(m_modularCameraViewportContext);
|
||||
|
||||
auto handleCameraChange = [this](const AZ::Matrix4x4&)
|
||||
auto handleCameraChange = [this]([[maybe_unused]] const AZ::Matrix4x4& cameraView)
|
||||
{
|
||||
// ignore these updates if the camera is being updated internally
|
||||
if (!m_updatingTransformInternally)
|
||||
{
|
||||
if (m_storedCamera.has_value())
|
||||
{
|
||||
// if an external change occurs ensure we update the stored reference frame if one is set
|
||||
RefreshReferenceFrame();
|
||||
return;
|
||||
}
|
||||
|
||||
m_previousCamera = m_targetCamera;
|
||||
UpdateCameraFromTransform(m_targetCamera, m_modularCameraViewportContext->GetCameraTransform());
|
||||
m_camera = m_targetCamera;
|
||||
}
|
||||
@@ -231,7 +251,7 @@ namespace AtomToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
m_modularCameraViewportContext->SetCameraTransform(m_camera.Transform());
|
||||
m_modularCameraViewportContext->SetCameraTransform(m_referenceFrameOverride * m_camera.Transform());
|
||||
}
|
||||
else if (m_cameraMode == CameraMode::Animation)
|
||||
{
|
||||
@@ -240,6 +260,8 @@ namespace AtomToolsFramework
|
||||
return t * t * t * (t * (t * 6.0f - 15.0f) + 10.0f);
|
||||
};
|
||||
|
||||
m_cameraAnimation.m_time = AZ::GetClamp(m_cameraAnimation.m_time + event.m_deltaTime.count(), 0.0f, 1.0f);
|
||||
|
||||
const auto& [transformStart, transformEnd, animationTime] = m_cameraAnimation;
|
||||
|
||||
const float transitionTime = smootherStepFn(animationTime);
|
||||
@@ -253,14 +275,13 @@ namespace AtomToolsFramework
|
||||
m_camera.m_lookAt = current.GetTranslation();
|
||||
m_targetCamera = m_camera;
|
||||
|
||||
m_modularCameraViewportContext->SetCameraTransform(current);
|
||||
|
||||
if (animationTime >= 1.0f)
|
||||
{
|
||||
m_cameraMode = CameraMode::Control;
|
||||
RefreshReferenceFrame();
|
||||
}
|
||||
|
||||
m_cameraAnimation.m_time = AZ::GetClamp(animationTime + event.m_deltaTime.count(), 0.0f, 1.0f);
|
||||
|
||||
m_modularCameraViewportContext->SetCameraTransform(current);
|
||||
}
|
||||
|
||||
m_updatingTransformInternally = false;
|
||||
@@ -280,7 +301,7 @@ namespace AtomToolsFramework
|
||||
void ModularViewportCameraControllerInstance::InterpolateToTransform(const AZ::Transform& worldFromLocal, const float lookAtDistance)
|
||||
{
|
||||
m_cameraMode = CameraMode::Animation;
|
||||
m_cameraAnimation = CameraAnimation{ m_camera.Transform(), worldFromLocal, 0.0f };
|
||||
m_cameraAnimation = CameraAnimation{ m_referenceFrameOverride * m_camera.Transform(), worldFromLocal, 0.0f };
|
||||
m_lookAtAfterInterpolation = worldFromLocal.GetTranslation() + worldFromLocal.GetBasisY() * lookAtDistance;
|
||||
}
|
||||
|
||||
@@ -288,4 +309,59 @@ namespace AtomToolsFramework
|
||||
{
|
||||
return m_lookAtAfterInterpolation;
|
||||
}
|
||||
|
||||
AZ::Transform ModularViewportCameraControllerInstance::GetReferenceFrame() const
|
||||
{
|
||||
return m_referenceFrameOverride;
|
||||
}
|
||||
|
||||
void ModularViewportCameraControllerInstance::SetReferenceFrame(const AZ::Transform& worldFromLocal)
|
||||
{
|
||||
if (!m_storedCamera.has_value())
|
||||
{
|
||||
m_storedCamera = m_previousCamera;
|
||||
}
|
||||
|
||||
m_referenceFrameOverride = worldFromLocal;
|
||||
m_targetCamera.m_pitch = 0.0f;
|
||||
m_targetCamera.m_yaw = 0.0f;
|
||||
m_targetCamera.m_lookAt = AZ::Vector3::CreateZero();
|
||||
m_targetCamera.m_lookDist = 0.0f;
|
||||
m_camera = m_targetCamera;
|
||||
}
|
||||
|
||||
void ModularViewportCameraControllerInstance::ClearReferenceFrame()
|
||||
{
|
||||
m_referenceFrameOverride = AZ::Transform::CreateIdentity();
|
||||
|
||||
if (m_storedCamera.has_value())
|
||||
{
|
||||
m_targetCamera = m_storedCamera.value();
|
||||
m_camera = m_targetCamera;
|
||||
}
|
||||
|
||||
m_storedCamera.reset();
|
||||
}
|
||||
|
||||
void ModularViewportCameraControllerInstance::RefreshReferenceFrame()
|
||||
{
|
||||
m_referenceFrameOverride = m_modularCameraViewportContext->GetCameraTransform() * m_camera.Transform().GetInverse();
|
||||
}
|
||||
|
||||
AZ::Transform PlaceholderModularCameraViewportContextImpl::GetCameraTransform() const
|
||||
{
|
||||
return m_cameraTransform;
|
||||
}
|
||||
|
||||
void PlaceholderModularCameraViewportContextImpl::SetCameraTransform(const AZ::Transform& transform)
|
||||
{
|
||||
m_cameraTransform = transform;
|
||||
m_viewMatrixChangedEvent.Signal(AzFramework::CameraViewFromCameraTransform(Matrix4x4FromTransform(transform)));
|
||||
}
|
||||
|
||||
void PlaceholderModularCameraViewportContextImpl::ConnectViewMatrixChangedHandler(
|
||||
AZ::RPI::ViewportContext::MatrixChangedEvent::Handler& handler)
|
||||
{
|
||||
handler.Connect(m_viewMatrixChangedEvent);
|
||||
}
|
||||
} // namespace AtomToolsFramework
|
||||
|
||||
@@ -93,12 +93,14 @@ ly_add_target(
|
||||
AUTOMOC
|
||||
FILES_CMAKE
|
||||
materialeditor_files.cmake
|
||||
Source/Platform/${PAL_PLATFORM_NAME}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake
|
||||
${pal_source_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake
|
||||
PLATFORM_INCLUDE_FILES
|
||||
${pal_source_dir}/tool_dependencies_${PAL_PLATFORM_NAME_LOWERCASE}.cmake
|
||||
INCLUDE_DIRECTORIES
|
||||
PRIVATE
|
||||
.
|
||||
Source
|
||||
Source/Platform/${PAL_PLATFORM_NAME}
|
||||
${pal_source_dir}
|
||||
PUBLIC
|
||||
Include
|
||||
BUILD_DEPENDENCIES
|
||||
@@ -108,8 +110,14 @@ ly_add_target(
|
||||
Gem::MaterialEditor.Window
|
||||
Gem::MaterialEditor.Viewport
|
||||
Gem::MaterialEditor.Document
|
||||
RUNTIME_DEPENDENCIES
|
||||
Gem::AtomToolsFramework.Editor
|
||||
Gem::EditorPythonBindings.Editor
|
||||
Gem::ImageProcessingAtom.Editor
|
||||
)
|
||||
|
||||
ly_set_gem_variant_to_load(TARGETS MaterialEditor VARIANTS Tools)
|
||||
|
||||
# Add a 'builders' alias to allow the MaterialEditor root gem path to be added to the generated
|
||||
# cmake_dependencies.<project>.assetprocessor.setreg to allow the asset scan folder for it to be added
|
||||
ly_create_alias(NAME MaterialEditor.Builders NAMESPACE Gem)
|
||||
@@ -118,26 +126,6 @@ ly_create_alias(NAME MaterialEditor.Builders NAMESPACE Gem)
|
||||
# Editor opens up the MaterialEditor
|
||||
ly_add_dependencies(Editor Gem::MaterialEditor)
|
||||
|
||||
ly_add_target_files(
|
||||
TARGETS
|
||||
MaterialEditor
|
||||
FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/../MaterialEditor.xml
|
||||
OUTPUT_SUBDIRECTORY
|
||||
Gems/Atom/Tools/MaterialEditor
|
||||
)
|
||||
|
||||
ly_add_target_dependencies(
|
||||
TARGETS
|
||||
MaterialEditor
|
||||
DEPENDENCIES_FILES
|
||||
tool_dependencies.cmake
|
||||
Source/Platform/${PAL_PLATFORM_NAME}/tool_dependencies_${PAL_PLATFORM_NAME_LOWERCASE}.cmake
|
||||
# The Material Editor needs the LyShine "Tools" gem variant for the custom LyShine pass
|
||||
DEPENDENT_TARGETS
|
||||
Gem::LyShine.Tools
|
||||
)
|
||||
|
||||
# Inject the project path into the MaterialEditor VS debugger command arguments if the build system being invoked
|
||||
# in a project centric view
|
||||
if(NOT PROJECT_NAME STREQUAL "O3DE")
|
||||
|
||||
@@ -1,38 +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 <AzCore/std/string/string.h>
|
||||
#include <QWidget>
|
||||
#include <AzFramework/Windowing/WindowBus.h>
|
||||
|
||||
namespace Platform
|
||||
{
|
||||
void LoadPluginDependencies()
|
||||
{
|
||||
AZ_Warning("Material Editor", false, "LoadPluginDependencies() function is not implemented");
|
||||
}
|
||||
|
||||
void ProcessInput(void* message)
|
||||
{
|
||||
AZ_Warning("Material Editor", false, "ProcessInput() function is not implemented");
|
||||
}
|
||||
|
||||
AzFramework::NativeWindowHandle GetWindowHandle(WId winId)
|
||||
{
|
||||
AZ_Warning("Material Editor", false, "GetWindowHandle() function is not implemented");
|
||||
AZ_UNUSED(winId);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AzFramework::WindowSize GetClientAreaSize(AzFramework::NativeWindowHandle window)
|
||||
{
|
||||
AZ_Warning("Material Editor", false, "GetClientAreaSize() function is not implemented");
|
||||
AZ_UNUSED(window);
|
||||
return AzFramework::WindowSize{1,1};
|
||||
}
|
||||
}
|
||||
@@ -9,5 +9,4 @@
|
||||
set(FILES
|
||||
MaterialEditor_Traits_Platform.h
|
||||
MaterialEditor_Traits_Linux.h
|
||||
MaterialEditor_Linux.cpp
|
||||
)
|
||||
|
||||
+1
-1
@@ -6,5 +6,5 @@
|
||||
#
|
||||
#
|
||||
|
||||
set(GEM_DEPENDENCIES
|
||||
set(LY_RUNTIME_DEPENDENCIES
|
||||
)
|
||||
|
||||
@@ -1,38 +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 <AzCore/std/string/string.h>
|
||||
#include <QWidget>
|
||||
#include <AzFramework/Windowing/WindowBus.h>
|
||||
|
||||
namespace Platform
|
||||
{
|
||||
void LoadPluginDependencies()
|
||||
{
|
||||
AZ_Warning("Material Editor", false, "LoadPluginDependencies() function is not implemented");
|
||||
}
|
||||
|
||||
void ProcessInput(void* message)
|
||||
{
|
||||
AZ_Warning("Material Editor", false, "ProcessInput() function is not implemented");
|
||||
}
|
||||
|
||||
AzFramework::NativeWindowHandle GetWindowHandle(WId winId)
|
||||
{
|
||||
AZ_Warning("Material Editor", false, "GetWindowHandle() function is not implemented");
|
||||
AZ_UNUSED(winId);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AzFramework::WindowSize GetClientAreaSize(AzFramework::NativeWindowHandle window)
|
||||
{
|
||||
AZ_Warning("Material Editor", false, "GetClientAreaSize() function is not implemented");
|
||||
AZ_UNUSED(window);
|
||||
return AzFramework::WindowSize{1,1};
|
||||
}
|
||||
}
|
||||
@@ -9,5 +9,4 @@
|
||||
set(FILES
|
||||
MaterialEditor_Traits_Platform.h
|
||||
MaterialEditor_Traits_Mac.h
|
||||
MaterialEditor_Mac.cpp
|
||||
)
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
#
|
||||
#
|
||||
|
||||
set(GEM_DEPENDENCIES
|
||||
set(LY_RUNTIME_DEPENDENCIES
|
||||
)
|
||||
|
||||
-58
@@ -1,58 +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 <AzCore/std/string/string.h>
|
||||
#include <AzFramework/API/ApplicationAPI.h>
|
||||
#include <QDir>
|
||||
#include <QWidget>
|
||||
#include <MaterialEditor_Traits_Platform.h>
|
||||
|
||||
namespace Platform
|
||||
{
|
||||
void ProcessInput(void* message)
|
||||
{
|
||||
MSG* msg = (MSG*)message;
|
||||
|
||||
// Ensure that the Windows WM_INPUT messages get passed through to the AzFramework input system,
|
||||
// but only while in game mode so we don't accumulate raw input events before we start actually
|
||||
// ticking the input devices, otherwise the queued events will get sent when entering game mode.
|
||||
if (msg->message == WM_INPUT)
|
||||
{
|
||||
UINT rawInputSize;
|
||||
const UINT rawInputHeaderSize = sizeof(RAWINPUTHEADER);
|
||||
GetRawInputData((HRAWINPUT)msg->lParam, RID_INPUT, NULL, &rawInputSize, rawInputHeaderSize);
|
||||
|
||||
LPBYTE rawInputBytes = new BYTE[rawInputSize];
|
||||
GetRawInputData((HRAWINPUT)msg->lParam, RID_INPUT, rawInputBytes, &rawInputSize, rawInputHeaderSize);
|
||||
|
||||
RAWINPUT* rawInput = (RAWINPUT*)rawInputBytes;
|
||||
|
||||
AzFramework::RawInputNotificationBusWindows::Broadcast(
|
||||
&AzFramework::RawInputNotificationBusWindows::Events::OnRawInputEvent, *rawInput);
|
||||
}
|
||||
}
|
||||
|
||||
AzFramework::NativeWindowHandle GetWindowHandle(WId winId)
|
||||
{
|
||||
return reinterpret_cast<HWND>(winId);
|
||||
}
|
||||
|
||||
AzFramework::WindowSize GetClientAreaSize(AzFramework::NativeWindowHandle window)
|
||||
{
|
||||
RECT r;
|
||||
if (GetWindowRect(reinterpret_cast<HWND>(window), &r))
|
||||
{
|
||||
return AzFramework::WindowSize{aznumeric_cast<uint32_t>(r.right - r.left), aznumeric_cast<uint32_t>(r.bottom - r.top)};
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Assert(false, "Failed to get dimensions for window");
|
||||
return AzFramework::WindowSize{};
|
||||
}
|
||||
}
|
||||
}
|
||||
-1
@@ -9,6 +9,5 @@
|
||||
set(FILES
|
||||
MaterialEditor_Traits_Platform.h
|
||||
MaterialEditor_Traits_Windows.h
|
||||
MaterialEditor_Windows.cpp
|
||||
MaterialEditor.rc
|
||||
)
|
||||
|
||||
+1
-1
@@ -6,6 +6,6 @@
|
||||
#
|
||||
#
|
||||
|
||||
set(GEM_DEPENDENCIES
|
||||
set(LY_RUNTIME_DEPENDENCIES
|
||||
Gem::QtForPython.Editor
|
||||
)
|
||||
|
||||
@@ -231,12 +231,10 @@ namespace MaterialEditor
|
||||
MaterialViewportNotificationBus::Handler::BusConnect();
|
||||
AZ::TickBus::Handler::BusConnect();
|
||||
AZ::TransformNotificationBus::MultiHandler::BusConnect(m_cameraEntity->GetId());
|
||||
AzFramework::WindowSystemRequestBus::Handler::BusConnect();
|
||||
}
|
||||
|
||||
MaterialViewportRenderer::~MaterialViewportRenderer()
|
||||
{
|
||||
AzFramework::WindowSystemRequestBus::Handler::BusDisconnect();
|
||||
AZ::TransformNotificationBus::MultiHandler::BusDisconnect();
|
||||
AZ::TickBus::Handler::BusDisconnect();
|
||||
AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler::BusDisconnect();
|
||||
@@ -287,11 +285,6 @@ namespace MaterialEditor
|
||||
return m_viewportController;
|
||||
}
|
||||
|
||||
AzFramework::NativeWindowHandle MaterialViewportRenderer::GetDefaultWindowHandle()
|
||||
{
|
||||
return (m_windowContext) ? m_windowContext->GetWindowHandle() : nullptr;
|
||||
}
|
||||
|
||||
void MaterialViewportRenderer::OnDocumentOpened(const AZ::Uuid& documentId)
|
||||
{
|
||||
AZ::Data::Instance<AZ::RPI::Material> materialInstance;
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#include <AtomToolsFramework/Document/AtomToolsDocumentNotificationBus.h>
|
||||
#include <AzCore/Component/TickBus.h>
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
#include <AzFramework/Windowing/WindowBus.h>
|
||||
#include <Viewport/InputController/MaterialEditorViewportInputController.h>
|
||||
|
||||
namespace AZ
|
||||
@@ -46,7 +45,6 @@ namespace MaterialEditor
|
||||
, public AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler
|
||||
, public MaterialViewportNotificationBus::Handler
|
||||
, public AZ::TransformNotificationBus::MultiHandler
|
||||
, public AzFramework::WindowSystemRequestBus::Handler
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(MaterialViewportRenderer, AZ::SystemAllocator, 0);
|
||||
@@ -81,9 +79,6 @@ namespace MaterialEditor
|
||||
// AZ::TransformNotificationBus::MultiHandler overrides...
|
||||
void OnTransformChanged(const AZ::Transform&, const AZ::Transform&) override;
|
||||
|
||||
// AzFramework::WindowSystemRequestBus::Handler overrides ...
|
||||
AzFramework::NativeWindowHandle GetDefaultWindowHandle() override;
|
||||
|
||||
using DirectionalLightHandle = AZ::Render::DirectionalLightFeatureProcessorInterface::LightHandle;
|
||||
|
||||
AZ::Data::Instance<AZ::RPI::SwapChainPass> m_swapChainPass;
|
||||
|
||||
@@ -13,24 +13,16 @@
|
||||
#include <Atom/RPI.Public/ViewportContextBus.h>
|
||||
#include <Atom/RPI.Public/WindowContext.h>
|
||||
|
||||
#include <Source/Viewport/MaterialViewportRenderer.h>
|
||||
#include <Source/Viewport/MaterialViewportWidget.h>
|
||||
#include <Viewport/MaterialViewportRenderer.h>
|
||||
#include <Viewport/MaterialViewportWidget.h>
|
||||
|
||||
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
|
||||
#include <QAbstractEventDispatcher>
|
||||
#include <QWindow>
|
||||
#include "Source/Viewport/ui_MaterialViewportWidget.h"
|
||||
#include "Viewport/ui_MaterialViewportWidget.h"
|
||||
AZ_POP_DISABLE_WARNING
|
||||
|
||||
#include <AzCore/PlatformIncl.h>
|
||||
#include <AzFramework/Viewport/ViewportControllerList.h>
|
||||
|
||||
namespace Platform
|
||||
{
|
||||
void ProcessInput(void* message);
|
||||
}
|
||||
|
||||
|
||||
namespace MaterialEditor
|
||||
{
|
||||
|
||||
@@ -40,11 +32,6 @@ namespace MaterialEditor
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
|
||||
if (auto dispatcher = QAbstractEventDispatcher::instance())
|
||||
{
|
||||
dispatcher->installNativeEventFilter(this);
|
||||
}
|
||||
|
||||
// The viewport context created by AtomToolsFramework::RenderViewportWidget has no name.
|
||||
// Systems like frame capturing and post FX expect there to be a context with DefaultViewportContextName
|
||||
auto viewportContextManager = AZ::Interface<AZ::RPI::ViewportContextRequestsInterface>::Get();
|
||||
@@ -54,13 +41,4 @@ namespace MaterialEditor
|
||||
m_renderer = AZStd::make_unique<MaterialViewportRenderer>(GetViewportContext()->GetWindowContext());
|
||||
GetControllerList()->Add(m_renderer->GetController());
|
||||
}
|
||||
|
||||
// This is a temporary fix to get input working in Qt window, otherwise it wont receive input events
|
||||
// This will later be handled on the QApplication subclass level
|
||||
bool MaterialViewportWidget::nativeEventFilter(const QByteArray& /*eventType*/, void* message, long* /*result*/)
|
||||
{
|
||||
Platform::ProcessInput(message);
|
||||
|
||||
return false;
|
||||
}
|
||||
} // namespace MaterialEditor
|
||||
|
||||
@@ -8,12 +8,10 @@
|
||||
#pragma once
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <AzCore/Asset/AssetCommon.h>
|
||||
#include <AzFramework/Windowing/WindowBus.h>
|
||||
|
||||
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
|
||||
#include <QWidget>
|
||||
#include <QAbstractNativeEventFilter>
|
||||
AZ_POP_DISABLE_WARNING
|
||||
#endif
|
||||
|
||||
@@ -38,14 +36,11 @@ namespace MaterialEditor
|
||||
|
||||
class MaterialViewportWidget
|
||||
: public AtomToolsFramework::RenderViewportWidget
|
||||
, public QAbstractNativeEventFilter
|
||||
{
|
||||
public:
|
||||
MaterialViewportWidget(QWidget* parent = nullptr);
|
||||
|
||||
QScopedPointer<Ui::MaterialViewportWidget> m_ui;
|
||||
AZStd::unique_ptr<MaterialViewportRenderer> m_renderer;
|
||||
|
||||
bool nativeEventFilter(const QByteArray& eventType, void* message, long* result) override;
|
||||
};
|
||||
} // namespace MaterialEditor
|
||||
|
||||
@@ -10,5 +10,4 @@ set(FILES
|
||||
Source/main.cpp
|
||||
Source/MaterialEditorApplication.cpp
|
||||
Source/MaterialEditorApplication.h
|
||||
tool_dependencies.cmake
|
||||
)
|
||||
|
||||
@@ -1,22 +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
|
||||
#
|
||||
#
|
||||
|
||||
set(GEM_DEPENDENCIES
|
||||
Gem::Atom_RHI_Null.Private
|
||||
Gem::Atom_RHI_DX12.Private
|
||||
Gem::Atom_RHI_Vulkan.Private
|
||||
Gem::Atom_RHI.Private
|
||||
Gem::Atom_Component_DebugCamera
|
||||
Gem::Atom_RPI.Editor
|
||||
Gem::Atom_RPI.Builders
|
||||
Gem::Atom_Feature_Common.Editor
|
||||
Gem::AtomToolsFramework.Editor
|
||||
Gem::AtomLyIntegration_CommonFeatures.Editor
|
||||
Gem::EditorPythonBindings.Editor
|
||||
Gem::ImageProcessingAtom.Editor
|
||||
)
|
||||
@@ -1,69 +0,0 @@
|
||||
<ObjectStream version="3">
|
||||
<Class name="ComponentApplication::Descriptor" version="2" type="{70277A3E-2AF5-4309-9BBF-6161AFBDE792}">
|
||||
<Class name="bool" field="useExistingAllocator" value="false" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
|
||||
<Class name="bool" field="grabAllMemory" value="false" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
|
||||
<Class name="bool" field="allocationRecords" value="true" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
|
||||
<Class name="bool" field="allocationRecordsSaveNames" value="false" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
|
||||
<Class name="bool" field="allocationRecordsAttemptDecodeImmediately" value="false" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
|
||||
<Class name="int" field="recordingMode" value="2" type="{72039442-EB38-4D42-A1AD-CB68F7E0EEF6}"/>
|
||||
<Class name="AZ::u64" field="stackRecordLevels" value="5" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
|
||||
<Class name="bool" field="autoIntegrityCheck" value="false" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
|
||||
<Class name="bool" field="markUnallocatedMemory" value="true" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
|
||||
<Class name="bool" field="doNotUsePools" value="false" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
|
||||
<Class name="bool" field="enableScriptReflection" value="true" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
|
||||
<Class name="unsigned int" field="pageSize" value="65536" type="{43DA906B-7DEF-4CA8-9790-854106D3F983}"/>
|
||||
<Class name="unsigned int" field="poolPageSize" value="4096" type="{43DA906B-7DEF-4CA8-9790-854106D3F983}"/>
|
||||
<Class name="unsigned int" field="blockAlignment" value="65536" type="{43DA906B-7DEF-4CA8-9790-854106D3F983}"/>
|
||||
<Class name="AZ::u64" field="blockSize" value="0" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
|
||||
<Class name="AZ::u64" field="reservedOS" value="0" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
|
||||
<Class name="AZ::u64" field="reservedDebug" value="0" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
|
||||
<Class name="bool" field="enableDrilling" value="true" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
|
||||
<Class name="AZStd::vector" field="modules" type="{8E779F80-AEAA-565B-ABB1-DE10B18CF995}">
|
||||
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
|
||||
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.Atom_RHI_DX12.Private.e011969cf32442fdaac2443a960ab5ff.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
|
||||
</Class>
|
||||
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
|
||||
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.Atom_RHI_Vulkan.Private.150d40d376124d98a388dfe890551c03.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
|
||||
</Class>
|
||||
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
|
||||
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.Atom_RHI.Private.fb7f322c8bdb42228d9e155c954f98bd.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
|
||||
</Class>
|
||||
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
|
||||
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.Atom_RHI_DX12.Private.e011969cf32442fdaac2443a960ab5ff.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
|
||||
</Class>
|
||||
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
|
||||
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.Atom_RHI.Private.fb7f322c8bdb42228d9e155c954f98bd.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
|
||||
</Class>
|
||||
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
|
||||
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.Atom_RPI.Private.a218db9eb2114477b46600fea4441a6c.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
|
||||
</Class>
|
||||
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
|
||||
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.Atom_Component_DebugCamera.013d1b42ad314c929b292c143bcbf045.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
|
||||
</Class>
|
||||
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
|
||||
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.Atom_RPI.Builders.a218db9eb2114477b46600fea4441a6c.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
|
||||
</Class>
|
||||
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
|
||||
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.Atom_Feature_Common.Editor.b58e5eed0901428ca78544b04dbd61bd.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
|
||||
</Class>
|
||||
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
|
||||
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.AtomLyIntegration_CommonFeatures.Editor.4e981f3b17394f5d84d674fff0f54f4f.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
|
||||
</Class>
|
||||
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
|
||||
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.EditorPythonBindings.Editor.b658359393884c4381c2fe2952b1472a.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
|
||||
</Class>
|
||||
<Class name="DynamicModuleDescriptor" field="element" type="{D2932FA3-9942-4FD2-A703-2E750F57C003}">
|
||||
<Class name="AZStd::string" field="dynamicLibraryPath" value="Gem.ImageProcessingAtom.Editor.9d10b00be96045caa64c705e5772cb64.v0.1.0" type="{189CC2ED-FDDE-5680-91D4-9F630A79187F}"/>
|
||||
</Class>
|
||||
</Class>
|
||||
</Class>
|
||||
<Class name="AZ::Entity" version="2" type="{75651658-8663-478D-9090-2432DFCAFA44}">
|
||||
<Class name="EntityId" field="Id" version="1" type="{6383F1D3-BB27-4E6B-A49A-6409B2059EAA}">
|
||||
<Class name="AZ::u64" field="id" value="0" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
|
||||
</Class>
|
||||
<Class name="AZStd::string" field="Name" value="SystemEntity" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
|
||||
<Class name="AZStd::vector" field="Components" type="{0D23B755-6E8F-5C6C-B7C9-A352A55DC1DF}"/>
|
||||
<Class name="bool" field="IsDependencyReady" value="false" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
|
||||
<Class name="bool" field="IsRuntimeActive" value="true" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
|
||||
</Class>
|
||||
</ObjectStream>
|
||||
@@ -43,6 +43,7 @@ ly_add_target(
|
||||
NAMESPACE Gem
|
||||
AUTOMOC
|
||||
AUTOUIC
|
||||
AUTORCC
|
||||
FILES_CMAKE
|
||||
shadermanagementconsolewindow_files.cmake
|
||||
INCLUDE_DIRECTORIES
|
||||
@@ -64,6 +65,8 @@ ly_add_target(
|
||||
FILES_CMAKE
|
||||
shadermanagementconsole_files.cmake
|
||||
${pal_source_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake
|
||||
PLATFORM_INCLUDE_FILES
|
||||
${pal_source_dir}/tool_dependencies_${PAL_PLATFORM_NAME_LOWERCASE}.cmake
|
||||
INCLUDE_DIRECTORIES
|
||||
PRIVATE
|
||||
.
|
||||
@@ -78,27 +81,16 @@ ly_add_target(
|
||||
Gem::ShaderManagementConsole.Window
|
||||
Gem::ShaderManagementConsole.Document
|
||||
RUNTIME_DEPENDENCIES
|
||||
Gem::Atom_RHI_DX12.Private
|
||||
Gem::Atom_RHI_Vulkan.Private
|
||||
Gem::Atom_RHI.Private
|
||||
Gem::Atom_RPI.Private
|
||||
Gem::Atom_RPI.Builders
|
||||
Gem::Atom_Feature_Common.Editor
|
||||
Gem::AtomToolsFramework.Editor
|
||||
Gem::EditorPythonBindings.Editor
|
||||
)
|
||||
|
||||
ly_set_gem_variant_to_load(TARGETS ShaderManagementConsole VARIANTS Tools)
|
||||
|
||||
# Add build dependency to Editor for the ShaderManagementConsole application since
|
||||
# Editor opens up the ShaderManagementConsole
|
||||
ly_add_dependencies(Editor Gem::ShaderManagementConsole)
|
||||
|
||||
ly_add_target_dependencies(
|
||||
TARGETS
|
||||
ShaderManagementConsole
|
||||
DEPENDENCIES_FILES
|
||||
tool_dependencies.cmake
|
||||
Source/Platform/${PAL_PLATFORM_NAME}/tool_dependencies_${PAL_PLATFORM_NAME_LOWERCASE}.cmake
|
||||
)
|
||||
|
||||
# Inject the project path into the ShaderManagementConsole VS debugger command arguments if the build system being invoked
|
||||
# in a project centric view
|
||||
if(NOT PROJECT_NAME STREQUAL "O3DE")
|
||||
@@ -108,9 +100,14 @@ endif()
|
||||
# Adds the ShaderManagementConsole target as a C preprocessor define so that it can be used as a Settings Registry
|
||||
# specialization in order to look up the generated .setreg which contains the dependencies
|
||||
# specified for the target.
|
||||
set_source_files_properties(
|
||||
Source/ShaderManagementConsoleApplication.cpp
|
||||
PROPERTIES
|
||||
COMPILE_DEFINITIONS
|
||||
LY_CMAKE_TARGET="ShaderManagementConsole"
|
||||
)
|
||||
if(TARGET ShaderManagementConsole)
|
||||
set_source_files_properties(
|
||||
Source/ShaderManagementConsoleApplication.cpp
|
||||
PROPERTIES
|
||||
COMPILE_DEFINITIONS
|
||||
LY_CMAKE_TARGET="ShaderManagementConsole"
|
||||
)
|
||||
else()
|
||||
message(FATAL_ERROR "Cannot set LY_CMAKE_TARGET define to ShaderManagementConsole as the target doesn't exist anymore."
|
||||
" Perhaps it has been renamed")
|
||||
endif()
|
||||
|
||||
@@ -6,4 +6,4 @@
|
||||
#
|
||||
#
|
||||
|
||||
set(PAL_TRAIT_ATOM_SHADER_MANAGEMENT_CONSOLE_APPLICATION_SUPPORTED FALSE)
|
||||
set(PAL_TRAIT_ATOM_SHADER_MANAGEMENT_CONSOLE_APPLICATION_SUPPORTED TRUE)
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#define AZ_TRAIT_SHADER_MANAGEMENT_CONSOLE_EXT ""
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* 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 <ShaderManagementConsole_Traits_Linux.h>
|
||||
+2
@@ -7,4 +7,6 @@
|
||||
#
|
||||
|
||||
set(FILES
|
||||
ShaderManagementConsole_Traits_Platform.h
|
||||
ShaderManagementConsole_Traits_Linux.h
|
||||
)
|
||||
|
||||
+1
-1
@@ -6,5 +6,5 @@
|
||||
#
|
||||
#
|
||||
|
||||
set(GEM_DEPENDENCIES
|
||||
set(LY_RUNTIME_DEPENDENCIES
|
||||
)
|
||||
|
||||
-33
@@ -1,33 +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 <AzCore/std/string/string.h>
|
||||
#include <QWidget>
|
||||
#include <AzFramework/Windowing/WindowBus.h>
|
||||
|
||||
namespace Platform
|
||||
{
|
||||
void ProcessInput(void* message)
|
||||
{
|
||||
AZ_Warning("Shader Management Console", false, "ProcessInput() function is not implemented");
|
||||
}
|
||||
|
||||
AzFramework::NativeWindowHandle GetWindowHandle(WId winId)
|
||||
{
|
||||
AZ_Warning("Shader Management Console", false, "GetWindowHandle() function is not implemented");
|
||||
AZ_UNUSED(winId);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AzFramework::WindowSize GetClientAreaSize(AzFramework::NativeWindowHandle window)
|
||||
{
|
||||
AZ_Warning("Shader Management Console", false, "GetClientAreaSize() function is not implemented");
|
||||
AZ_UNUSED(window);
|
||||
return AzFramework::WindowSize{1,1};
|
||||
}
|
||||
}
|
||||
-1
@@ -9,5 +9,4 @@
|
||||
set(FILES
|
||||
ShaderManagementConsole_Traits_Platform.h
|
||||
ShaderManagementConsole_Traits_Mac.h
|
||||
ShaderManagementConsole_Mac.cpp
|
||||
)
|
||||
|
||||
+1
-1
@@ -6,5 +6,5 @@
|
||||
#
|
||||
#
|
||||
|
||||
set(GEM_DEPENDENCIES
|
||||
set(LY_RUNTIME_DEPENDENCIES
|
||||
)
|
||||
|
||||
-56
@@ -1,56 +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 <AzCore/std/string/string.h>
|
||||
#include <QWidget>
|
||||
#include <ShaderManagementConsole_Traits_Platform.h>
|
||||
|
||||
namespace Platform
|
||||
{
|
||||
void ProcessInput(void* message)
|
||||
{
|
||||
MSG* msg = (MSG*)message;
|
||||
|
||||
// Ensure that the Windows WM_INPUT messages get passed through to the AzFramework input system,
|
||||
// but only while in game mode so we don't accumulate raw input events before we start actually
|
||||
// ticking the input devices, otherwise the queued events will get sent when entering game mode.
|
||||
if (msg->message == WM_INPUT)
|
||||
{
|
||||
UINT rawInputSize;
|
||||
const UINT rawInputHeaderSize = sizeof(RAWINPUTHEADER);
|
||||
GetRawInputData((HRAWINPUT)msg->lParam, RID_INPUT, NULL, &rawInputSize, rawInputHeaderSize);
|
||||
|
||||
LPBYTE rawInputBytes = new BYTE[rawInputSize];
|
||||
GetRawInputData((HRAWINPUT)msg->lParam, RID_INPUT, rawInputBytes, &rawInputSize, rawInputHeaderSize);
|
||||
|
||||
RAWINPUT* rawInput = (RAWINPUT*)rawInputBytes;
|
||||
|
||||
AzFramework::RawInputNotificationBusWindows::Broadcast(
|
||||
&AzFramework::RawInputNotificationBusWindows::Events::OnRawInputEvent, *rawInput);
|
||||
}
|
||||
}
|
||||
|
||||
AzFramework::NativeWindowHandle GetWindowHandle(WId winId)
|
||||
{
|
||||
return reinterpret_cast<HWND>(winId);
|
||||
}
|
||||
|
||||
AzFramework::WindowSize GetClientAreaSize(AzFramework::NativeWindowHandle window)
|
||||
{
|
||||
RECT r;
|
||||
if (GetWindowRect(reinterpret_cast<HWND>(window), &r))
|
||||
{
|
||||
return AzFramework::WindowSize{aznumeric_cast<uint32_t>(r.right - r.left), aznumeric_cast<uint32_t>(r.bottom - r.top)};
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Assert(false, "Failed to get dimensions for window");
|
||||
return AzFramework::WindowSize{};
|
||||
}
|
||||
}
|
||||
}
|
||||
-1
@@ -9,6 +9,5 @@
|
||||
set(FILES
|
||||
ShaderManagementConsole_Traits_Platform.h
|
||||
ShaderManagementConsole_Traits_Windows.h
|
||||
ShaderManagementConsole_Windows.cpp
|
||||
ShaderManagementConsole.rc
|
||||
)
|
||||
|
||||
+1
-1
@@ -6,6 +6,6 @@
|
||||
#
|
||||
#
|
||||
|
||||
set(GEM_DEPENDENCIES
|
||||
set(LY_RUNTIME_DEPENDENCIES
|
||||
Gem::QtForPython.Editor
|
||||
)
|
||||
|
||||
-10
@@ -1,10 +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
|
||||
#
|
||||
#
|
||||
|
||||
set(GEM_DEPENDENCIES
|
||||
)
|
||||
@@ -1,22 +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
|
||||
#
|
||||
#
|
||||
|
||||
set(GEM_DEPENDENCIES
|
||||
Gem::Atom_RHI_Null.Private
|
||||
Gem::Atom_RHI_DX12.Private
|
||||
Gem::Atom_RHI_Vulkan.Private
|
||||
Gem::Atom_RHI.Private
|
||||
Gem::Atom_Component_DebugCamera
|
||||
Gem::Atom_RPI.Editor
|
||||
Gem::Atom_RPI.Builders
|
||||
Gem::Atom_Feature_Common.Editor
|
||||
Gem::AtomToolsFramework.Editor
|
||||
Gem::AtomLyIntegration_CommonFeatures.Editor
|
||||
Gem::EditorPythonBindings.Editor
|
||||
Gem::ImageProcessingAtom.Editor
|
||||
)
|
||||
+6
-1
@@ -82,6 +82,8 @@ namespace AZ::Render
|
||||
|
||||
void AtomViewportDisplayIconsSystemComponent::Activate()
|
||||
{
|
||||
m_drawContextRegistered = false;
|
||||
|
||||
AzToolsFramework::EditorViewportIconDisplay::Register(this);
|
||||
|
||||
Bootstrap::NotificationBus::Handler::BusConnect();
|
||||
@@ -97,9 +99,10 @@ namespace AZ::Render
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (perViewportDynamicDrawInterface)
|
||||
if (perViewportDynamicDrawInterface && m_drawContextRegistered)
|
||||
{
|
||||
perViewportDynamicDrawInterface->UnregisterDynamicDrawContext(m_drawContextName);
|
||||
m_drawContextRegistered = false;
|
||||
}
|
||||
|
||||
AzToolsFramework::EditorViewportIconDisplay::Unregister(this);
|
||||
@@ -367,6 +370,8 @@ namespace AZ::Render
|
||||
drawContext->EndInit();
|
||||
});
|
||||
|
||||
m_drawContextRegistered = true;
|
||||
|
||||
Data::AssetBus::Handler::BusDisconnect();
|
||||
}
|
||||
} // namespace AZ::Render
|
||||
|
||||
+2
@@ -77,6 +77,8 @@ namespace AZ
|
||||
};
|
||||
AZStd::unordered_map<IconId, IconData> m_iconData;
|
||||
IconId m_currentId = 0;
|
||||
|
||||
bool m_drawContextRegistered = false;
|
||||
};
|
||||
} // namespace Render
|
||||
} // namespace AZ
|
||||
|
||||
+4
-3
@@ -212,6 +212,9 @@ namespace AZ
|
||||
|
||||
AZ::Vector3 position = AZ::Vector3::CreateZero();
|
||||
AZ::TransformBus::EventResult(position, GetEntityId(), &AZ::TransformBus::Events::GetWorldTranslation);
|
||||
AZ::Quaternion rotationQuaternion = AZ::Quaternion::CreateIdentity();
|
||||
AZ::TransformBus::EventResult(rotationQuaternion, GetEntityId(), &AZ::TransformBus::Events::GetWorldRotationQuaternion);
|
||||
AZ::Matrix3x3 rotationMatrix = AZ::Matrix3x3::CreateFromQuaternion(rotationQuaternion);
|
||||
|
||||
float scale = 1.0f;
|
||||
AZ::TransformBus::EventResult(scale, GetEntityId(), &AZ::TransformBus::Events::GetLocalUniformScale);
|
||||
@@ -224,9 +227,7 @@ namespace AZ
|
||||
AZ::Vector3 innerExtents(configuration.m_innerWidth, configuration.m_innerLength, configuration.m_innerHeight);
|
||||
innerExtents *= scale;
|
||||
|
||||
AZ::Vector3 innerMin(position.GetX() - innerExtents.GetX() / 2, position.GetY() - innerExtents.GetY() / 2, position.GetZ() - innerExtents.GetZ() / 2);
|
||||
AZ::Vector3 innerMax(position.GetX() + innerExtents.GetX() / 2, position.GetY() + innerExtents.GetY() / 2, position.GetZ() + innerExtents.GetZ() / 2);
|
||||
debugDisplay.DrawWireBox(innerMin, innerMax);
|
||||
debugDisplay.DrawWireOBB(position, rotationMatrix.GetBasisX(), rotationMatrix.GetBasisY(), rotationMatrix.GetBasisZ(), innerExtents / 2.0f);
|
||||
}
|
||||
|
||||
AZ::Aabb EditorReflectionProbeComponent::GetEditorSelectionBoundsViewport([[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo)
|
||||
|
||||
@@ -57,6 +57,7 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS)
|
||||
PRIVATE
|
||||
AZ::AzToolsFramework
|
||||
Gem::Camera.Static
|
||||
Gem::AtomToolsFramework.Static
|
||||
RUNTIME_DEPENDENCIES
|
||||
Legacy::EditorCommon
|
||||
)
|
||||
|
||||
Binary file not shown.
@@ -372,6 +372,8 @@ namespace EMotionFX
|
||||
// updates the skinning matrices of all nodes
|
||||
void ActorInstance::UpdateSkinningMatrices()
|
||||
{
|
||||
AZ_PROFILE_SCOPE(Animation, "ActorInstance::UpdateSkinningMatrices");
|
||||
|
||||
AZ::Matrix3x4* skinningMatrices = m_transformData->GetSkinningMatrices();
|
||||
const Pose* pose = m_transformData->GetCurrentPose();
|
||||
|
||||
@@ -596,6 +598,8 @@ namespace EMotionFX
|
||||
// update the bounding volume
|
||||
void ActorInstance::UpdateBounds(size_t geomLODLevel, EBoundsType boundsType, uint32 itemFrequency)
|
||||
{
|
||||
AZ_PROFILE_SCOPE(Animation, "ActorInstance::UpdateBounds");
|
||||
|
||||
// depending on the bounding volume update type
|
||||
switch (boundsType)
|
||||
{
|
||||
|
||||
@@ -218,6 +218,8 @@ namespace EMotionFX
|
||||
// output the results into the internal pose object
|
||||
void AnimGraphInstance::Output(Pose* outputPose)
|
||||
{
|
||||
AZ_PROFILE_SCOPE(Animation, "AnimGraphInstance::Output");
|
||||
|
||||
// reset max used
|
||||
const uint32 threadIndex = m_actorInstance->GetThreadIndex();
|
||||
AnimGraphPosePool& posePool = GetEMotionFX().GetThreadData(threadIndex)->GetPosePool();
|
||||
@@ -854,6 +856,8 @@ namespace EMotionFX
|
||||
// synchronize all nodes, based on sync tracks etc
|
||||
void AnimGraphInstance::Update(float timePassedInSeconds)
|
||||
{
|
||||
AZ_PROFILE_SCOPE(Animation, "AnimGraphInstance::Update");
|
||||
|
||||
// pass 0: (Optional, networking only) When this instance is shared between network, restore the instance using an animgraph snapshot.
|
||||
if (m_snapshot)
|
||||
{
|
||||
@@ -940,6 +944,8 @@ namespace EMotionFX
|
||||
// reset all node pose ref counts
|
||||
void AnimGraphInstance::ResetPoseRefCountsForAllNodes()
|
||||
{
|
||||
AZ_PROFILE_SCOPE(Animation, "AnimGraphInstance::ResetPoseRefCountsForAllNodes");
|
||||
|
||||
const size_t numNodes = m_animGraph->GetNumNodes();
|
||||
for (size_t i = 0; i < numNodes; ++i)
|
||||
{
|
||||
@@ -951,6 +957,8 @@ namespace EMotionFX
|
||||
// reset all node pose ref counts
|
||||
void AnimGraphInstance::ResetRefDataRefCountsForAllNodes()
|
||||
{
|
||||
AZ_PROFILE_SCOPE(Animation, "AnimGraphInstance::ResetRefDataRefCountsForAllNodes");
|
||||
|
||||
const size_t numNodes = m_animGraph->GetNumNodes();
|
||||
for (size_t i = 0; i < numNodes; ++i)
|
||||
{
|
||||
@@ -962,6 +970,8 @@ namespace EMotionFX
|
||||
// reset all node flags
|
||||
void AnimGraphInstance::ResetFlagsForAllObjects()
|
||||
{
|
||||
AZ_PROFILE_SCOPE(Animation, "AnimGraphInstance::ResetFlagsForAllObjects");
|
||||
|
||||
MCore::MemSet(m_objectFlags.data(), 0, sizeof(uint32) * m_objectFlags.size());
|
||||
|
||||
for (AnimGraphInstance* childInstance : m_childAnimGraphInstances)
|
||||
|
||||
@@ -394,6 +394,8 @@ namespace EMotionFX
|
||||
// the main process method of the final node
|
||||
void AnimGraphMotionNode::Output(AnimGraphInstance* animGraphInstance)
|
||||
{
|
||||
AZ_PROFILE_SCOPE(Animation, "AnimGraphMotionNode::Output");
|
||||
|
||||
// if this motion is disabled, output the bind pose
|
||||
if (m_disabled)
|
||||
{
|
||||
@@ -537,6 +539,8 @@ namespace EMotionFX
|
||||
|
||||
void AnimGraphMotionNode::UniqueData::Update()
|
||||
{
|
||||
AZ_PROFILE_SCOPE(Animation, "AnimGraphMotionNode::Update");
|
||||
|
||||
AnimGraphMotionNode* motionNode = azdynamic_cast<AnimGraphMotionNode*>(m_object);
|
||||
AZ_Assert(motionNode, "Unique data linked to incorrect node type.");
|
||||
|
||||
|
||||
@@ -92,6 +92,8 @@ namespace EMotionFX
|
||||
|
||||
void AnimGraphStateMachine::Output(AnimGraphInstance* animGraphInstance)
|
||||
{
|
||||
AZ_PROFILE_SCOPE(Animation, "AnimGraphStateMachine::Update");
|
||||
|
||||
ActorInstance* actorInstance = animGraphInstance->GetActorInstance();
|
||||
AnimGraphPose* outputPose = nullptr;
|
||||
|
||||
@@ -476,6 +478,8 @@ namespace EMotionFX
|
||||
|
||||
void AnimGraphStateMachine::Update(AnimGraphInstance* animGraphInstance, float timePassedInSeconds)
|
||||
{
|
||||
AZ_PROFILE_SCOPE(Animation, "AnimGraphStateMachine::Update");
|
||||
|
||||
UniqueData* uniqueData = static_cast<UniqueData*>(FindOrCreateUniqueNodeData(animGraphInstance));
|
||||
|
||||
// Defer switch to entry state.
|
||||
@@ -622,6 +626,8 @@ namespace EMotionFX
|
||||
|
||||
void AnimGraphStateMachine::PostUpdate(AnimGraphInstance* animGraphInstance, float timePassedInSeconds)
|
||||
{
|
||||
AZ_PROFILE_SCOPE(Animation, "AnimGraphStateMachine::PostUpdate");
|
||||
|
||||
RequestRefDatas(animGraphInstance);
|
||||
UniqueData* uniqueData = static_cast<UniqueData*>(FindOrCreateUniqueNodeData(animGraphInstance));
|
||||
AnimGraphRefCountedData* data = uniqueData->GetRefCountedData();
|
||||
@@ -1344,6 +1350,8 @@ namespace EMotionFX
|
||||
|
||||
void AnimGraphStateMachine::TopDownUpdate(AnimGraphInstance* animGraphInstance, float timePassedInSeconds)
|
||||
{
|
||||
AZ_PROFILE_SCOPE(Animation, "AnimGraphStateMachine::TopDownUpdate");
|
||||
|
||||
UniqueData* uniqueData = static_cast<UniqueData*>(FindOrCreateUniqueNodeData(animGraphInstance));
|
||||
|
||||
if (!IsTransitioning(uniqueData))
|
||||
|
||||
@@ -166,6 +166,8 @@ namespace EMotionFX
|
||||
|
||||
void BlendSpace1DNode::Output(AnimGraphInstance* animGraphInstance)
|
||||
{
|
||||
AZ_PROFILE_SCOPE(Animation, "BlendSpace1DNode::Output");
|
||||
|
||||
if (!AnimGraphInstanceExists(animGraphInstance))
|
||||
{
|
||||
return;
|
||||
@@ -276,6 +278,8 @@ namespace EMotionFX
|
||||
|
||||
void BlendSpace1DNode::Update(AnimGraphInstance* animGraphInstance, float timePassedInSeconds)
|
||||
{
|
||||
AZ_PROFILE_SCOPE(Animation, "BlendSpace1DNode::Update");
|
||||
|
||||
if (!m_disabled)
|
||||
{
|
||||
EMotionFX::BlendTreeConnection* paramConnection = GetInputPort(INPUTPORT_VALUE).m_connection;
|
||||
|
||||
@@ -282,6 +282,8 @@ namespace EMotionFX
|
||||
|
||||
void BlendSpace2DNode::Output(AnimGraphInstance* animGraphInstance)
|
||||
{
|
||||
AZ_PROFILE_SCOPE(Animation, "BlendSpace2DNode::Output");
|
||||
|
||||
if (!AnimGraphInstanceExists(animGraphInstance))
|
||||
{
|
||||
return;
|
||||
@@ -402,6 +404,8 @@ namespace EMotionFX
|
||||
|
||||
void BlendSpace2DNode::Update(AnimGraphInstance* animGraphInstance, float timePassedInSeconds)
|
||||
{
|
||||
AZ_PROFILE_SCOPE(Animation, "BlendSpace2DNode::Update");
|
||||
|
||||
if (!AnimGraphInstanceExists(animGraphInstance))
|
||||
{
|
||||
return;
|
||||
|
||||
@@ -116,10 +116,11 @@ namespace EMotionFX
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
// process the blend tree and calculate its output
|
||||
void BlendTree::Output(AnimGraphInstance* animGraphInstance)
|
||||
{
|
||||
AZ_PROFILE_SCOPE(Animation, "BlendTree::Output");
|
||||
|
||||
AZ_Assert(m_finalNode, "There should always be a final node. Something seems to be wrong with the blend tree creation.");
|
||||
|
||||
// get the output pose
|
||||
@@ -164,6 +165,8 @@ namespace EMotionFX
|
||||
// post sync update
|
||||
void BlendTree::PostUpdate(AnimGraphInstance* animGraphInstance, float timePassedInSeconds)
|
||||
{
|
||||
AZ_PROFILE_SCOPE(Animation, "AnimGraphStateMachine::PostUpdate");
|
||||
|
||||
// if this node is disabled, exit
|
||||
if (m_disabled)
|
||||
{
|
||||
@@ -212,6 +215,8 @@ namespace EMotionFX
|
||||
// update all nodes
|
||||
void BlendTree::Update(AnimGraphInstance* animGraphInstance, float timePassedInSeconds)
|
||||
{
|
||||
AZ_PROFILE_SCOPE(Animation, "BlendTree::Update");
|
||||
|
||||
// if this node is disabled, output the bind pose
|
||||
if (m_disabled)
|
||||
{
|
||||
@@ -256,6 +261,8 @@ namespace EMotionFX
|
||||
// top down update
|
||||
void BlendTree::TopDownUpdate(AnimGraphInstance* animGraphInstance, float timePassedInSeconds)
|
||||
{
|
||||
AZ_PROFILE_SCOPE(Animation, "BlendTree::TopDownUpdate");
|
||||
|
||||
// get the final node
|
||||
AnimGraphNode* finalNode = GetRealFinalNode();
|
||||
|
||||
|
||||
@@ -45,6 +45,8 @@ namespace EMotionFX
|
||||
|
||||
void BlendTreeBlend2Node::Update(AnimGraphInstance* animGraphInstance, float timePassedInSeconds)
|
||||
{
|
||||
AZ_PROFILE_SCOPE(Animation, "BlendTreeBlend2Node::Update");
|
||||
|
||||
if (m_disabled)
|
||||
{
|
||||
AnimGraphNodeData* uniqueData = FindOrCreateUniqueNodeData(animGraphInstance);
|
||||
@@ -88,9 +90,10 @@ namespace EMotionFX
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void BlendTreeBlend2Node::Output(AnimGraphInstance* animGraphInstance)
|
||||
{
|
||||
AZ_PROFILE_SCOPE(Animation, "BlendTreeBlend2Node::Output");
|
||||
|
||||
if (m_disabled)
|
||||
{
|
||||
RequestPoses(animGraphInstance);
|
||||
|
||||
@@ -147,6 +147,8 @@ namespace EMotionFX
|
||||
// update motion queue and instances
|
||||
void MotionSystem::Update(float timePassed, bool updateNodes)
|
||||
{
|
||||
AZ_PROFILE_SCOPE(Animation, "MotionSystem::Update");
|
||||
|
||||
MCORE_UNUSED(updateNodes);
|
||||
|
||||
// update the motion queue
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace EMotionFX
|
||||
|
||||
void ClothJointWidget::InternalReinit()
|
||||
{
|
||||
if (m_selectedModelIndices.size() == 1)
|
||||
if (GetSelectedModelIndices().size() == 1)
|
||||
{
|
||||
Physics::CharacterColliderNodeConfiguration* nodeConfig = GetNodeConfig();
|
||||
if (nodeConfig)
|
||||
@@ -94,17 +94,17 @@ namespace EMotionFX
|
||||
|
||||
void ClothJointWidget::OnAddCollider(const AZ::TypeId& colliderType)
|
||||
{
|
||||
ColliderHelpers::AddCollider(m_selectedModelIndices , PhysicsSetup::Cloth, colliderType);
|
||||
ColliderHelpers::AddCollider(GetSelectedModelIndices(), PhysicsSetup::Cloth, colliderType);
|
||||
}
|
||||
|
||||
void ClothJointWidget::OnCopyCollider(size_t colliderIndex)
|
||||
{
|
||||
ColliderHelpers::CopyColliderToClipboard(m_selectedModelIndices.first(), colliderIndex, PhysicsSetup::Cloth);
|
||||
ColliderHelpers::CopyColliderToClipboard(GetSelectedModelIndices().first(), colliderIndex, PhysicsSetup::Cloth);
|
||||
}
|
||||
|
||||
void ClothJointWidget::OnPasteCollider(size_t colliderIndex, bool replace)
|
||||
{
|
||||
ColliderHelpers::PasteColliderFromClipboard(m_selectedModelIndices.first(), colliderIndex, PhysicsSetup::Cloth, replace);
|
||||
ColliderHelpers::PasteColliderFromClipboard(GetSelectedModelIndices().first(), colliderIndex, PhysicsSetup::Cloth, replace);
|
||||
}
|
||||
|
||||
void ClothJointWidget::OnRemoveCollider(size_t colliderIndex)
|
||||
@@ -114,7 +114,7 @@ namespace EMotionFX
|
||||
|
||||
Physics::CharacterColliderNodeConfiguration* ClothJointWidget::GetNodeConfig() const
|
||||
{
|
||||
AZ_Assert(m_selectedModelIndices.size() == 1, "Get Node config function only return the config when it is single seleted");
|
||||
AZ_Assert(GetSelectedModelIndices().size() == 1, "Get Node config function only return the config when it is single seleted");
|
||||
Actor* actor = GetActor();
|
||||
Node* joint = GetNode();
|
||||
if (!actor || !joint)
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace EMotionFX
|
||||
|
||||
void HitDetectionJointWidget::InternalReinit()
|
||||
{
|
||||
if (m_selectedModelIndices.size() == 1)
|
||||
if (GetSelectedModelIndices().size() == 1)
|
||||
{
|
||||
Physics::CharacterColliderNodeConfiguration* hitDetectionNodeConfig = GetNodeConfig();
|
||||
if (hitDetectionNodeConfig)
|
||||
@@ -90,17 +90,17 @@ namespace EMotionFX
|
||||
|
||||
void HitDetectionJointWidget::OnAddCollider(const AZ::TypeId& colliderType)
|
||||
{
|
||||
ColliderHelpers::AddCollider(m_selectedModelIndices, PhysicsSetup::HitDetection, colliderType);
|
||||
ColliderHelpers::AddCollider(GetSelectedModelIndices(), PhysicsSetup::HitDetection, colliderType);
|
||||
}
|
||||
|
||||
void HitDetectionJointWidget::OnCopyCollider(size_t colliderIndex)
|
||||
{
|
||||
ColliderHelpers::CopyColliderToClipboard(m_selectedModelIndices.first(), colliderIndex, PhysicsSetup::HitDetection);
|
||||
ColliderHelpers::CopyColliderToClipboard(GetSelectedModelIndices().first(), colliderIndex, PhysicsSetup::HitDetection);
|
||||
}
|
||||
|
||||
void HitDetectionJointWidget::OnPasteCollider(size_t colliderIndex, bool replace)
|
||||
{
|
||||
ColliderHelpers::PasteColliderFromClipboard(m_selectedModelIndices.first(), colliderIndex, PhysicsSetup::HitDetection, replace);
|
||||
ColliderHelpers::PasteColliderFromClipboard(GetSelectedModelIndices().first(), colliderIndex, PhysicsSetup::HitDetection, replace);
|
||||
}
|
||||
|
||||
void HitDetectionJointWidget::OnRemoveCollider(size_t colliderIndex)
|
||||
@@ -110,7 +110,7 @@ namespace EMotionFX
|
||||
|
||||
Physics::CharacterColliderNodeConfiguration* HitDetectionJointWidget::GetNodeConfig()
|
||||
{
|
||||
AZ_Assert(m_selectedModelIndices.size() == 1, "Get Node config function only return the config when it is single seleted");
|
||||
AZ_Assert(GetSelectedModelIndices().size() == 1, "Get Node config function only return the config when it is single seleted");
|
||||
Actor* actor = GetActor();
|
||||
Node* node = GetNode();
|
||||
if (!actor || !node)
|
||||
|
||||
@@ -116,7 +116,8 @@ namespace EMotionFX
|
||||
|
||||
void RagdollNodeWidget::InternalReinit()
|
||||
{
|
||||
if (m_selectedModelIndices.size() == 1)
|
||||
const QModelIndexList& selectedModelIndices = GetSelectedModelIndices();
|
||||
if (selectedModelIndices.size() == 1)
|
||||
{
|
||||
m_ragdollNodeEditor->ClearInstances(false);
|
||||
|
||||
@@ -142,7 +143,7 @@ namespace EMotionFX
|
||||
m_collidersWidget->Reset();
|
||||
}
|
||||
|
||||
m_jointLimitWidget->Update(m_selectedModelIndices[0]);
|
||||
m_jointLimitWidget->Update(selectedModelIndices[0]);
|
||||
m_ragdollNodeCard->setExpanded(true);
|
||||
m_ragdollNodeCard->show();
|
||||
m_jointLimitWidget->show();
|
||||
@@ -169,31 +170,32 @@ namespace EMotionFX
|
||||
|
||||
void RagdollNodeWidget::OnAddRemoveRagdollNode()
|
||||
{
|
||||
const QModelIndexList& selectedModelIndices = GetSelectedModelIndices();
|
||||
if (GetRagdollNodeConfig())
|
||||
{
|
||||
// The node is present in the ragdoll, remove it.
|
||||
RagdollNodeInspectorPlugin::RemoveFromRagdoll(m_selectedModelIndices);
|
||||
RagdollNodeInspectorPlugin::RemoveFromRagdoll(selectedModelIndices);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The node is not part of the ragdoll, add it.
|
||||
RagdollNodeInspectorPlugin::AddToRagdoll(m_selectedModelIndices);
|
||||
RagdollNodeInspectorPlugin::AddToRagdoll(selectedModelIndices);
|
||||
}
|
||||
}
|
||||
|
||||
void RagdollNodeWidget::OnAddCollider(const AZ::TypeId& colliderType)
|
||||
{
|
||||
ColliderHelpers::AddCollider(m_selectedModelIndices, PhysicsSetup::Ragdoll, colliderType);
|
||||
ColliderHelpers::AddCollider(GetSelectedModelIndices(), PhysicsSetup::Ragdoll, colliderType);
|
||||
}
|
||||
|
||||
void RagdollNodeWidget::OnCopyCollider(size_t colliderIndex)
|
||||
{
|
||||
ColliderHelpers::CopyColliderToClipboard(m_selectedModelIndices.first(), colliderIndex, PhysicsSetup::Ragdoll);
|
||||
ColliderHelpers::CopyColliderToClipboard(GetSelectedModelIndices().first(), colliderIndex, PhysicsSetup::Ragdoll);
|
||||
}
|
||||
|
||||
void RagdollNodeWidget::OnPasteCollider(size_t colliderIndex, bool replace)
|
||||
{
|
||||
ColliderHelpers::PasteColliderFromClipboard(m_selectedModelIndices.first(), colliderIndex, PhysicsSetup::Ragdoll, replace);
|
||||
ColliderHelpers::PasteColliderFromClipboard(GetSelectedModelIndices().first(), colliderIndex, PhysicsSetup::Ragdoll, replace);
|
||||
}
|
||||
|
||||
void RagdollNodeWidget::OnRemoveCollider(size_t colliderIndex)
|
||||
|
||||
+12
-8
@@ -131,7 +131,8 @@ namespace EMotionFX
|
||||
|
||||
void SimulatedObjectColliderWidget::InternalReinit()
|
||||
{
|
||||
if (m_selectedModelIndices.size() == 1)
|
||||
const QModelIndexList& selectedModelIndices = GetSelectedModelIndices();
|
||||
if (selectedModelIndices.size() == 1)
|
||||
{
|
||||
Physics::CharacterColliderNodeConfiguration* nodeConfig = GetNodeConfig();
|
||||
if (nodeConfig)
|
||||
@@ -172,12 +173,13 @@ namespace EMotionFX
|
||||
}
|
||||
|
||||
AZStd::string labelText;
|
||||
const QModelIndexList& selectedModelIndices = GetSelectedModelIndices();
|
||||
const AZStd::vector<SimulatedObject*>& simObjs = actor->GetSimulatedObjectSetup()->GetSimulatedObjects();
|
||||
for (const SimulatedObject* obj : simObjs)
|
||||
{
|
||||
for (int i = 0; i < m_selectedModelIndices.size(); ++i)
|
||||
for (int i = 0; i < selectedModelIndices.size(); ++i)
|
||||
{
|
||||
Node* node = m_selectedModelIndices[i].data(SkeletonModel::ROLE_POINTER).value<Node*>();
|
||||
Node* node = selectedModelIndices[i].data(SkeletonModel::ROLE_POINTER).value<Node*>();
|
||||
if (obj->FindSimulatedJointBySkeletonJointIndex(node->GetNodeIndex()))
|
||||
{
|
||||
if (!labelText.empty())
|
||||
@@ -208,8 +210,9 @@ namespace EMotionFX
|
||||
return;
|
||||
}
|
||||
|
||||
const QModelIndexList& selectedModelIndices = GetSelectedModelIndices();
|
||||
// Only show the notification when it is single selection.
|
||||
if (m_selectedModelIndices.size() != 1)
|
||||
if (selectedModelIndices.size() != 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -250,17 +253,18 @@ namespace EMotionFX
|
||||
|
||||
void SimulatedObjectColliderWidget::OnAddCollider(const AZ::TypeId& colliderType)
|
||||
{
|
||||
ColliderHelpers::AddCollider(m_selectedModelIndices, PhysicsSetup::SimulatedObjectCollider, colliderType);
|
||||
ColliderHelpers::AddCollider(GetSelectedModelIndices(), PhysicsSetup::SimulatedObjectCollider, colliderType);
|
||||
}
|
||||
|
||||
void SimulatedObjectColliderWidget::OnCopyCollider(size_t colliderIndex)
|
||||
{
|
||||
ColliderHelpers::CopyColliderToClipboard(m_selectedModelIndices.first(), colliderIndex, PhysicsSetup::SimulatedObjectCollider);
|
||||
ColliderHelpers::CopyColliderToClipboard(GetSelectedModelIndices().first(), colliderIndex, PhysicsSetup::SimulatedObjectCollider);
|
||||
}
|
||||
|
||||
void SimulatedObjectColliderWidget::OnPasteCollider(size_t colliderIndex, bool replace)
|
||||
{
|
||||
ColliderHelpers::PasteColliderFromClipboard(m_selectedModelIndices.first(), colliderIndex, PhysicsSetup::SimulatedObjectCollider, replace);
|
||||
ColliderHelpers::PasteColliderFromClipboard(
|
||||
GetSelectedModelIndices().first(), colliderIndex, PhysicsSetup::SimulatedObjectCollider, replace);
|
||||
}
|
||||
|
||||
void SimulatedObjectColliderWidget::OnRemoveCollider(size_t colliderIndex)
|
||||
@@ -270,7 +274,7 @@ namespace EMotionFX
|
||||
|
||||
Physics::CharacterColliderNodeConfiguration* SimulatedObjectColliderWidget::GetNodeConfig() const
|
||||
{
|
||||
AZ_Assert(m_selectedModelIndices.size() == 1, "Get Node config function only return the config when it is single seleted");
|
||||
AZ_Assert(GetSelectedModelIndices().size() == 1, "Get Node config function only return the config when it is single seleted");
|
||||
Actor* actor = GetActor();
|
||||
Node* joint = GetNode();
|
||||
if (!actor || !joint)
|
||||
|
||||
+4
-4
@@ -211,15 +211,15 @@ namespace EMotionFX
|
||||
|
||||
AZ::Outcome<const QModelIndexList&> SkeletonOutlinerPlugin::GetSelectedRowIndices()
|
||||
{
|
||||
return AZ::Success(m_selectedRows);
|
||||
return AZ::Success(m_treeView->selectionModel()->selectedRows());
|
||||
}
|
||||
|
||||
void SkeletonOutlinerPlugin::OnSelectionChanged([[maybe_unused]] const QItemSelection& selected, [[maybe_unused]] const QItemSelection& deselected)
|
||||
{
|
||||
m_selectedRows = m_treeView->selectionModel()->selectedRows();
|
||||
if (m_selectedRows.size() == 1)
|
||||
QModelIndexList selectedRows = m_treeView->selectionModel()->selectedRows();
|
||||
if (selectedRows.size() == 1)
|
||||
{
|
||||
const QModelIndex& modelIndex = m_selectedRows[0];
|
||||
const QModelIndex& modelIndex = selectedRows[0];
|
||||
Node* selectedNode = modelIndex.data(SkeletonModel::ROLE_POINTER).value<Node*>();
|
||||
Actor* selectedActor = modelIndex.data(SkeletonModel::ROLE_ACTOR_POINTER).value<Actor*>();
|
||||
SkeletonOutlinerNotificationBus::Broadcast(&SkeletonOutlinerNotifications::SingleNodeSelectionChanged, selectedActor, selectedNode);
|
||||
|
||||
@@ -72,14 +72,7 @@ namespace EMotionFX
|
||||
|
||||
setLayout(mainLayout);
|
||||
|
||||
AZ::Outcome<const QModelIndexList&> selectedRowIndicesOutcome;
|
||||
QModelIndexList selectedModelIndices;
|
||||
SkeletonOutlinerRequestBus::BroadcastResult(selectedRowIndicesOutcome, &SkeletonOutlinerRequests::GetSelectedRowIndices);
|
||||
if (selectedRowIndicesOutcome.IsSuccess())
|
||||
{
|
||||
selectedModelIndices = selectedRowIndicesOutcome.GetValue();
|
||||
}
|
||||
Reinit(selectedModelIndices);
|
||||
Reinit();
|
||||
|
||||
// Connect to the model.
|
||||
SkeletonModel* skeletonModel = nullptr;
|
||||
@@ -92,9 +85,9 @@ namespace EMotionFX
|
||||
}
|
||||
}
|
||||
|
||||
void SkeletonModelJointWidget::Reinit(const QModelIndexList& selectedModelIndices)
|
||||
void SkeletonModelJointWidget::Reinit()
|
||||
{
|
||||
m_selectedModelIndices = selectedModelIndices;
|
||||
const QModelIndexList& selectedModelIndices = GetSelectedModelIndices();
|
||||
|
||||
if (!EMStudio::GetManager()->GetIgnoreVisibility() && !isVisible())
|
||||
{
|
||||
@@ -103,15 +96,15 @@ namespace EMotionFX
|
||||
|
||||
if (GetActor())
|
||||
{
|
||||
if (!m_selectedModelIndices.isEmpty())
|
||||
if (!selectedModelIndices.isEmpty())
|
||||
{
|
||||
if (m_selectedModelIndices.size() == 1)
|
||||
if (selectedModelIndices.size() == 1)
|
||||
{
|
||||
m_jointNameLabel->setText(GetNode()->GetName());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_jointNameLabel->setText(QString("%1 joints selected").arg(m_selectedModelIndices.size()));
|
||||
m_jointNameLabel->setText(QString("%1 joints selected").arg(selectedModelIndices.size()));
|
||||
}
|
||||
|
||||
m_noSelectionWidget->hide();
|
||||
@@ -136,7 +129,7 @@ namespace EMotionFX
|
||||
void SkeletonModelJointWidget::showEvent(QShowEvent* event)
|
||||
{
|
||||
QWidget::showEvent(event);
|
||||
Reinit(m_selectedModelIndices);
|
||||
Reinit();
|
||||
}
|
||||
|
||||
void SkeletonModelJointWidget::OnSelectionChanged([[maybe_unused]] const QItemSelection& selected, [[maybe_unused]] const QItemSelection& deselected)
|
||||
@@ -146,36 +139,28 @@ namespace EMotionFX
|
||||
if (skeletonModel)
|
||||
{
|
||||
const QModelIndexList selectedRows = skeletonModel->GetSelectionModel().selectedRows();
|
||||
Reinit(selectedRows);
|
||||
}
|
||||
Reinit();
|
||||
}
|
||||
|
||||
void SkeletonModelJointWidget::OnDataChanged([[maybe_unused]] const QModelIndex& topLeft, [[maybe_unused]] const QModelIndex& bottomRight, [[maybe_unused]] const QVector<int>& roles)
|
||||
{
|
||||
Reinit(m_selectedModelIndices);
|
||||
Reinit();
|
||||
}
|
||||
|
||||
void SkeletonModelJointWidget::OnModelReset()
|
||||
{
|
||||
Reinit(QModelIndexList());
|
||||
Reinit();
|
||||
}
|
||||
|
||||
Actor* SkeletonModelJointWidget::GetActor() const
|
||||
{
|
||||
Actor* actor = nullptr;
|
||||
if (!m_selectedModelIndices.empty())
|
||||
SkeletonModel* skeletonModel = nullptr;
|
||||
SkeletonOutlinerRequestBus::BroadcastResult(skeletonModel, &SkeletonOutlinerRequests::GetModel);
|
||||
if (skeletonModel)
|
||||
{
|
||||
actor = m_selectedModelIndices[0].data(SkeletonModel::ROLE_ACTOR_POINTER).value<Actor*>();
|
||||
}
|
||||
|
||||
if (!actor)
|
||||
{
|
||||
SkeletonModel* skeletonModel = nullptr;
|
||||
SkeletonOutlinerRequestBus::BroadcastResult(skeletonModel, &SkeletonOutlinerRequests::GetModel);
|
||||
if (skeletonModel)
|
||||
{
|
||||
actor = skeletonModel->GetActor();
|
||||
}
|
||||
actor = skeletonModel->GetActor();
|
||||
}
|
||||
return actor;
|
||||
}
|
||||
@@ -183,10 +168,24 @@ namespace EMotionFX
|
||||
Node* SkeletonModelJointWidget::GetNode() const
|
||||
{
|
||||
Node* node = nullptr;
|
||||
if (!m_selectedModelIndices.empty())
|
||||
const QModelIndexList& selectedModelIndices = GetSelectedModelIndices();
|
||||
if (!selectedModelIndices.empty())
|
||||
{
|
||||
node = m_selectedModelIndices[0].data(SkeletonModel::ROLE_POINTER).value<Node*>();
|
||||
node = selectedModelIndices[0].data(SkeletonModel::ROLE_POINTER).value<Node*>();
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
QModelIndexList SkeletonModelJointWidget::GetSelectedModelIndices() const
|
||||
{
|
||||
QModelIndexList selectedModelIndices;
|
||||
SkeletonModel* skeletonModel = nullptr;
|
||||
SkeletonOutlinerRequestBus::BroadcastResult(skeletonModel, &SkeletonOutlinerRequests::GetModel);
|
||||
if (skeletonModel)
|
||||
{
|
||||
selectedModelIndices = skeletonModel->GetSelectionModel().selectedRows();
|
||||
}
|
||||
|
||||
return selectedModelIndices;
|
||||
}
|
||||
} // namespace EMotionFX
|
||||
|
||||
@@ -33,13 +33,14 @@ namespace EMotionFX
|
||||
|
||||
virtual void CreateGUI();
|
||||
|
||||
void Reinit(const QModelIndexList& selectedModelIndices);
|
||||
void Reinit();
|
||||
|
||||
void showEvent(QShowEvent* event) override;
|
||||
|
||||
protected:
|
||||
Actor* GetActor() const;
|
||||
Node* GetNode() const;
|
||||
QModelIndexList GetSelectedModelIndices() const;
|
||||
virtual QWidget* CreateContentWidget(QWidget* parent) = 0;
|
||||
virtual QWidget* CreateNoSelectionWidget(QWidget* parent) = 0;
|
||||
virtual void InternalReinit() = 0;
|
||||
@@ -50,7 +51,6 @@ namespace EMotionFX
|
||||
void OnModelReset();
|
||||
|
||||
protected:
|
||||
QModelIndexList m_selectedModelIndices;
|
||||
QLabel* m_jointNameLabel;
|
||||
static int s_jointLabelSpacing;
|
||||
static int s_jointNameSpacing;
|
||||
|
||||
@@ -49,7 +49,6 @@ namespace HttpRequestor
|
||||
{
|
||||
m_thread.join();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Manager::AddRequest(Parameters && httpRequestParameters)
|
||||
|
||||
@@ -199,6 +199,8 @@ namespace Multiplayer
|
||||
|
||||
friend class NetworkEntityManager;
|
||||
friend class EntityReplicationManager;
|
||||
|
||||
friend class HierarchyTests;
|
||||
};
|
||||
|
||||
bool NetworkRoleHasController(NetEntityRole networkRole);
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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/ComponentBus.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
using NetworkHierarchyChangedEvent = AZ::Event<const AZ::EntityId&>;
|
||||
using NetworkHierarchyLeaveEvent = AZ::Event<>;
|
||||
|
||||
class NetworkHierarchyRequests
|
||||
: public AZ::ComponentBus
|
||||
{
|
||||
public:
|
||||
//! @returns true if the entity a hierarchical component attached should be considered for inclusion in a hierarchy
|
||||
//! this should return false when an entity is deactivating
|
||||
virtual bool IsHierarchyEnabled() const = 0;
|
||||
|
||||
//! @returns hierarchical entities, the first element is the top level root
|
||||
virtual AZStd::vector<AZ::Entity*> GetHierarchicalEntities() const = 0;
|
||||
|
||||
//! @returns the top level root of a hierarchy, or nullptr if this entity is not in a hierarchy
|
||||
virtual AZ::Entity* GetHierarchicalRoot() const = 0;
|
||||
|
||||
//! @return true if this entity is a child entity within a hierarchy
|
||||
virtual bool IsHierarchicalChild() const = 0;
|
||||
|
||||
//! @return true if this entity is the top level root of a hierarchy
|
||||
virtual bool IsHierarchicalRoot() const = 0;
|
||||
|
||||
//! Binds the provided NetworkHierarchyChangedEvent handler to a Network Hierarchy component.
|
||||
//! @param handler the handler to invoke when the entity's network hierarchy has been modified.
|
||||
virtual void BindNetworkHierarchyChangedEventHandler(NetworkHierarchyChangedEvent::Handler& handler) = 0;
|
||||
|
||||
//! Binds the provided NetworkHierarchyLeaveEvent handler to a Network Hierarchy component.
|
||||
//! @param handler the handler to invoke when the entity left its network hierarchy.
|
||||
virtual void BindNetworkHierarchyLeaveEventHandler(NetworkHierarchyLeaveEvent::Handler& handler) = 0;
|
||||
};
|
||||
|
||||
typedef AZ::EBus<NetworkHierarchyRequests> NetworkHierarchyRequestBus;
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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/Component/TransformBus.h>
|
||||
#include <Multiplayer/Components/NetworkHierarchyBus.h>
|
||||
#include <Source/AutoGen/NetworkHierarchyChildComponent.AutoComponent.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
class NetworkHierarchyRootComponent;
|
||||
|
||||
//! @class NetworkHierarchyChildComponent
|
||||
//! @brief Component that declares network dependency on the parent of this entity
|
||||
/*
|
||||
* The parent of this entity should have @NetworkHierarchyChildComponent (or @NetworkHierarchyRootComponent).
|
||||
* A network hierarchy is a collection of entities with one @NetworkHierarchyRootComponent at the top parent
|
||||
* and one or more @NetworkHierarchyChildComponent on its child entities.
|
||||
*/
|
||||
class NetworkHierarchyChildComponent final
|
||||
: public NetworkHierarchyChildComponentBase
|
||||
, public NetworkHierarchyRequestBus::Handler
|
||||
{
|
||||
friend class NetworkHierarchyRootComponent;
|
||||
|
||||
public:
|
||||
AZ_MULTIPLAYER_COMPONENT(Multiplayer::NetworkHierarchyChildComponent, s_networkHierarchyChildComponentConcreteUuid, Multiplayer::NetworkHierarchyChildComponentBase);
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
|
||||
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
|
||||
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
|
||||
|
||||
NetworkHierarchyChildComponent();
|
||||
|
||||
//! NetworkHierarchyChildComponentBase overrides.
|
||||
//! @{
|
||||
void OnInit() override;
|
||||
void OnActivate(EntityIsMigrating entityIsMigrating) override;
|
||||
void OnDeactivate(EntityIsMigrating entityIsMigrating) override;
|
||||
//! @}
|
||||
|
||||
//! NetworkHierarchyRequestBus overrides.
|
||||
//! @{
|
||||
bool IsHierarchyEnabled() const override;
|
||||
bool IsHierarchicalChild() const override;
|
||||
bool IsHierarchicalRoot() const override { return false; }
|
||||
AZ::Entity* GetHierarchicalRoot() const override;
|
||||
AZStd::vector<AZ::Entity*> GetHierarchicalEntities() const override;
|
||||
void BindNetworkHierarchyChangedEventHandler(NetworkHierarchyChangedEvent::Handler& handler) override;
|
||||
void BindNetworkHierarchyLeaveEventHandler(NetworkHierarchyLeaveEvent::Handler& handler) override;
|
||||
//! @}
|
||||
|
||||
protected:
|
||||
//! Used by @NetworkHierarchyRootComponent
|
||||
void SetTopLevelHierarchyRootEntity(AZ::Entity* hierarchyRoot);
|
||||
|
||||
private:
|
||||
AZ::ChildChangedEvent::Handler m_childChangedHandler;
|
||||
AZ::ParentChangedEvent::Handler m_parentChangedHandler;
|
||||
|
||||
void OnChildChanged(AZ::ChildChangeType type, AZ::EntityId child);
|
||||
void OnParentChanged(AZ::EntityId oldParent, AZ::EntityId parent);
|
||||
|
||||
//! Points to the top level root.
|
||||
AZ::Entity* m_rootEntity = nullptr;
|
||||
|
||||
AZ::Event<NetEntityId>::Handler m_hierarchyRootNetIdChanged;
|
||||
void OnHierarchyRootNetIdChanged(NetEntityId rootNetId);
|
||||
|
||||
NetworkHierarchyChangedEvent m_networkHierarchyChangedEvent;
|
||||
NetworkHierarchyLeaveEvent m_networkHierarchyLeaveEvent;
|
||||
|
||||
//! Set to false when deactivating or otherwise not to be included in hierarchy considerations.
|
||||
bool m_isHierarchyEnabled = true;
|
||||
|
||||
void NotifyChildrenHierarchyDisbanded();
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* 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/Component/TransformBus.h>
|
||||
#include <Multiplayer/Components/NetworkHierarchyBus.h>
|
||||
#include <Source/AutoGen/NetworkHierarchyRootComponent.AutoComponent.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
//! @class NetworkHierarchyRootComponent
|
||||
//! @brief Component that declares the top level entity of a network hierarchy.
|
||||
/*
|
||||
* Call @GetHierarchicalEntities to get the list of hierarchical entities.
|
||||
* A network hierarchy is meant to be a small group of entities. You can control the maximum supported size of
|
||||
* a network hierarchy by modifying CVar @bg_hierarchyEntityMaxLimit.
|
||||
*
|
||||
* A root component marks either a top most root of a hierarchy, or an inner root of an attach hierarchy.
|
||||
*/
|
||||
class NetworkHierarchyRootComponent final
|
||||
: public NetworkHierarchyRootComponentBase
|
||||
, public NetworkHierarchyRequestBus::Handler
|
||||
{
|
||||
friend class NetworkHierarchyChildComponent;
|
||||
public:
|
||||
AZ_MULTIPLAYER_COMPONENT(Multiplayer::NetworkHierarchyRootComponent, s_networkHierarchyRootComponentConcreteUuid, Multiplayer::NetworkHierarchyRootComponentBase);
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
|
||||
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
|
||||
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
|
||||
|
||||
NetworkHierarchyRootComponent();
|
||||
|
||||
//! NetworkHierarchyRootComponentBase overrides.
|
||||
//! @{
|
||||
void OnInit() override;
|
||||
void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
|
||||
void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
|
||||
//! @}
|
||||
|
||||
//! NetworkHierarchyRequestBus overrides.
|
||||
//! @{
|
||||
bool IsHierarchyEnabled() const override;
|
||||
bool IsHierarchicalRoot() const override;
|
||||
bool IsHierarchicalChild() const override;
|
||||
AZStd::vector<AZ::Entity*> GetHierarchicalEntities() const override;
|
||||
AZ::Entity* GetHierarchicalRoot() const override;
|
||||
void BindNetworkHierarchyChangedEventHandler(NetworkHierarchyChangedEvent::Handler& handler) override;
|
||||
void BindNetworkHierarchyLeaveEventHandler(NetworkHierarchyLeaveEvent::Handler& handler) override;
|
||||
//! @}
|
||||
|
||||
protected:
|
||||
void SetTopLevelHierarchyRootEntity(AZ::Entity* hierarchyRoot);
|
||||
|
||||
private:
|
||||
AZ::ChildChangedEvent::Handler m_childChangedHandler;
|
||||
AZ::ParentChangedEvent::Handler m_parentChangedHandler;
|
||||
|
||||
void OnChildChanged(AZ::ChildChangeType type, AZ::EntityId child);
|
||||
void OnParentChanged(AZ::EntityId oldParent, AZ::EntityId parent);
|
||||
|
||||
NetworkHierarchyChangedEvent m_networkHierarchyChangedEvent;
|
||||
NetworkHierarchyLeaveEvent m_networkHierarchyLeaveEvent;
|
||||
|
||||
//! Points to the top level root, if this root is an inner root in this hierarchy.
|
||||
AZ::Entity* m_rootEntity = nullptr;
|
||||
|
||||
AZStd::vector<AZ::Entity*> m_hierarchicalEntities;
|
||||
|
||||
//! Rebuilds hierarchy starting from this root component's entity.
|
||||
void RebuildHierarchy();
|
||||
|
||||
//! @param underEntity Walk the child entities that belong to @underEntity and consider adding them to the hierarchy
|
||||
//! @param currentEntityCount The total number of entities in the hierarchy prior to calling this method,
|
||||
//! used to avoid adding too many entities to the hierarchy while walking recursively the relevant entities.
|
||||
//! @currentEntityCount will be modified to reflect the total entity count upon completion of this method.
|
||||
//! @returns false if an attempt was made to go beyond the maximum supported hierarchy size, true otherwise
|
||||
bool RecursiveAttachHierarchicalEntities(AZ::EntityId underEntity, uint32_t& currentEntityCount);
|
||||
|
||||
//! @param entity Add the child entity and any of its relevant children to the hierarchy
|
||||
//! @param currentEntityCount The total number of entities in the hierarchy prior to calling this method,
|
||||
//! used to avoid adding too many entities to the hierarchy while walking recursively the relevant entities.
|
||||
//! @currentEntityCount will be modified to reflect the total entity count upon completion of this method.
|
||||
//! @returns false if an attempt was made to go beyond the maximum supported hierarchy size, true otherwise
|
||||
bool RecursiveAttachHierarchicalChild(AZ::EntityId entity, uint32_t& currentEntityCount);
|
||||
|
||||
void SetRootForEntity(AZ::Entity* root, const AZ::Entity* childEntity);
|
||||
|
||||
//! Set to false when deactivating or otherwise not to be included in hierarchy considerations.
|
||||
bool m_isHierarchyEnabled = true;
|
||||
};
|
||||
}
|
||||
@@ -31,9 +31,11 @@ namespace Multiplayer
|
||||
private:
|
||||
void OnPreRender(float deltaTime);
|
||||
void OnCorrection();
|
||||
|
||||
void OnParentChanged(NetEntityId parentId);
|
||||
|
||||
EntityPreRenderEvent::Handler m_entityPreRenderEventHandler;
|
||||
EntityCorrectionEvent::Handler m_entityCorrectionEventHandler;
|
||||
AZ::Event<NetEntityId>::Handler m_parentChangedEventHandler;
|
||||
|
||||
Multiplayer::HostFrameId m_targetHostFrameId = HostFrameId(0);
|
||||
};
|
||||
@@ -49,7 +51,9 @@ namespace Multiplayer
|
||||
|
||||
private:
|
||||
void OnTransformChangedEvent(const AZ::Transform& worldTm);
|
||||
void OnParentIdChangedEvent(AZ::EntityId oldParent, AZ::EntityId newParent);
|
||||
|
||||
AZ::TransformChangedEvent::Handler m_transformChangedHandler;
|
||||
AZ::ParentChangedEvent::Handler m_parentIdChangedHandler;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/EBus/Event.h>
|
||||
#include <AzCore/Asset/AssetCommon.h>
|
||||
#include <AzFramework/Spawnable/SpawnableEntitiesInterface.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
@@ -75,6 +76,15 @@ namespace Multiplayer
|
||||
const AZ::Transform& transform
|
||||
) = 0;
|
||||
|
||||
//! Requests a network spawnable to instantiate at a given transform
|
||||
//! This is an async function. The instantiated entities are not available immediately but will be constructed by the spawnable system
|
||||
//! The spawnable ticket has to be kept for the whole lifetime of the entities
|
||||
//! @param netSpawnable the network spawnable to spawn
|
||||
//! @param transform the transform where the spawnable should be spawned
|
||||
//! @return the ticket for managing the spawned entities
|
||||
[[nodiscard]] virtual AZStd::unique_ptr<AzFramework::EntitySpawnTicket> RequestNetSpawnableInstantiation(
|
||||
const AZ::Data::Asset<AzFramework::Spawnable>& netSpawnable, const AZ::Transform& transform) = 0;
|
||||
|
||||
//! Configures new networked entity
|
||||
//! @param netEntity the entity to setup
|
||||
//! @param prefabEntryId the name of the spawnable the entity originated from
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<Component
|
||||
Name="NetworkHierarchyChildComponent"
|
||||
Namespace="Multiplayer"
|
||||
OverrideComponent="true"
|
||||
OverrideController="false"
|
||||
OverrideInclude="Multiplayer/Components/NetworkHierarchyChildComponent.h"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
|
||||
<ComponentRelation Constraint="Required" HasController="true" Name="NetworkTransformComponent" Namespace="Multiplayer" Include="Multiplayer/Components/NetworkTransformComponent.h" />
|
||||
|
||||
<NetworkProperty Type="NetEntityId" Name="hierarchyRoot" Init="InvalidNetEntityId" ReplicateFrom="Authority" ReplicateTo="Client" IsRewindable="false" IsPredictable="false" IsPublic="true" Container="Object" ExposeToEditor="false" ExposeToScript="false" GenerateEventBindings="true" />
|
||||
</Component>
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<Component
|
||||
Name="NetworkHierarchyRootComponent"
|
||||
Namespace="Multiplayer"
|
||||
OverrideComponent="true"
|
||||
OverrideController="false"
|
||||
OverrideInclude="Multiplayer/Components/NetworkHierarchyRootComponent.h"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
|
||||
<ComponentRelation Constraint="Required" HasController="true" Name="NetworkTransformComponent" Namespace="Multiplayer" Include="Multiplayer/Components/NetworkTransformComponent.h" />
|
||||
|
||||
<NetworkProperty Type="NetEntityId" Name="hierarchyRoot" Init="InvalidNetEntityId" ReplicateFrom="Authority" ReplicateTo="Client" IsRewindable="false" IsPredictable="false" IsPublic="true" Container="Object" ExposeToEditor="false" ExposeToScript="false" GenerateEventBindings="true" />
|
||||
</Component>
|
||||
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
#include <AzFramework/Components/TransformComponent.h>
|
||||
#include <Multiplayer/IMultiplayer.h>
|
||||
#include <Multiplayer/Components/NetBindComponent.h>
|
||||
#include <Multiplayer/Components/NetworkHierarchyBus.h>
|
||||
#include <Multiplayer/Components/NetworkHierarchyChildComponent.h>
|
||||
#include <Multiplayer/Components/NetworkHierarchyRootComponent.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
void NetworkHierarchyChildComponent::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
|
||||
if (serializeContext)
|
||||
{
|
||||
serializeContext->Class<NetworkHierarchyChildComponent, NetworkHierarchyChildComponentBase>()
|
||||
->Version(1);
|
||||
|
||||
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
|
||||
{
|
||||
editContext->Class<NetworkHierarchyChildComponent>(
|
||||
"Network Hierarchy Child", "Declares a network dependency on the root of this hierarchy.")
|
||||
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
||||
->Attribute(AZ::Edit::Attributes::Category, "Multiplayer")
|
||||
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game"))
|
||||
;
|
||||
}
|
||||
}
|
||||
NetworkHierarchyChildComponentBase::Reflect(context);
|
||||
}
|
||||
|
||||
void NetworkHierarchyChildComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
|
||||
{
|
||||
required.push_back(AZ_CRC_CE("NetworkTransformComponent"));
|
||||
}
|
||||
|
||||
void NetworkHierarchyChildComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
|
||||
{
|
||||
provided.push_back(AZ_CRC_CE("NetworkHierarchyChildComponent"));
|
||||
}
|
||||
|
||||
void NetworkHierarchyChildComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
|
||||
{
|
||||
incompatible.push_back(AZ_CRC_CE("NetworkHierarchyChildComponent"));
|
||||
incompatible.push_back(AZ_CRC_CE("NetworkHierarchyRootComponent"));
|
||||
}
|
||||
|
||||
NetworkHierarchyChildComponent::NetworkHierarchyChildComponent()
|
||||
: m_childChangedHandler([this](AZ::ChildChangeType type, AZ::EntityId child) { OnChildChanged(type, child); })
|
||||
, m_parentChangedHandler([this](AZ::EntityId oldParent, AZ::EntityId parent) { OnParentChanged(oldParent, parent); })
|
||||
, m_hierarchyRootNetIdChanged([this](NetEntityId rootNetId) {OnHierarchyRootNetIdChanged(rootNetId); })
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void NetworkHierarchyChildComponent::OnInit()
|
||||
{
|
||||
}
|
||||
|
||||
void NetworkHierarchyChildComponent::OnActivate([[maybe_unused]] EntityIsMigrating entityIsMigrating)
|
||||
{
|
||||
m_isHierarchyEnabled = true;
|
||||
|
||||
HierarchyRootAddEvent(m_hierarchyRootNetIdChanged);
|
||||
NetworkHierarchyRequestBus::Handler::BusConnect(GetEntityId());
|
||||
|
||||
if (AzFramework::TransformComponent* transformComponent = GetEntity()->FindComponent<AzFramework::TransformComponent>())
|
||||
{
|
||||
transformComponent->BindChildChangedEventHandler(m_childChangedHandler);
|
||||
transformComponent->BindParentChangedEventHandler(m_parentChangedHandler);
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkHierarchyChildComponent::OnDeactivate([[maybe_unused]] EntityIsMigrating entityIsMigrating)
|
||||
{
|
||||
m_isHierarchyEnabled = false;
|
||||
|
||||
if (m_rootEntity)
|
||||
{
|
||||
if (NetworkHierarchyRootComponent* root = m_rootEntity->FindComponent<NetworkHierarchyRootComponent>())
|
||||
{
|
||||
root->RebuildHierarchy();
|
||||
}
|
||||
}
|
||||
|
||||
NotifyChildrenHierarchyDisbanded();
|
||||
|
||||
NetworkHierarchyRequestBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
bool NetworkHierarchyChildComponent::IsHierarchyEnabled() const
|
||||
{
|
||||
return m_isHierarchyEnabled;
|
||||
}
|
||||
|
||||
bool NetworkHierarchyChildComponent::IsHierarchicalChild() const
|
||||
{
|
||||
return GetHierarchyRoot() != InvalidNetEntityId;
|
||||
}
|
||||
|
||||
AZ::Entity* NetworkHierarchyChildComponent::GetHierarchicalRoot() const
|
||||
{
|
||||
return m_rootEntity;
|
||||
}
|
||||
|
||||
AZStd::vector<AZ::Entity*> NetworkHierarchyChildComponent::GetHierarchicalEntities() const
|
||||
{
|
||||
if (m_rootEntity)
|
||||
{
|
||||
return m_rootEntity->FindComponent<NetworkHierarchyRootComponent>()->GetHierarchicalEntities();
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void NetworkHierarchyChildComponent::BindNetworkHierarchyChangedEventHandler(NetworkHierarchyChangedEvent::Handler& handler)
|
||||
{
|
||||
handler.Connect(m_networkHierarchyChangedEvent);
|
||||
}
|
||||
|
||||
void NetworkHierarchyChildComponent::BindNetworkHierarchyLeaveEventHandler(NetworkHierarchyLeaveEvent::Handler& handler)
|
||||
{
|
||||
handler.Connect(m_networkHierarchyLeaveEvent);
|
||||
}
|
||||
|
||||
void NetworkHierarchyChildComponent::SetTopLevelHierarchyRootEntity(AZ::Entity* hierarchyRoot)
|
||||
{
|
||||
m_rootEntity = hierarchyRoot;
|
||||
if (HasController() && GetNetBindComponent()->GetNetEntityRole() == NetEntityRole::Authority)
|
||||
{
|
||||
NetworkHierarchyChildComponentController* controller = static_cast<NetworkHierarchyChildComponentController*>(GetController());
|
||||
if (m_rootEntity)
|
||||
{
|
||||
const NetEntityId netRootId = GetNetworkEntityManager()->GetNetEntityIdById(m_rootEntity->GetId());
|
||||
controller->SetHierarchyRoot(netRootId);
|
||||
|
||||
m_networkHierarchyChangedEvent.Signal(m_rootEntity->GetId());
|
||||
}
|
||||
else
|
||||
{
|
||||
controller->SetHierarchyRoot(InvalidNetEntityId);
|
||||
|
||||
m_networkHierarchyLeaveEvent.Signal();
|
||||
}
|
||||
}
|
||||
|
||||
if (m_rootEntity == nullptr)
|
||||
{
|
||||
NotifyChildrenHierarchyDisbanded();
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkHierarchyChildComponent::OnChildChanged([[maybe_unused]] AZ::ChildChangeType type, [[maybe_unused]] AZ::EntityId child)
|
||||
{
|
||||
if (m_rootEntity)
|
||||
{
|
||||
if (NetworkHierarchyRootComponent* root = m_rootEntity->FindComponent<NetworkHierarchyRootComponent>())
|
||||
{
|
||||
root->RebuildHierarchy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkHierarchyChildComponent::OnParentChanged([[maybe_unused]] AZ::EntityId oldParent, [[maybe_unused]] AZ::EntityId parent)
|
||||
{
|
||||
if (m_rootEntity)
|
||||
{
|
||||
if (NetworkHierarchyRootComponent* root = m_rootEntity->FindComponent<NetworkHierarchyRootComponent>())
|
||||
{
|
||||
root->RebuildHierarchy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkHierarchyChildComponent::OnHierarchyRootNetIdChanged(NetEntityId rootNetId)
|
||||
{
|
||||
ConstNetworkEntityHandle rootHandle = GetNetworkEntityManager()->GetEntity(rootNetId);
|
||||
if (rootHandle.Exists())
|
||||
{
|
||||
AZ::Entity* newRoot = rootHandle.GetEntity();
|
||||
if (m_rootEntity != newRoot)
|
||||
{
|
||||
m_rootEntity = newRoot;
|
||||
m_networkHierarchyChangedEvent.Signal(m_rootEntity->GetId());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_isHierarchyEnabled = false;
|
||||
m_rootEntity = nullptr;
|
||||
m_networkHierarchyLeaveEvent.Signal();
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkHierarchyChildComponent::NotifyChildrenHierarchyDisbanded()
|
||||
{
|
||||
AZStd::vector<AZ::EntityId> allChildren;
|
||||
AZ::TransformBus::EventResult(allChildren, GetEntityId(), &AZ::TransformBus::Events::GetChildren);
|
||||
for (const AZ::EntityId& childEntityId : allChildren)
|
||||
{
|
||||
if (const AZ::Entity* childEntity = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->FindEntity(childEntityId))
|
||||
{
|
||||
if (auto* hierarchyChildComponent = childEntity->FindComponent<NetworkHierarchyChildComponent>())
|
||||
{
|
||||
hierarchyChildComponent->SetTopLevelHierarchyRootEntity(nullptr);
|
||||
}
|
||||
else if (auto* hierarchyRootComponent = childEntity->FindComponent<NetworkHierarchyRootComponent>())
|
||||
{
|
||||
hierarchyRootComponent->SetTopLevelHierarchyRootEntity(nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,329 @@
|
||||
/*
|
||||
* 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/Entity.h>
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
#include <AzCore/Console/ILogger.h>
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
#include <AzFramework/Components/TransformComponent.h>
|
||||
#include <Multiplayer/IMultiplayer.h>
|
||||
#include <Multiplayer/Components/NetBindComponent.h>
|
||||
#include <Multiplayer/Components/NetworkHierarchyChildComponent.h>
|
||||
#include <Multiplayer/Components/NetworkHierarchyRootComponent.h>
|
||||
|
||||
AZ_CVAR(uint32_t, bg_hierarchyEntityMaxLimit, 16, nullptr, AZ::ConsoleFunctorFlags::Null,
|
||||
"Maximum allowed size of network entity hierarchies, including top level entity.");
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
void NetworkHierarchyRootComponent::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
|
||||
if (serializeContext)
|
||||
{
|
||||
serializeContext->Class<NetworkHierarchyRootComponent, NetworkHierarchyRootComponentBase>()
|
||||
->Version(1);
|
||||
|
||||
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
|
||||
{
|
||||
editContext->Class<NetworkHierarchyRootComponent>(
|
||||
"Network Hierarchy Root", "Marks the entity as the root of an entity hierarchy.")
|
||||
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
||||
->Attribute(AZ::Edit::Attributes::Category, "Multiplayer")
|
||||
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game"))
|
||||
;
|
||||
}
|
||||
}
|
||||
NetworkHierarchyRootComponentBase::Reflect(context);
|
||||
}
|
||||
|
||||
void NetworkHierarchyRootComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
|
||||
{
|
||||
required.push_back(AZ_CRC_CE("NetworkTransformComponent"));
|
||||
}
|
||||
|
||||
void NetworkHierarchyRootComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
|
||||
{
|
||||
provided.push_back(AZ_CRC_CE("NetworkHierarchyRootComponent"));
|
||||
}
|
||||
|
||||
void NetworkHierarchyRootComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
|
||||
{
|
||||
incompatible.push_back(AZ_CRC_CE("NetworkHierarchyChildComponent"));
|
||||
incompatible.push_back(AZ_CRC_CE("NetworkHierarchyRootComponent"));
|
||||
}
|
||||
|
||||
NetworkHierarchyRootComponent::NetworkHierarchyRootComponent()
|
||||
: m_childChangedHandler([this](AZ::ChildChangeType type, AZ::EntityId child) { OnChildChanged(type, child); })
|
||||
, m_parentChangedHandler([this](AZ::EntityId oldParent, AZ::EntityId parent) { OnParentChanged(oldParent, parent); })
|
||||
{
|
||||
}
|
||||
|
||||
void NetworkHierarchyRootComponent::OnInit()
|
||||
{
|
||||
}
|
||||
|
||||
void NetworkHierarchyRootComponent::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
|
||||
{
|
||||
m_isHierarchyEnabled = true;
|
||||
m_hierarchicalEntities.push_back(GetEntity());
|
||||
|
||||
NetworkHierarchyRequestBus::Handler::BusConnect(GetEntityId());
|
||||
|
||||
if (AzFramework::TransformComponent* transformComponent = GetEntity()->FindComponent<AzFramework::TransformComponent>())
|
||||
{
|
||||
transformComponent->BindChildChangedEventHandler(m_childChangedHandler);
|
||||
transformComponent->BindParentChangedEventHandler(m_parentChangedHandler);
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkHierarchyRootComponent::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
|
||||
{
|
||||
m_isHierarchyEnabled = false;
|
||||
|
||||
if (m_rootEntity)
|
||||
{
|
||||
// Tell parent to re-build the hierarchy
|
||||
if (NetworkHierarchyRootComponent* root = m_rootEntity->FindComponent<NetworkHierarchyRootComponent>())
|
||||
{
|
||||
root->RebuildHierarchy();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Notify children that the hierarchy is disbanding
|
||||
AZStd::vector<AZ::EntityId> allChildren;
|
||||
AZ::TransformBus::EventResult(allChildren, GetEntityId(), &AZ::TransformBus::Events::GetChildren);
|
||||
|
||||
for (const AZ::EntityId& childEntityId : allChildren)
|
||||
{
|
||||
if (const AZ::Entity* childEntity = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->FindEntity(childEntityId))
|
||||
{
|
||||
SetRootForEntity(nullptr, childEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_childChangedHandler.Disconnect();
|
||||
m_parentChangedHandler.Disconnect();
|
||||
|
||||
NetworkHierarchyRequestBus::Handler::BusDisconnect();
|
||||
|
||||
m_hierarchicalEntities.clear();
|
||||
m_rootEntity = nullptr;
|
||||
}
|
||||
|
||||
bool NetworkHierarchyRootComponent::IsHierarchyEnabled() const
|
||||
{
|
||||
return m_isHierarchyEnabled;
|
||||
}
|
||||
|
||||
bool NetworkHierarchyRootComponent::IsHierarchicalRoot() const
|
||||
{
|
||||
return GetHierarchyRoot() == InvalidNetEntityId;
|
||||
}
|
||||
|
||||
bool NetworkHierarchyRootComponent::IsHierarchicalChild() const
|
||||
{
|
||||
return !IsHierarchicalRoot();
|
||||
}
|
||||
|
||||
AZStd::vector<AZ::Entity*> NetworkHierarchyRootComponent::GetHierarchicalEntities() const
|
||||
{
|
||||
return m_hierarchicalEntities;
|
||||
}
|
||||
|
||||
AZ::Entity* NetworkHierarchyRootComponent::GetHierarchicalRoot() const
|
||||
{
|
||||
if (m_rootEntity)
|
||||
{
|
||||
return m_rootEntity;
|
||||
}
|
||||
|
||||
return GetEntity();
|
||||
}
|
||||
|
||||
void NetworkHierarchyRootComponent::BindNetworkHierarchyChangedEventHandler(NetworkHierarchyChangedEvent::Handler& handler)
|
||||
{
|
||||
handler.Connect(m_networkHierarchyChangedEvent);
|
||||
}
|
||||
|
||||
void NetworkHierarchyRootComponent::BindNetworkHierarchyLeaveEventHandler(NetworkHierarchyLeaveEvent::Handler& handler)
|
||||
{
|
||||
handler.Connect(m_networkHierarchyLeaveEvent);
|
||||
}
|
||||
|
||||
void NetworkHierarchyRootComponent::OnChildChanged([[maybe_unused]] AZ::ChildChangeType type, [[maybe_unused]] AZ::EntityId child)
|
||||
{
|
||||
if (IsHierarchicalRoot())
|
||||
{
|
||||
// Parent-child notifications are not reliable enough to avoid duplicate notifications,
|
||||
// so we will rebuild from scratch to avoid duplicate entries in @m_hierarchicalEntities.
|
||||
RebuildHierarchy();
|
||||
}
|
||||
else if (NetworkHierarchyRootComponent* root = GetHierarchicalRoot()->FindComponent<NetworkHierarchyRootComponent>())
|
||||
{
|
||||
root->RebuildHierarchy();
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkHierarchyRootComponent::OnParentChanged([[maybe_unused]] AZ::EntityId oldParent, AZ::EntityId newParent)
|
||||
{
|
||||
// If the parent is part of a hierarchy, it will detect this entity as a new child and rebuild hierarchy.
|
||||
// Thus, we only need to take care of a case when the parent is not part of a hierarchy,
|
||||
// in which case, this entity will be a new root of a new hierarchy.
|
||||
|
||||
if (AZ::Entity* parentEntity = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->FindEntity(newParent))
|
||||
{
|
||||
if (parentEntity->FindComponent<NetworkHierarchyRootComponent>() == nullptr &&
|
||||
parentEntity->FindComponent<NetworkHierarchyChildComponent>() == nullptr)
|
||||
{
|
||||
RebuildHierarchy();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_hierarchicalEntities.clear();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Detached from parent
|
||||
RebuildHierarchy();
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkHierarchyRootComponent::RebuildHierarchy()
|
||||
{
|
||||
AZStd::vector<AZ::Entity*> previousEntities;
|
||||
m_hierarchicalEntities.swap(previousEntities);
|
||||
|
||||
m_hierarchicalEntities.push_back(GetEntity()); // Add the root.
|
||||
|
||||
uint32_t currentEntityCount = aznumeric_cast<uint32_t>(m_hierarchicalEntities.size());
|
||||
RecursiveAttachHierarchicalEntities(GetEntityId(), currentEntityCount);
|
||||
|
||||
bool hierarchyChanged = false;
|
||||
|
||||
// Send out join and leave events.
|
||||
for (AZ::Entity* currentEntity : m_hierarchicalEntities)
|
||||
{
|
||||
const auto prevEntityIterator = AZStd::find(previousEntities.begin(), previousEntities.end(), currentEntity);
|
||||
if (prevEntityIterator != previousEntities.end())
|
||||
{
|
||||
// This entity was here before the build of the hierarchy.
|
||||
previousEntities.erase(prevEntityIterator);
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is a newly added entity to the network hierarchy.
|
||||
hierarchyChanged = true;
|
||||
SetRootForEntity(GetEntity(), currentEntity);
|
||||
}
|
||||
}
|
||||
|
||||
// These entities were removed since last rebuild.
|
||||
for (const AZ::Entity* previousEntity : previousEntities)
|
||||
{
|
||||
SetRootForEntity(nullptr, previousEntity);
|
||||
}
|
||||
|
||||
if (!previousEntities.empty())
|
||||
{
|
||||
hierarchyChanged = true;
|
||||
}
|
||||
|
||||
if (hierarchyChanged)
|
||||
{
|
||||
m_networkHierarchyChangedEvent.Signal(GetEntityId());
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkHierarchyRootComponent::SetRootForEntity(AZ::Entity* root, const AZ::Entity* childEntity)
|
||||
{
|
||||
if (auto* hierarchyChildComponent = childEntity->FindComponent<NetworkHierarchyChildComponent>())
|
||||
{
|
||||
hierarchyChildComponent->SetTopLevelHierarchyRootEntity(root);
|
||||
}
|
||||
else if (auto* hierarchyRootComponent = childEntity->FindComponent<NetworkHierarchyRootComponent>())
|
||||
{
|
||||
hierarchyRootComponent->SetTopLevelHierarchyRootEntity(root);
|
||||
}
|
||||
}
|
||||
|
||||
bool NetworkHierarchyRootComponent::RecursiveAttachHierarchicalEntities(AZ::EntityId underEntity, uint32_t& currentEntityCount)
|
||||
{
|
||||
AZStd::vector<AZ::EntityId> allChildren;
|
||||
AZ::TransformBus::EventResult(allChildren, underEntity, &AZ::TransformBus::Events::GetChildren);
|
||||
|
||||
for (const AZ::EntityId& newChildId : allChildren)
|
||||
{
|
||||
if (!RecursiveAttachHierarchicalChild(newChildId, currentEntityCount))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NetworkHierarchyRootComponent::RecursiveAttachHierarchicalChild(AZ::EntityId entity, uint32_t& currentEntityCount)
|
||||
{
|
||||
if (currentEntityCount >= bg_hierarchyEntityMaxLimit)
|
||||
{
|
||||
AZLOG_WARN("Entity %s is trying to build a network hierarchy that is too large. bg_hierarchyEntityMaxLimit is currently set to (%u)",
|
||||
GetEntity()->GetName().c_str(), static_cast<uint32_t>(bg_hierarchyEntityMaxLimit));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (AZ::Entity* childEntity = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->FindEntity(entity))
|
||||
{
|
||||
auto* hierarchyChildComponent = childEntity->FindComponent<NetworkHierarchyChildComponent>();
|
||||
auto* hierarchyRootComponent = childEntity->FindComponent<NetworkHierarchyRootComponent>();
|
||||
|
||||
if ((hierarchyChildComponent && hierarchyChildComponent->IsHierarchyEnabled()) ||
|
||||
(hierarchyRootComponent && hierarchyRootComponent->IsHierarchyEnabled()))
|
||||
{
|
||||
m_hierarchicalEntities.push_back(childEntity);
|
||||
++currentEntityCount;
|
||||
|
||||
if (!RecursiveAttachHierarchicalEntities(entity, currentEntityCount))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NetworkHierarchyRootComponent::SetTopLevelHierarchyRootEntity(AZ::Entity* hierarchyRoot)
|
||||
{
|
||||
m_rootEntity = hierarchyRoot;
|
||||
|
||||
if (HasController() && GetNetBindComponent()->GetNetEntityRole() == NetEntityRole::Authority)
|
||||
{
|
||||
NetworkHierarchyChildComponentController* controller = static_cast<NetworkHierarchyChildComponentController*>(GetController());
|
||||
if (hierarchyRoot)
|
||||
{
|
||||
const NetEntityId netRootId = GetNetworkEntityManager()->GetNetEntityIdById(hierarchyRoot->GetId());
|
||||
controller->SetHierarchyRoot(netRootId);
|
||||
}
|
||||
else
|
||||
{
|
||||
controller->SetHierarchyRoot(InvalidNetEntityId);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_rootEntity == nullptr)
|
||||
{
|
||||
// We lost the parent hierarchical entity, so as a root we need to re-build our own hierarchy.
|
||||
RebuildHierarchy();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,7 @@ namespace Multiplayer
|
||||
NetworkTransformComponent::NetworkTransformComponent()
|
||||
: m_entityPreRenderEventHandler([this](float deltaTime) { OnPreRender(deltaTime); })
|
||||
, m_entityCorrectionEventHandler([this]() { OnCorrection(); })
|
||||
, m_parentChangedEventHandler([this](NetEntityId parentId) { OnParentChanged(parentId); })
|
||||
{
|
||||
;
|
||||
}
|
||||
@@ -41,6 +42,7 @@ namespace Multiplayer
|
||||
{
|
||||
GetNetBindComponent()->AddEntityPreRenderEventHandler(m_entityPreRenderEventHandler);
|
||||
GetNetBindComponent()->AddEntityCorrectionEventHandler(m_entityCorrectionEventHandler);
|
||||
ParentEntityIdAddEvent(m_parentChangedEventHandler);
|
||||
}
|
||||
|
||||
void NetworkTransformComponent::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
|
||||
@@ -97,10 +99,26 @@ namespace Multiplayer
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkTransformComponent::OnParentChanged(NetEntityId parentId)
|
||||
{
|
||||
const ConstNetworkEntityHandle parentEntityHandle = GetNetworkEntityManager()->GetEntity(parentId);
|
||||
if (parentEntityHandle.Exists())
|
||||
{
|
||||
if (const AZ::Entity* parentEntity = parentEntityHandle.GetEntity())
|
||||
{
|
||||
GetEntity()->GetTransform()->SetParent(parentEntity->GetId());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GetEntity()->GetTransform()->SetParent(AZ::EntityId());
|
||||
}
|
||||
}
|
||||
|
||||
NetworkTransformComponentController::NetworkTransformComponentController(NetworkTransformComponent& parent)
|
||||
: NetworkTransformComponentControllerBase(parent)
|
||||
, m_transformChangedHandler([this](const AZ::Transform&, const AZ::Transform& worldTm) { OnTransformChangedEvent(worldTm); })
|
||||
, m_parentIdChangedHandler([this](AZ::EntityId oldParent, AZ::EntityId newParent) { OnParentIdChangedEvent(oldParent, newParent); })
|
||||
{
|
||||
;
|
||||
}
|
||||
@@ -109,6 +127,9 @@ namespace Multiplayer
|
||||
{
|
||||
GetParent().GetTransformComponent()->BindTransformChangedEventHandler(m_transformChangedHandler);
|
||||
OnTransformChangedEvent(GetParent().GetTransformComponent()->GetWorldTM());
|
||||
|
||||
GetParent().GetTransformComponent()->BindParentChangedEventHandler(m_parentIdChangedHandler);
|
||||
OnParentIdChangedEvent(AZ::EntityId(), GetParent().GetTransformComponent()->GetParentId());
|
||||
}
|
||||
|
||||
void NetworkTransformComponentController::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
|
||||
@@ -122,4 +143,14 @@ namespace Multiplayer
|
||||
SetTranslation(worldTm.GetTranslation());
|
||||
SetScale(worldTm.GetUniformScale());
|
||||
}
|
||||
|
||||
void NetworkTransformComponentController::OnParentIdChangedEvent([[maybe_unused]] AZ::EntityId oldParent, AZ::EntityId newParent)
|
||||
{
|
||||
AZ::Entity* parentEntity = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->FindEntity(newParent);
|
||||
if (parentEntity)
|
||||
{
|
||||
const ConstNetworkEntityHandle parentHandle(parentEntity, GetNetworkEntityTracker());
|
||||
SetParentEntityId(parentHandle.GetNetEntityId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,13 +6,14 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzNetworking/Framework/NetworkingSystemComponent.h>
|
||||
#include <Multiplayer/Components/NetBindComponent.h>
|
||||
#include <Multiplayer/Components/NetworkHierarchyChildComponent.h>
|
||||
#include <Multiplayer/Components/NetworkHierarchyRootComponent.h>
|
||||
#include <Source/MultiplayerGem.h>
|
||||
#include <Source/MultiplayerSystemComponent.h>
|
||||
#include <Source/AutoGen/AutoComponentTypes.h>
|
||||
#include <Source/Pipeline/NetBindMarkerComponent.h>
|
||||
#include <Source/Pipeline/NetworkSpawnableHolderComponent.h>
|
||||
#include <Multiplayer/Components/NetBindComponent.h>
|
||||
#include <AzNetworking/Framework/NetworkingSystemComponent.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
@@ -23,7 +24,6 @@ namespace Multiplayer
|
||||
AzNetworking::NetworkingSystemComponent::CreateDescriptor(),
|
||||
MultiplayerSystemComponent::CreateDescriptor(),
|
||||
NetBindComponent::CreateDescriptor(),
|
||||
NetBindMarkerComponent::CreateDescriptor(),
|
||||
NetworkSpawnableHolderComponent::CreateDescriptor(),
|
||||
});
|
||||
|
||||
|
||||
+20
-6
@@ -75,6 +75,8 @@ namespace Multiplayer
|
||||
|
||||
void EntityReplicationManager::ActivatePendingEntities()
|
||||
{
|
||||
AZStd::vector<NetEntityId> notReadyEntities;
|
||||
|
||||
const AZ::TimeMs endTimeMs = AZ::GetElapsedTimeMs() + m_entityActivationTimeSliceMs;
|
||||
while (!m_entitiesPendingActivation.empty())
|
||||
{
|
||||
@@ -83,7 +85,14 @@ namespace Multiplayer
|
||||
EntityReplicator* entityReplicator = GetEntityReplicator(entityId);
|
||||
if (entityReplicator && !entityReplicator->IsMarkedForRemoval())
|
||||
{
|
||||
entityReplicator->ActivateNetworkEntity();
|
||||
if (entityReplicator->IsReadyToActivate())
|
||||
{
|
||||
entityReplicator->ActivateNetworkEntity();
|
||||
}
|
||||
else
|
||||
{
|
||||
notReadyEntities.push_back(entityId);
|
||||
}
|
||||
}
|
||||
if (m_entityActivationTimeSliceMs > AZ::TimeMs{ 0 } && AZ::GetElapsedTimeMs() > endTimeMs)
|
||||
{
|
||||
@@ -91,6 +100,11 @@ namespace Multiplayer
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (NetEntityId netEntityId : notReadyEntities)
|
||||
{
|
||||
m_entitiesPendingActivation.push_back(netEntityId);
|
||||
}
|
||||
}
|
||||
|
||||
void EntityReplicationManager::SendUpdates(AZ::TimeMs hostTimeMs)
|
||||
@@ -249,15 +263,15 @@ namespace Multiplayer
|
||||
void EntityReplicationManager::SendEntityUpdates(AZ::TimeMs hostTimeMs)
|
||||
{
|
||||
EntityReplicatorList toSendList = GenerateEntityUpdateList();
|
||||
|
||||
|
||||
AZLOG(NET_ReplicationInfo, "Sending %zd updates from %d to %d", toSendList.size(), (uint8_t)GetNetworkEntityManager()->GetHostId(), (uint8_t)GetRemoteHostId());
|
||||
|
||||
|
||||
// prep a replication record for send, at this point, everything needs to be sent
|
||||
for (EntityReplicator* replicator : toSendList)
|
||||
{
|
||||
replicator->GetPropertyPublisher()->PrepareSerialization();
|
||||
}
|
||||
|
||||
|
||||
// While our to send list is not empty, build up another packet to send
|
||||
do
|
||||
{
|
||||
@@ -524,7 +538,7 @@ namespace Multiplayer
|
||||
|
||||
bool EntityReplicationManager::HandlePropertyChangeMessage
|
||||
(
|
||||
AzNetworking::IConnection* invokingConnection,
|
||||
AzNetworking::IConnection* invokingConnection,
|
||||
EntityReplicator* entityReplicator,
|
||||
AzNetworking::PacketId packetId,
|
||||
NetEntityId netEntityId,
|
||||
@@ -1137,7 +1151,7 @@ namespace Multiplayer
|
||||
AzNetworking::TrackChangedSerializer<AzNetworking::NetworkOutputSerializer> outputSerializer(message.m_propertyUpdateData.GetBuffer(), static_cast<uint32_t>(message.m_propertyUpdateData.GetSize()));
|
||||
if (!HandlePropertyChangeMessage
|
||||
(
|
||||
invokingConnection,
|
||||
invokingConnection,
|
||||
replicator,
|
||||
AzNetworking::InvalidPacketId,
|
||||
message.m_entityId,
|
||||
|
||||
@@ -6,23 +6,25 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Source/NetworkEntity/EntityReplication/EntityReplicator.h>
|
||||
#include <Source/NetworkEntity/EntityReplication/EntityReplicationManager.h>
|
||||
#include <Source/NetworkEntity/EntityReplication/PropertyPublisher.h>
|
||||
#include <Source/NetworkEntity/EntityReplication/PropertySubscriber.h>
|
||||
#include <Source/NetworkEntity/NetworkEntityAuthorityTracker.h>
|
||||
#include <Source/NetworkEntity/NetworkEntityTracker.h>
|
||||
#include <Source/AutoGen/Multiplayer.AutoPackets.h>
|
||||
#include <Multiplayer/IMultiplayer.h>
|
||||
#include <Multiplayer/Components/NetBindComponent.h>
|
||||
#include <Multiplayer/Components/NetworkHierarchyChildComponent.h>
|
||||
#include <Multiplayer/Components/NetworkHierarchyRootComponent.h>
|
||||
#include <Multiplayer/Components/NetworkTransformComponent.h>
|
||||
#include <Multiplayer/NetworkEntity/NetworkEntityRpcMessage.h>
|
||||
#include <Source/AutoGen/Multiplayer.AutoPackets.h>
|
||||
#include <Source/NetworkEntity/NetworkEntityAuthorityTracker.h>
|
||||
#include <Source/NetworkEntity/NetworkEntityTracker.h>
|
||||
#include <Source/NetworkEntity/EntityReplication/EntityReplicationManager.h>
|
||||
#include <Source/NetworkEntity/EntityReplication/EntityReplicator.h>
|
||||
#include <Source/NetworkEntity/EntityReplication/PropertyPublisher.h>
|
||||
#include <Source/NetworkEntity/EntityReplication/PropertySubscriber.h>
|
||||
|
||||
#include <AzNetworking/ConnectionLayer/IConnection.h>
|
||||
#include <AzNetworking/PacketLayer/IPacket.h>
|
||||
#include <AzNetworking/Serialization/ISerializer.h>
|
||||
#include <AzNetworking/Serialization/NetworkInputSerializer.h>
|
||||
#include <AzNetworking/Serialization/NetworkOutputSerializer.h>
|
||||
#include <AzNetworking/ConnectionLayer/IConnection.h>
|
||||
|
||||
#include <AzCore/Component/ComponentApplicationBus.h>
|
||||
#include <AzCore/Console/IConsole.h>
|
||||
@@ -48,7 +50,7 @@ namespace Multiplayer
|
||||
, m_onForwardRpcHandler([this](NetworkEntityRpcMessage& entityRpcMessage) { OnSendRpcEvent(entityRpcMessage); })
|
||||
, m_onSendAutonomousRpcHandler([this](NetworkEntityRpcMessage& entityRpcMessage) { OnSendRpcEvent(entityRpcMessage); })
|
||||
, m_onForwardAutonomousRpcHandler([this](NetworkEntityRpcMessage& entityRpcMessage) { OnSendRpcEvent(entityRpcMessage); })
|
||||
, m_onEntityStopHandler([this](const ConstNetworkEntityHandle &) { OnEntityRemovedEvent(); })
|
||||
, m_onEntityStopHandler([this](const ConstNetworkEntityHandle&) { OnEntityRemovedEvent(); })
|
||||
, m_proxyRemovalEvent([this] { OnProxyRemovalTimedEvent(); }, AZ::Name("ProxyRemovalTimedEvent"))
|
||||
{
|
||||
if (auto localEnt = m_entityHandle.GetEntity())
|
||||
@@ -119,12 +121,12 @@ namespace Multiplayer
|
||||
{
|
||||
m_replicationManager.AddReplicatorToPendingSend(*this);
|
||||
m_propertyPublisher = AZStd::make_unique<PropertyPublisher>
|
||||
(
|
||||
GetRemoteNetworkRole(),
|
||||
!RemoteManagerOwnsEntityLifetime() ? PropertyPublisher::OwnsLifetime::True : PropertyPublisher::OwnsLifetime::False,
|
||||
m_netBindComponent,
|
||||
*m_connection
|
||||
);
|
||||
(
|
||||
GetRemoteNetworkRole(),
|
||||
!RemoteManagerOwnsEntityLifetime() ? PropertyPublisher::OwnsLifetime::True : PropertyPublisher::OwnsLifetime::False,
|
||||
m_netBindComponent,
|
||||
*m_connection
|
||||
);
|
||||
m_netBindComponent->AddEntityDirtiedEventHandler(m_onEntityDirtiedHandler);
|
||||
}
|
||||
else
|
||||
@@ -279,7 +281,7 @@ namespace Multiplayer
|
||||
AZ_Assert(netBindComponent, "No Multiplayer::NetBindComponent");
|
||||
|
||||
bool isAuthority = (GetBoundLocalNetworkRole() == NetEntityRole::Authority)
|
||||
&& (GetBoundLocalNetworkRole() == netBindComponent->GetNetEntityRole());
|
||||
&& (GetBoundLocalNetworkRole() == netBindComponent->GetNetEntityRole());
|
||||
bool isClient = GetRemoteNetworkRole() == NetEntityRole::Client;
|
||||
bool isAutonomous = GetBoundLocalNetworkRole() == NetEntityRole::Autonomous;
|
||||
if (isAuthority || isClient || isAutonomous)
|
||||
@@ -306,9 +308,9 @@ namespace Multiplayer
|
||||
bool EntityReplicator::RemoteManagerOwnsEntityLifetime() const
|
||||
{
|
||||
bool isServer = (GetBoundLocalNetworkRole() == NetEntityRole::Server)
|
||||
&& (GetRemoteNetworkRole() == NetEntityRole::Authority);
|
||||
&& (GetRemoteNetworkRole() == NetEntityRole::Authority);
|
||||
bool isClient = (GetBoundLocalNetworkRole() == NetEntityRole::Client)
|
||||
|| (GetBoundLocalNetworkRole() == NetEntityRole::Autonomous);
|
||||
|| (GetBoundLocalNetworkRole() == NetEntityRole::Autonomous);
|
||||
|
||||
return isServer || isClient;
|
||||
}
|
||||
@@ -405,6 +407,62 @@ namespace Multiplayer
|
||||
return m_replicationManager.GetResendTimeoutTimeMs();
|
||||
}
|
||||
|
||||
bool EntityReplicator::IsReadyToActivate() const
|
||||
{
|
||||
const AZ::Entity* entity = m_entityHandle.GetEntity();
|
||||
AZ_Assert(entity, "Entity replicator entity unexpectedly missing");
|
||||
|
||||
const NetworkHierarchyChildComponent* hierarchyChildComponent = entity->FindComponent<NetworkHierarchyChildComponent>();
|
||||
const NetworkHierarchyRootComponent* hierarchyRootComponent = nullptr;
|
||||
|
||||
if (hierarchyChildComponent == nullptr)
|
||||
{
|
||||
// Child and root hierarchy components are mutually exclusive
|
||||
hierarchyRootComponent = entity->FindComponent<NetworkHierarchyRootComponent>();
|
||||
}
|
||||
|
||||
if ((hierarchyChildComponent && hierarchyChildComponent->IsHierarchicalChild())
|
||||
|| (hierarchyRootComponent && hierarchyRootComponent->IsHierarchicalChild()))
|
||||
{
|
||||
// If hierarchy is enabled for the entity, check if the parent is available
|
||||
if (const NetworkTransformComponent* networkTransform = entity->FindComponent<NetworkTransformComponent>())
|
||||
{
|
||||
const NetEntityId parentId = networkTransform->GetParentEntityId();
|
||||
/*
|
||||
* For root entities attached to a level, a network parent won't be set.
|
||||
* In this case, this entity is the root entity of the hierarchy and it will be activated first.
|
||||
*/
|
||||
if (parentId != InvalidNetEntityId)
|
||||
{
|
||||
ConstNetworkEntityHandle parentHandle = GetNetworkEntityManager()->GetEntity(parentId);
|
||||
|
||||
const AZ::Entity* parentEntity = parentHandle.GetEntity();
|
||||
if (parentEntity && parentEntity->GetState() == AZ::Entity::State::Active)
|
||||
{
|
||||
AZLOG
|
||||
(
|
||||
NET_HierarchyActivationInfo,
|
||||
"Hierchical entity %s asking for activation - granted",
|
||||
entity->GetName().c_str()
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
AZLOG
|
||||
(
|
||||
NET_HierarchyActivationInfo,
|
||||
"Hierchical entity %s asking for activation - waiting on the parent %u",
|
||||
entity->GetName().c_str(),
|
||||
aznumeric_cast<uint32_t>(parentId)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
NetworkEntityUpdateMessage EntityReplicator::GenerateUpdatePacket()
|
||||
{
|
||||
if (IsMarkedForRemoval() && OwnsReplicatorLifetime()) // TODO: clean this up
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace Multiplayer
|
||||
{
|
||||
public:
|
||||
EntityReplicator(EntityReplicationManager& replicationManager, AzNetworking::IConnection* connection, NetEntityRole remoteNetworkRole, const ConstNetworkEntityHandle& entityHandle);
|
||||
virtual ~EntityReplicator();
|
||||
~EntityReplicator() override;
|
||||
|
||||
NetEntityRole GetBoundLocalNetworkRole() const;
|
||||
NetEntityRole GetRemoteNetworkRole() const;
|
||||
@@ -62,6 +62,8 @@ namespace Multiplayer
|
||||
bool IsDeletionAcknowledged() const;
|
||||
bool WasMigrated() const;
|
||||
void SetWasMigrated(bool wasMigrated);
|
||||
// If an entity is part of a network hierarchy then it is only ready to activate when its direct parent entity is active.
|
||||
bool IsReadyToActivate() const;
|
||||
|
||||
NetworkEntityUpdateMessage GenerateUpdatePacket();
|
||||
|
||||
|
||||
@@ -465,8 +465,60 @@ namespace Multiplayer
|
||||
return netEntityId;
|
||||
}
|
||||
|
||||
void NetworkEntityManager::OnRootSpawnableAssigned(
|
||||
[[maybe_unused]] AZ::Data::Asset<AzFramework::Spawnable> rootSpawnable, [[maybe_unused]] uint32_t generation)
|
||||
AZStd::unique_ptr<AzFramework::EntitySpawnTicket> NetworkEntityManager::RequestNetSpawnableInstantiation(
|
||||
const AZ::Data::Asset<AzFramework::Spawnable>& netSpawnable, const AZ::Transform& transform)
|
||||
{
|
||||
// Prepare the parameters for the spawning process
|
||||
AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs;
|
||||
optionalArgs.m_priority = AzFramework::SpawnablePriority_High;
|
||||
|
||||
const AZ::Name netSpawnableName =
|
||||
AZ::Interface<INetworkSpawnableLibrary>::Get()->GetSpawnableNameFromAssetId(netSpawnable.GetId());
|
||||
|
||||
if (netSpawnableName.IsEmpty())
|
||||
{
|
||||
AZ_Error("NetworkEntityManager", false,
|
||||
"RequestNetSpawnableInstantiation: Requested spawnable %s doesn't exist in the NetworkSpawnableLibrary. Please make sure it is a network spawnable",
|
||||
netSpawnable.GetHint().c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Pre-insertion callback allows us to do network-specific setup for the entities before they are added to the scene
|
||||
optionalArgs.m_preInsertionCallback = [netSpawnableName, rootTransform = transform]
|
||||
(AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableEntityContainerView entities)
|
||||
{
|
||||
bool shouldUpdateTransform = (rootTransform.IsClose(AZ::Transform::Identity()) == false);
|
||||
|
||||
for (uint32_t netEntityIndex = 0, entitiesSize = aznumeric_cast<uint32_t>(entities.size());
|
||||
netEntityIndex < entitiesSize; ++netEntityIndex)
|
||||
{
|
||||
AZ::Entity* netEntity = *(entities.begin() + netEntityIndex);
|
||||
|
||||
if (shouldUpdateTransform)
|
||||
{
|
||||
AzFramework::TransformComponent* netEntityTransform =
|
||||
netEntity->FindComponent<AzFramework::TransformComponent>();
|
||||
|
||||
AZ::Transform worldTm = netEntityTransform->GetWorldTM();
|
||||
worldTm = rootTransform * worldTm;
|
||||
netEntityTransform->SetWorldTM(worldTm);
|
||||
}
|
||||
|
||||
PrefabEntityId prefabEntityId;
|
||||
prefabEntityId.m_prefabName = netSpawnableName;
|
||||
prefabEntityId.m_entityOffset = netEntityIndex;
|
||||
AZ::Interface<INetworkEntityManager>::Get()->SetupNetEntity(netEntity, prefabEntityId, NetEntityRole::Authority);
|
||||
}
|
||||
};
|
||||
|
||||
// Spawn with the newly created ticket. This allows the calling code to manage the lifetime of the constructed entities
|
||||
auto ticket = AZStd::make_unique<AzFramework::EntitySpawnTicket>(netSpawnable);
|
||||
AzFramework::SpawnableEntitiesInterface::Get()->SpawnAllEntities(*ticket, AZStd::move(optionalArgs));
|
||||
return ticket;
|
||||
}
|
||||
|
||||
void NetworkEntityManager::OnRootSpawnableAssigned(AZ::Data::Asset<AzFramework::Spawnable> rootSpawnable,
|
||||
[[maybe_unused]] uint32_t generation)
|
||||
{
|
||||
auto* multiplayer = GetMultiplayer();
|
||||
const auto agentType = multiplayer->GetAgentType();
|
||||
@@ -479,7 +531,6 @@ namespace Multiplayer
|
||||
|
||||
void NetworkEntityManager::OnRootSpawnableReleased([[maybe_unused]] uint32_t generation)
|
||||
{
|
||||
// TODO: Do we need to clear all entities here?
|
||||
auto* multiplayer = GetMultiplayer();
|
||||
const auto agentType = multiplayer->GetAgentType();
|
||||
|
||||
|
||||
@@ -60,6 +60,9 @@ namespace Multiplayer
|
||||
const AZ::Transform& transform
|
||||
) override;
|
||||
|
||||
AZStd::unique_ptr<AzFramework::EntitySpawnTicket> RequestNetSpawnableInstantiation(
|
||||
const AZ::Data::Asset<AzFramework::Spawnable>& netSpawnable, const AZ::Transform& transform) override;
|
||||
|
||||
void SetupNetEntity(AZ::Entity* netEntity, PrefabEntityId prefabEntityId, NetEntityRole netEntityRole) override;
|
||||
|
||||
uint32_t GetEntityCount() const override;
|
||||
|
||||
@@ -37,6 +37,7 @@ namespace Multiplayer
|
||||
NetworkEntityHandle Get(NetEntityId netEntityId);
|
||||
ConstNetworkEntityHandle Get(NetEntityId netEntityId) const;
|
||||
|
||||
//! Returns Net Entity ID for a given AZ Entity ID.
|
||||
NetEntityId Get(const AZ::EntityId& entityId) const;
|
||||
|
||||
//! Returns true if the netEntityId exists.
|
||||
|
||||
@@ -1,115 +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 <Source/Pipeline/NetBindMarkerComponent.h>
|
||||
#include <AzCore/Asset/AssetManager.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <Multiplayer/IMultiplayer.h>
|
||||
#include <Multiplayer/INetworkSpawnableLibrary.h>
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
#include <AzFramework/Components/TransformComponent.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
void NetBindMarkerComponent::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
|
||||
if (serializeContext)
|
||||
{
|
||||
serializeContext->Class<NetBindMarkerComponent, AZ::Component>()
|
||||
->Version(1)
|
||||
->Field("NetEntityIndex", &NetBindMarkerComponent::m_netEntityIndex)
|
||||
->Field("NetSpawnableAsset", &NetBindMarkerComponent::m_networkSpawnableAsset);
|
||||
}
|
||||
}
|
||||
|
||||
AzFramework::Spawnable* GetSpawnableFromAsset(AZ::Data::Asset<AzFramework::Spawnable>& asset)
|
||||
{
|
||||
AzFramework::Spawnable* spawnable = asset.GetAs<AzFramework::Spawnable>();
|
||||
if (!spawnable)
|
||||
{
|
||||
asset =
|
||||
AZ::Data::AssetManager::Instance().GetAsset<AzFramework::Spawnable>(asset.GetId(), AZ::Data::AssetLoadBehavior::PreLoad);
|
||||
AZ::Data::AssetManager::Instance().BlockUntilLoadComplete(asset);
|
||||
|
||||
spawnable = asset.GetAs<AzFramework::Spawnable>();
|
||||
}
|
||||
|
||||
return spawnable;
|
||||
}
|
||||
|
||||
|
||||
void NetBindMarkerComponent::Activate()
|
||||
{
|
||||
const auto agentType = AZ::Interface<IMultiplayer>::Get()->GetAgentType();
|
||||
const bool spawnImmediately =
|
||||
(agentType == MultiplayerAgentType::ClientServer || agentType == MultiplayerAgentType::DedicatedServer);
|
||||
|
||||
if (spawnImmediately && m_networkSpawnableAsset.GetId().IsValid())
|
||||
{
|
||||
AZ::Transform worldTm = GetEntity()->FindComponent<AzFramework::TransformComponent>()->GetWorldTM();
|
||||
auto preInsertionCallback =
|
||||
[worldTm = AZStd::move(worldTm), netEntityIndex = m_netEntityIndex, spawnableAssetId = m_networkSpawnableAsset.GetId()]
|
||||
(AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableEntityContainerView entities)
|
||||
{
|
||||
if (entities.size() == 1)
|
||||
{
|
||||
AZ::Entity* netEntity = *entities.begin();
|
||||
|
||||
auto* transformComponent = netEntity->FindComponent<AzFramework::TransformComponent>();
|
||||
transformComponent->SetWorldTM(worldTm);
|
||||
|
||||
AZ::Name spawnableName = AZ::Interface<INetworkSpawnableLibrary>::Get()->GetSpawnableNameFromAssetId(spawnableAssetId);
|
||||
PrefabEntityId prefabEntityId;
|
||||
prefabEntityId.m_prefabName = spawnableName;
|
||||
prefabEntityId.m_entityOffset = static_cast<uint32_t>(netEntityIndex);
|
||||
AZ::Interface<INetworkEntityManager>::Get()->SetupNetEntity(netEntity, prefabEntityId, NetEntityRole::Authority);
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error("NetBindMarkerComponent", false, "Requested to spawn 1 entity, but received %d", entities.size());
|
||||
}
|
||||
};
|
||||
|
||||
m_netSpawnTicket = AzFramework::EntitySpawnTicket(m_networkSpawnableAsset);
|
||||
AzFramework::SpawnEntitiesOptionalArgs optionalArgs;
|
||||
optionalArgs.m_preInsertionCallback = AZStd::move(preInsertionCallback);
|
||||
AzFramework::SpawnableEntitiesInterface::Get()->SpawnEntities(
|
||||
m_netSpawnTicket, { m_netEntityIndex }, AZStd::move(optionalArgs));
|
||||
}
|
||||
}
|
||||
|
||||
void NetBindMarkerComponent::Deactivate()
|
||||
{
|
||||
if(m_netSpawnTicket.IsValid())
|
||||
{
|
||||
AzFramework::SpawnableEntitiesInterface::Get()->DespawnAllEntities(m_netSpawnTicket);
|
||||
}
|
||||
}
|
||||
|
||||
size_t NetBindMarkerComponent::GetNetEntityIndex() const
|
||||
{
|
||||
return m_netEntityIndex;
|
||||
}
|
||||
|
||||
void NetBindMarkerComponent::SetNetEntityIndex(size_t netEntityIndex)
|
||||
{
|
||||
m_netEntityIndex = netEntityIndex;
|
||||
}
|
||||
|
||||
void NetBindMarkerComponent::SetNetworkSpawnableAsset(AZ::Data::Asset<AzFramework::Spawnable> networkSpawnableAsset)
|
||||
{
|
||||
m_networkSpawnableAsset = networkSpawnableAsset;
|
||||
}
|
||||
|
||||
AZ::Data::Asset<AzFramework::Spawnable> NetBindMarkerComponent::GetNetworkSpawnableAsset() const
|
||||
{
|
||||
return m_networkSpawnableAsset;
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user