Ensure render geometry is refreshed when the mesh component controller is moved (#6452)
* ensure render geometry is refreshed when the mesh component controller is moved Signed-off-by: Tom Hulton-Harrop <82228511+hultonha@users.noreply.github.com> * cache bus pointer and send update notification when mesh changes Signed-off-by: Tom Hulton-Harrop <82228511+hultonha@users.noreply.github.com> * add integration test to detect MeshComponentController notification to IntersectionNotificationBus Signed-off-by: Tom Hulton-Harrop <82228511+hultonha@users.noreply.github.com> * remove optimize off Signed-off-by: Tom Hulton-Harrop <82228511+hultonha@users.noreply.github.com> * update build visibility for AtomLyIntegration editor static lib Signed-off-by: Tom Hulton-Harrop <82228511+hultonha@users.noreply.github.com> * move runtime dependencies to gem module Signed-off-by: Tom Hulton-Harrop <82228511+hultonha@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
a623e52331
commit
5940982244
@@ -5,25 +5,23 @@
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Component/EntityId.h>
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/Math/Aabb.h>
|
||||
|
||||
#include <AzFramework/Entity/EntityContextBus.h>
|
||||
#include <AzFramework/Render/GeometryIntersectionBus.h>
|
||||
#include <AzFramework/Render/GeometryIntersectionStructures.h>
|
||||
#include <AzFramework/Render/IntersectorInterface.h>
|
||||
#include <AzFramework/Render/GeometryIntersectionBus.h>
|
||||
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
|
||||
namespace RenderGeometry
|
||||
{
|
||||
//! Implementation of IntersectorBus interface, this class contains a cached AABB list
|
||||
//! of the connected entities to the intersection bus and calculates performant ray intersections against them
|
||||
//! Implementation of IntersectorBus interface, this class contains a cached AABB list of the connected
|
||||
//! entities to the intersection bus and calculates efficient ray intersections against them.
|
||||
class Intersector final
|
||||
: public IntersectorBus::Handler
|
||||
, protected IntersectionNotificationBus::Handler
|
||||
@@ -34,32 +32,32 @@ namespace AzFramework
|
||||
Intersector(AzFramework::EntityContextId contextId);
|
||||
~Intersector();
|
||||
|
||||
// IntersectorBus
|
||||
// IntersectorBus overrides ...
|
||||
RayResult RayIntersect(const RayRequest& ray) override;
|
||||
|
||||
// IntersectionNotifications
|
||||
// IntersectionNotificationBus overrides ...
|
||||
void OnEntityConnected(AZ::EntityId entityId) override;
|
||||
void OnEntityDisconnected(AZ::EntityId entityId) override;
|
||||
void OnGeometryChanged(AZ::EntityId entityId) override;
|
||||
|
||||
private:
|
||||
|
||||
struct EntityData
|
||||
{
|
||||
EntityData(AZ::EntityId id, AZ::Aabb aabb)
|
||||
: m_id(id)
|
||||
, m_aabb(aabb)
|
||||
, m_ref(1) {}
|
||||
, m_ref(1)
|
||||
{
|
||||
}
|
||||
|
||||
AZ::EntityId m_id;
|
||||
AZ::Aabb m_aabb;
|
||||
int m_ref;
|
||||
};
|
||||
|
||||
|
||||
class EntityDataList
|
||||
{
|
||||
public:
|
||||
|
||||
int AddRef(AZ::EntityId entityId);
|
||||
int RemoveRef(AZ::EntityId entityId);
|
||||
void Update(const EntityData& newData);
|
||||
@@ -88,5 +86,5 @@ namespace AzFramework
|
||||
AZStd::vector<AZStd::pair<const EntityData*, float>> m_candidatesSortedByDist;
|
||||
AzFramework::EntityContextId m_contextId;
|
||||
};
|
||||
}
|
||||
}
|
||||
} // namespace RenderGeometry
|
||||
} // namespace AzFramework
|
||||
|
||||
@@ -77,8 +77,7 @@ ly_create_alias(NAME AtomLyIntegration_CommonFeatures.Servers NAMESPACE Gem
|
||||
|
||||
if(PAL_TRAIT_BUILD_HOST_TOOLS)
|
||||
ly_add_target(
|
||||
NAME AtomLyIntegration_CommonFeatures.Editor GEM_MODULE
|
||||
|
||||
NAME AtomLyIntegration_CommonFeatures.Editor.Static STATIC
|
||||
NAMESPACE Gem
|
||||
AUTOUIC
|
||||
AUTOMOC
|
||||
@@ -98,7 +97,7 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS)
|
||||
PRIVATE
|
||||
ATOMLYINTEGRATION_FEATURE_COMMON_EDITOR
|
||||
BUILD_DEPENDENCIES
|
||||
PRIVATE
|
||||
PUBLIC
|
||||
Gem::AtomLyIntegration_CommonFeatures.Static
|
||||
Gem::Atom_RPI.Edit
|
||||
Gem::AtomToolsFramework.Static
|
||||
@@ -108,6 +107,24 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS)
|
||||
Legacy::Editor.Headers
|
||||
Legacy::EditorCommon
|
||||
Legacy::CryCommon
|
||||
)
|
||||
|
||||
ly_add_target(
|
||||
NAME AtomLyIntegration_CommonFeatures.Editor GEM_MODULE
|
||||
NAMESPACE Gem
|
||||
FILES_CMAKE
|
||||
atomlyintegration_commonfeatures_shared_files.cmake
|
||||
INCLUDE_DIRECTORIES
|
||||
PRIVATE
|
||||
Source
|
||||
PUBLIC
|
||||
Include
|
||||
COMPILE_DEFINITIONS
|
||||
PRIVATE
|
||||
ATOMLYINTEGRATION_FEATURE_COMMON_EDITOR
|
||||
BUILD_DEPENDENCIES
|
||||
PRIVATE
|
||||
Gem::AtomLyIntegration_CommonFeatures.Editor.Static
|
||||
RUNTIME_DEPENDENCIES
|
||||
Gem::Atom_RPI.Editor
|
||||
Gem::Atom_Feature_Common.Editor
|
||||
@@ -128,6 +145,33 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS)
|
||||
Gem::AtomLyIntegration_CommonFeatures.Editor
|
||||
Gem::GradientSignal.Tools
|
||||
)
|
||||
|
||||
################################################################################
|
||||
# Tests
|
||||
################################################################################
|
||||
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
ly_add_target(
|
||||
NAME AtomLyIntegration_CommonFeatures.Editor.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
|
||||
NAMESPACE Gem
|
||||
FILES_CMAKE
|
||||
atomlyintegration_commonfeatures_editor_test_files.cmake
|
||||
INCLUDE_DIRECTORIES
|
||||
PRIVATE
|
||||
Tests
|
||||
Source
|
||||
BUILD_DEPENDENCIES
|
||||
PRIVATE
|
||||
AZ::AzTest
|
||||
AZ::AzTestShared
|
||||
AZ::AzToolsFramework
|
||||
AZ::AzToolsFrameworkTestCommon
|
||||
Gem::AtomLyIntegration_CommonFeatures.Static
|
||||
Gem::AtomLyIntegration_CommonFeatures.Editor.Static
|
||||
)
|
||||
ly_add_googletest(
|
||||
NAME Gem::AtomLyIntegration_CommonFeatures.Editor.Tests
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# AtomLyIntegration_CommonFeatures gem targets are required as part of the Editor and AssetProcessor
|
||||
|
||||
+26
-10
@@ -82,7 +82,6 @@ namespace AZ
|
||||
->Field("MinimumScreenCoverage", &MeshComponentConfig::m_minimumScreenCoverage)
|
||||
->Field("QualityDecayRate", &MeshComponentConfig::m_qualityDecayRate);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool MeshComponentConfig::IsAssetSet()
|
||||
@@ -225,33 +224,42 @@ namespace AZ
|
||||
{
|
||||
}
|
||||
|
||||
static AzFramework::EntityContextId FindOwningContextId(const AZ::EntityId entityId)
|
||||
{
|
||||
AzFramework::EntityContextId contextId = AzFramework::EntityContextId::CreateNull();
|
||||
AzFramework::EntityIdContextQueryBus::EventResult(
|
||||
contextId, entityId, &AzFramework::EntityIdContextQueries::GetOwningContextId);
|
||||
return contextId;
|
||||
}
|
||||
|
||||
void MeshComponentController::Activate(const AZ::EntityComponentIdPair& entityComponentIdPair)
|
||||
{
|
||||
const AZ::EntityId entityId = entityComponentIdPair.GetEntityId();
|
||||
m_entityComponentIdPair = entityComponentIdPair;
|
||||
|
||||
m_transformInterface = TransformBus::FindFirstHandler(entityId);
|
||||
AZ_Warning("MeshComponentController", m_transformInterface, "Unable to attach to a TransformBus handler. This mesh will always be rendered at the origin.");
|
||||
AZ_Warning(
|
||||
"MeshComponentController", m_transformInterface,
|
||||
"Unable to attach to a TransformBus handler. This mesh will always be rendered at the origin.");
|
||||
|
||||
m_meshFeatureProcessor = RPI::Scene::GetFeatureProcessorForEntity<MeshFeatureProcessorInterface>(entityId);
|
||||
AZ_Error("MeshComponentController", m_meshFeatureProcessor, "Unable to find a MeshFeatureProcessorInterface on the entityId.");
|
||||
|
||||
m_cachedNonUniformScale = AZ::Vector3::CreateOne();
|
||||
AZ::NonUniformScaleRequestBus::EventResult(m_cachedNonUniformScale, entityId, &AZ::NonUniformScaleRequests::GetScale);
|
||||
AZ::NonUniformScaleRequestBus::Event(entityId, &AZ::NonUniformScaleRequests::RegisterScaleChangedEvent,
|
||||
m_nonUniformScaleChangedHandler);
|
||||
AZ::NonUniformScaleRequestBus::Event(
|
||||
entityId, &AZ::NonUniformScaleRequests::RegisterScaleChangedEvent, m_nonUniformScaleChangedHandler);
|
||||
|
||||
const auto entityContextId = FindOwningContextId(entityId);
|
||||
MeshComponentRequestBus::Handler::BusConnect(entityId);
|
||||
TransformNotificationBus::Handler::BusConnect(entityId);
|
||||
MaterialReceiverRequestBus::Handler::BusConnect(entityId);
|
||||
MaterialComponentNotificationBus::Handler::BusConnect(entityId);
|
||||
AzFramework::BoundsRequestBus::Handler::BusConnect(entityId);
|
||||
AzFramework::EntityContextId contextId;
|
||||
AzFramework::EntityIdContextQueryBus::EventResult(
|
||||
contextId, entityId, &AzFramework::EntityIdContextQueries::GetOwningContextId);
|
||||
AzFramework::RenderGeometry::IntersectionRequestBus::Handler::BusConnect({entityId, contextId});
|
||||
AzFramework::RenderGeometry::IntersectionRequestBus::Handler::BusConnect({ entityId, entityContextId });
|
||||
AzFramework::RenderGeometry::IntersectionNotificationBus::Bind(m_intersectionNotificationBus, entityContextId);
|
||||
|
||||
//Buses must be connected before RegisterModel in case requests are made as a result of HandleModelChange
|
||||
// Buses must be connected before RegisterModel in case requests are made as a result of HandleModelChange
|
||||
RegisterModel();
|
||||
}
|
||||
|
||||
@@ -291,6 +299,11 @@ namespace AZ
|
||||
{
|
||||
m_meshFeatureProcessor->SetTransform(m_meshHandle, world, m_cachedNonUniformScale);
|
||||
}
|
||||
|
||||
// ensure the render geometry is kept in sync with any changes to the entity the mesh is on
|
||||
AzFramework::RenderGeometry::IntersectionNotificationBus::Event(
|
||||
m_intersectionNotificationBus, &AzFramework::RenderGeometry::IntersectionNotificationBus::Events::OnGeometryChanged,
|
||||
m_entityComponentIdPair.GetEntityId());
|
||||
}
|
||||
|
||||
void MeshComponentController::HandleNonUniformScaleChange(const AZ::Vector3& nonUniformScale)
|
||||
@@ -301,7 +314,7 @@ namespace AZ
|
||||
m_meshFeatureProcessor->SetTransform(m_meshHandle, m_transformInterface->GetWorldTM(), m_cachedNonUniformScale);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
RPI::ModelMaterialSlotMap MeshComponentController::GetModelMaterialSlots() const
|
||||
{
|
||||
Data::Asset<const RPI::ModelAsset> modelAsset = GetModelAsset();
|
||||
@@ -369,6 +382,9 @@ namespace AZ
|
||||
MeshComponentNotificationBus::Event(entityId, &MeshComponentNotificationBus::Events::OnModelReady, m_configuration.m_modelAsset, model);
|
||||
MaterialReceiverNotificationBus::Event(entityId, &MaterialReceiverNotificationBus::Events::OnMaterialAssignmentsChanged);
|
||||
AZ::Interface<AzFramework::IEntityBoundsUnion>::Get()->RefreshEntityLocalBoundsUnion(entityId);
|
||||
AzFramework::RenderGeometry::IntersectionNotificationBus::Event(
|
||||
m_intersectionNotificationBus, &AzFramework::RenderGeometry::IntersectionNotificationBus::Events::OnGeometryChanged,
|
||||
m_entityComponentIdPair.GetEntityId());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -162,6 +162,8 @@ namespace AZ
|
||||
bool m_isVisible = true;
|
||||
MeshComponentConfig m_configuration;
|
||||
AZ::Vector3 m_cachedNonUniformScale = AZ::Vector3::CreateOne();
|
||||
//! Cached bus to use to notify RenderGeometry::Intersector the entity/component has changed.
|
||||
AzFramework::RenderGeometry::IntersectionNotificationBus::BusPtr m_intersectionNotificationBus;
|
||||
|
||||
MeshFeatureProcessorInterface::ModelChangedEvent::Handler m_changeEventHandler
|
||||
{
|
||||
@@ -173,6 +175,5 @@ namespace AZ
|
||||
[&](const AZ::Vector3& nonUniformScale) { HandleNonUniformScaleChange(nonUniformScale); }
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace Render
|
||||
} // namespace AZ
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
#include <AzTest/AzTest.h>
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
class AtomLyIntegrationHook : public AZ::Test::ITestEnvironment
|
||||
{
|
||||
public:
|
||||
void SetupEnvironment() override
|
||||
{
|
||||
AZ::AllocatorInstance<AZ::SystemAllocator>::Create();
|
||||
}
|
||||
|
||||
void TeardownEnvironment() override
|
||||
{
|
||||
AZ::AllocatorInstance<AZ::SystemAllocator>::Destroy();
|
||||
}
|
||||
};
|
||||
|
||||
// required to support running integration tests with Qt
|
||||
AZTEST_EXPORT int AZ_UNIT_TEST_HOOK_NAME(int argc, char** argv)
|
||||
{
|
||||
::testing::InitGoogleMock(&argc, argv);
|
||||
QApplication app(argc, argv);
|
||||
AZ::Test::printUnusedParametersWarning(argc, argv);
|
||||
AZ::Test::addTestEnvironments({ DEFAULT_UNIT_TEST_ENV, new AtomLyIntegrationHook });
|
||||
int result = RUN_ALL_TESTS();
|
||||
return result;
|
||||
}
|
||||
|
||||
IMPLEMENT_TEST_EXECUTABLE_MAIN();
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* 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 <AzTest/AzTest.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
|
||||
#include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
|
||||
|
||||
#include <Mesh/EditorMeshComponent.h>
|
||||
#include <Mesh/MeshComponentController.h>
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
static AzFramework::EntityContextId FindOwningContextId(const AZ::EntityId entityId)
|
||||
{
|
||||
AzFramework::EntityContextId contextId = AzFramework::EntityContextId::CreateNull();
|
||||
AzFramework::EntityIdContextQueryBus::EventResult(contextId, entityId, &AzFramework::EntityIdContextQueries::GetOwningContextId);
|
||||
return contextId;
|
||||
}
|
||||
|
||||
class IntersectionNotificationDetector : public AzFramework::RenderGeometry::IntersectionNotificationBus::Handler
|
||||
{
|
||||
public:
|
||||
void Connect(const AzFramework::EntityContextId& entityContextId);
|
||||
void Disconnect();
|
||||
|
||||
// IntersectionNotificationBus overrides ...
|
||||
void OnEntityConnected(AZ::EntityId entityId) override;
|
||||
void OnEntityDisconnected(AZ::EntityId entityId) override;
|
||||
void OnGeometryChanged(AZ::EntityId entityId) override;
|
||||
|
||||
AZ::EntityId m_lastEntityIdChanged;
|
||||
};
|
||||
|
||||
void IntersectionNotificationDetector::Connect(const AzFramework::EntityContextId& entityContextId)
|
||||
{
|
||||
AzFramework::RenderGeometry::IntersectionNotificationBus::Handler::BusConnect(entityContextId);
|
||||
}
|
||||
|
||||
void IntersectionNotificationDetector::Disconnect()
|
||||
{
|
||||
AzFramework::RenderGeometry::IntersectionNotificationBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
void IntersectionNotificationDetector::OnEntityConnected([[maybe_unused]] AZ::EntityId entityId)
|
||||
{
|
||||
}
|
||||
|
||||
void IntersectionNotificationDetector::OnEntityDisconnected([[maybe_unused]] AZ::EntityId entityId)
|
||||
{
|
||||
}
|
||||
|
||||
void IntersectionNotificationDetector::OnGeometryChanged(AZ::EntityId entityId)
|
||||
{
|
||||
m_lastEntityIdChanged = entityId;
|
||||
}
|
||||
|
||||
class MeshComponentControllerFixture : public ToolsApplicationFixture
|
||||
{
|
||||
public:
|
||||
void SetUpEditorFixtureImpl() override
|
||||
{
|
||||
m_meshComponentDescriptor = AZStd::unique_ptr<AZ::ComponentDescriptor>(AZ::Render::MeshComponent::CreateDescriptor());
|
||||
m_meshComponentDescriptor->Reflect(GetApplication()->GetSerializeContext());
|
||||
|
||||
m_editorMeshComponentDescriptor =
|
||||
AZStd::unique_ptr<AZ::ComponentDescriptor>(AZ::Render::EditorMeshComponent::CreateDescriptor());
|
||||
m_editorMeshComponentDescriptor->Reflect(GetApplication()->GetSerializeContext());
|
||||
|
||||
m_entityId1 = CreateDefaultEditorEntity("Entity1");
|
||||
m_entityIds.push_back(m_entityId1);
|
||||
|
||||
m_intersectionNotificationDetector.Connect(FindOwningContextId(m_entityId1));
|
||||
}
|
||||
|
||||
void TearDownEditorFixtureImpl() override
|
||||
{
|
||||
bool entityDestroyed = false;
|
||||
AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult(
|
||||
entityDestroyed, &AzToolsFramework::EditorEntityContextRequestBus::Events::DestroyEditorEntity, m_entityId1);
|
||||
|
||||
m_intersectionNotificationDetector.Disconnect();
|
||||
|
||||
m_meshComponentDescriptor.reset();
|
||||
m_editorMeshComponentDescriptor.reset();
|
||||
}
|
||||
|
||||
AZ::EntityId m_entityId1;
|
||||
AzToolsFramework::EntityIdList m_entityIds;
|
||||
AZStd::unique_ptr<AZ::ComponentDescriptor> m_meshComponentDescriptor;
|
||||
AZStd::unique_ptr<AZ::ComponentDescriptor> m_editorMeshComponentDescriptor;
|
||||
IntersectionNotificationDetector m_intersectionNotificationDetector;
|
||||
};
|
||||
|
||||
TEST_F(MeshComponentControllerFixture, IntersectionNotificationBusIsNotifiedWhenMeshComponentControllerTransformIsModified)
|
||||
{
|
||||
auto* entity1 = AzToolsFramework::GetEntityById(m_entityId1);
|
||||
entity1->Deactivate();
|
||||
entity1->CreateComponent<AZ::Render::EditorMeshComponent>();
|
||||
// note: RPI::Scene::GetFeatureProcessorForEntity<MeshFeatureProcessorInterface>(...) returns nullptr
|
||||
// and so m_meshFeatureProcessor is null
|
||||
AZ_TEST_START_TRACE_SUPPRESSION;
|
||||
entity1->Activate();
|
||||
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
|
||||
|
||||
AZ::TransformBus::Event(
|
||||
m_entityId1, &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3(1.0f, 2.0f, 3.0f)));
|
||||
|
||||
using ::testing::Eq;
|
||||
EXPECT_THAT(m_entityId1, Eq(m_intersectionNotificationDetector.m_lastEntityIdChanged));
|
||||
}
|
||||
} // namespace UnitTest
|
||||
-1
@@ -10,7 +10,6 @@ set(FILES
|
||||
Include/AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentNotificationBus.h
|
||||
Include/AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentRequestBus.h
|
||||
Include/AtomLyIntegration/CommonFeatures/ReflectionProbe/EditorReflectionProbeBus.h
|
||||
Source/Module.cpp
|
||||
Source/Animation/EditorAttachmentComponent.h
|
||||
Source/Animation/EditorAttachmentComponent.cpp
|
||||
Source/EditorCommonFeaturesSystemComponent.h
|
||||
|
||||
+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
|
||||
#
|
||||
#
|
||||
|
||||
set(FILES
|
||||
Tests/Main.cpp
|
||||
Tests/MeshComponentControllerTests.cpp)
|
||||
Reference in New Issue
Block a user