From 588127772c0fd9fbb30310ee402b8dab042fbf23 Mon Sep 17 00:00:00 2001 From: mnaumov Date: Mon, 14 Jun 2021 12:40:19 -0700 Subject: [PATCH 1/6] Fixing missing preview in texture settings --- .../Code/Source/Processing/ImageConvertJob.cpp | 11 ++--------- .../Code/Source/Processing/ImageConvertJob.h | 1 - 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvertJob.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvertJob.cpp index c6f0d3946a..fab8d47885 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvertJob.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvertJob.cpp @@ -109,14 +109,7 @@ namespace ImageProcessingAtom while (!m_process->IsFinished() && !IsJobCancelled()) { m_process->UpdateProcess(); - if (m_isPreview) - { - m_output->SetProgress(m_process->GetProgress() / static_cast(m_previewProcessStep)); - } - else - { - m_output->SetProgress(m_process->GetProgress()); - } + m_output->SetProgress(m_process->GetProgress() / static_cast(m_previewProcessStep)); } IImageObjectPtr outputImage = m_process->GetOutputImage(); @@ -125,7 +118,7 @@ namespace ImageProcessingAtom m_output->SetOutputImage(outputImage, ImageConvertOutput::Base); m_output->SetOutputImage(outputImageAlpha, ImageConvertOutput::Alpha); - if (m_isPreview && !IsJobCancelled()) + if (!IsJobCancelled()) { // For preview, combine image output with alpha if any m_output->SetProgress(1.0f / static_cast(m_previewProcessStep)); diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvertJob.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvertJob.h index a0cf3ad488..17e6191314 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvertJob.h +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvertJob.h @@ -72,7 +72,6 @@ namespace ImageProcessingAtom static const int m_previewProcessStep = 2; AZStd::unique_ptr m_process; - bool m_isPreview; AZStd::atomic_bool m_isCancelled; ImageConvertOutput* m_output; }; From 8485a93612b45a01770874543b2cd6c1c8b639de Mon Sep 17 00:00:00 2001 From: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com> Date: Mon, 14 Jun 2021 16:30:54 -0400 Subject: [PATCH 2/6] Correcting the issue with processing of prefabs in Multiplayer enabled projects --- Gems/Multiplayer/Code/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Gems/Multiplayer/Code/CMakeLists.txt b/Gems/Multiplayer/Code/CMakeLists.txt index 430fe5ca4b..992d39c418 100644 --- a/Gems/Multiplayer/Code/CMakeLists.txt +++ b/Gems/Multiplayer/Code/CMakeLists.txt @@ -123,6 +123,8 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS) BUILD_DEPENDENCIES PRIVATE Gem::Multiplayer.Builders.Static + RUNTIME_DEPENDENCIES + Gem::Multiplayer.Editor ) ly_add_target( From 12c7391bd7e538d4d8af43ce8368444435f6654a Mon Sep 17 00:00:00 2001 From: yuriy0 Date: Mon, 14 Jun 2021 18:29:19 -0400 Subject: [PATCH 3/6] Bug fix: correct RTTI for AttributeInvocable (#1004) * Bug fix: correct RTTI for AttributeInvocable * Allow test case to crash if RTTI is wrong * Revert "Allow test case to crash if RTTI is wrong" Based on PR feedback, this change adds no value to the test and is confusing. This reverts commit 6c36065c3759d857cc16ab011d09167261181141. * Remove perhaps confusing comments and add a more to the point comment Co-authored-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> --- .../AzCore/AzCore/RTTI/ReflectContext.h | 2 +- Code/Framework/AzCore/Tests/Serialization.cpp | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/Code/Framework/AzCore/AzCore/RTTI/ReflectContext.h b/Code/Framework/AzCore/AzCore/RTTI/ReflectContext.h index e03823888a..5b27cb9f88 100644 --- a/Code/Framework/AzCore/AzCore/RTTI/ReflectContext.h +++ b/Code/Framework/AzCore/AzCore/RTTI/ReflectContext.h @@ -274,7 +274,7 @@ namespace AZ { using Callable = AZStd::conditional_t::value, AZStd::function::function_type>, Invocable>; public: - AZ_RTTI((AttributeInvocable, "{60D5804F-9AF4-4EB1-8F5A-62AFB4883F9D}"), AZ::Attribute); + AZ_RTTI((AttributeInvocable, "{60D5804F-9AF4-4EB1-8F5A-62AFB4883F9D}", Invocable), AZ::Attribute); AZ_CLASS_ALLOCATOR(AttributeInvocable, SystemAllocator, 0); template explicit AttributeInvocable(CallableType&& invocable) diff --git a/Code/Framework/AzCore/Tests/Serialization.cpp b/Code/Framework/AzCore/Tests/Serialization.cpp index 036942f9dd..4dba972324 100644 --- a/Code/Framework/AzCore/Tests/Serialization.cpp +++ b/Code/Framework/AzCore/Tests/Serialization.cpp @@ -2010,6 +2010,28 @@ TEST_F(SerializeBasicTest, BasicTypeTest_Succeed) } + + /* + This test will dynamic cast (azrtti_cast) between incompatible types, which should always result in nullptr. + If this test fails, the RTTI declaration for the relevant type is incorrect. + */ + TEST_F(Serialization, AttributeRTTI) + { + { + AttributeInvocable> fn([](AZStd::string x) { return x + x; }); + Attribute* fnDownCast = &fn; + auto fnUpCast = azrtti_cast>*>(fnDownCast); + EXPECT_EQ(fnUpCast, nullptr); + } + + { + AttributeFunction fn([](AZStd::string x) { return x + x; }); + Attribute* fnDownCast = &fn; + auto fnUpCast = azrtti_cast*>(fnDownCast); + EXPECT_EQ(fnUpCast, nullptr); + } + } + /* * Deprecation */ From e22d21f4b686e0091892af2356ba0b10b235bcc8 Mon Sep 17 00:00:00 2001 From: yuriy0 Date: Mon, 14 Jun 2021 21:28:50 -0400 Subject: [PATCH 4/6] Expose Actor bounding box configurations to component serialize and edit contexts. (#491) * Expose Actor bounding box configurations to component serialize and edit contexts. Allows one, for example, to have an animation which moves the character far away from the static bounds, and not have that character dissapear when the static bounds are outside of the camera frustum * Apply suggestions from code review * Bug fix: name the parameter, place comments in the intended place. --- .../Integration/Components/ActorComponent.cpp | 59 ++++++++++++++++++- .../Integration/Components/ActorComponent.h | 21 +++++++ .../Components/EditorActorComponent.cpp | 52 +++++++++++++++- .../Editor/Components/EditorActorComponent.h | 2 + 4 files changed, 132 insertions(+), 2 deletions(-) diff --git a/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.cpp b/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.cpp index b0065708fb..52977c9920 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.cpp +++ b/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.cpp @@ -57,14 +57,68 @@ namespace EMotionFX } }; + ////////////////////////////////////////////////////////////////////////// + void ActorComponent::BoundingBoxConfiguration::Set(ActorInstance* actor) const + { + if (m_autoUpdateBounds) + { + actor->SetupAutoBoundsUpdate(m_updateTimeFrequency, m_boundsType, m_updateItemFrequency); + } + else + { + actor->SetBoundsUpdateType(m_boundsType); + actor->SetBoundsUpdateEnabled(false); + } + } + + void ActorComponent::BoundingBoxConfiguration::SetAndUpdate(ActorInstance* actor) const + { + Set(actor); + const AZ::u32 freq = actor->GetBoundsUpdateEnabled() ? actor->GetBoundsUpdateItemFrequency() : 1; + actor->UpdateBounds(0, actor->GetBoundsUpdateType(), freq); + } + + void ActorComponent::BoundingBoxConfiguration::Reflect(AZ::ReflectContext * context) + { + if (auto* serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(2, [](AZ::SerializeContext& sc, AZ::SerializeContext::DataElementNode& node) + { + if (node.GetVersion() < 2) + { + // m_boundsType used to be an enum class with `int' underlying type, is now `u8' + static const char* m_boundsType_name = "m_boundsType"; + static AZ::Crc32 m_boundsType_nameCrc(m_boundsType_name); + + int m_boundsType_as_int; + if (!node.GetChildData(m_boundsType_nameCrc, m_boundsType_as_int)) + { + return false; + } + if (!node.RemoveElementByName(m_boundsType_nameCrc)) return false; + if (node.AddElementWithData(sc, m_boundsType_name, (AZ::u8)m_boundsType_as_int) == -1) return false; + } + return true; + }) + ->Field("m_boundsType", &BoundingBoxConfiguration::m_boundsType) + ->Field("m_autoUpdateBounds", &BoundingBoxConfiguration::m_autoUpdateBounds) + ->Field("m_updateTimeFrequency", &BoundingBoxConfiguration::m_updateTimeFrequency) + ->Field("m_updateItemFrequency", &BoundingBoxConfiguration::m_updateItemFrequency) + ; + } + } + ////////////////////////////////////////////////////////////////////////// void ActorComponent::Configuration::Reflect(AZ::ReflectContext* context) { + BoundingBoxConfiguration::Reflect(context); + auto* serializeContext = azrtti_cast(context); if (serializeContext) { serializeContext->Class() - ->Version(3) + ->Version(4) ->Field("ActorAsset", &Configuration::m_actorAsset) ->Field("MaterialPerLOD", &Configuration::m_materialPerLOD) ->Field("RenderSkeleton", &Configuration::m_renderSkeleton) @@ -74,6 +128,7 @@ namespace EMotionFX ->Field("AttachmentTarget", &Configuration::m_attachmentTarget) ->Field("SkinningMethod", &Configuration::m_skinningMethod) ->Field("LODLevel", &Configuration::m_lodLevel) + ->Field("BoundingBoxConfig", &Configuration::m_bboxConfig) ->Field("ForceJointsUpdateOOV", &Configuration::m_forceUpdateJointsOOV) ; } @@ -349,6 +404,8 @@ namespace EMotionFX AZ::TransformNotificationBus::MultiHandler::BusConnect(GetEntityId()); m_actorInstance->UpdateWorldTransform(); + // Set bounds update mode and compute bbox first time + m_configuration.m_bboxConfig.SetAndUpdate(m_actorInstance.get()); m_actorInstance->UpdateBounds(0, ActorInstance::EBoundsType::BOUNDS_STATIC_BASED); // Creating the render actor AFTER both actor asset and mesh asset loaded. diff --git a/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.h b/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.h index 4172ce63bc..b3e02f0cbd 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.h +++ b/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.h @@ -49,6 +49,26 @@ namespace EMotionFX AZ_COMPONENT(ActorComponent, "{BDC97E7F-A054-448B-A26F-EA2B5D78E377}"); friend class EditorActorComponent; + struct BoundingBoxConfiguration + { + AZ_TYPE_INFO(BoundingBoxConfiguration, "{EBCFF975-00A5-4578-85C7-59909F52067C}"); + + BoundingBoxConfiguration() = default; + + EMotionFX::ActorInstance::EBoundsType m_boundsType = EMotionFX::ActorInstance::BOUNDS_STATIC_BASED; + bool m_autoUpdateBounds = true; + float m_updateTimeFrequency = 0.f; + AZ::u32 m_updateItemFrequency = 1; + + // Set the bounding box configuration of the given actor instance to the parameters given by `this'. The actor instance must not be null (this is not checked). + void Set(ActorInstance* inst) const; + + // Set the bounding box configuration, then update the bounds of the actor instance + void SetAndUpdate(ActorInstance* inst) const; + + static void Reflect(AZ::ReflectContext* context); + }; + /** * Configuration struct for procedural configuration of Actor Components. */ @@ -71,6 +91,7 @@ namespace EMotionFX // default, joints level update (beside the root joint) on // actor are disabled when the actor is out of view. bool m_forceUpdateJointsOOV = false; + BoundingBoxConfiguration m_bboxConfig; ///< Configuration for bounding box type and updates static void Reflect(AZ::ReflectContext* context); }; diff --git a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp index 5c085e96f7..157e8c0136 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp +++ b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp @@ -58,11 +58,49 @@ namespace EMotionFX ->Field("SkinningMethod", &EditorActorComponent::m_skinningMethod) ->Field("UpdateJointTransformsWhenOutOfView", &EditorActorComponent::m_forceUpdateJointsOOV) ->Field("LodLevel", &EditorActorComponent::m_lodLevel) + ->Field("BBoxConfig", &EditorActorComponent::m_bboxConfig) ; AZ::EditContext* editContext = serializeContext->GetEditContext(); if (editContext) { + editContext->Class("Actor Bounding Box Config", "") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + + ->DataElement(AZ::Edit::UIHandlers::ComboBox, &ActorComponent::BoundingBoxConfiguration::m_boundsType, + "Bounds type", + "The method used to compute the Actor bounding box. NOTE: ordered by least expensive to compute to most expensive to compute." + ) + ->EnumAttribute(ActorInstance::BOUNDS_STATIC_BASED, "Static bounds (source-asset bounds)") + ->EnumAttribute(ActorInstance::BOUNDS_NODE_BASED, "Bone position-based") + ->EnumAttribute(ActorInstance::BOUNDS_NODEOBB_BASED, "Bone local bounding box-based") + ->EnumAttribute(ActorInstance::BOUNDS_MESH_BASED, "Render mesh vertex position-based (VERY EXPENSIVE)") + + ->DataElement(0, &ActorComponent::BoundingBoxConfiguration::m_autoUpdateBounds, + "Automatically update bounds?", + "If true, bounds are automatically updated based on some frequency. Otherwise bounds are computed only at creation or when triggered manually" + ) + ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::AttributesAndValues) + + ->DataElement(0, &ActorComponent::BoundingBoxConfiguration::m_updateTimeFrequency, + "Update frequency", + "How often to update bounds automatically" + ) + ->Attribute(AZ::Edit::Attributes::Suffix, " Hz") + ->Attribute(AZ::Edit::Attributes::Min, 0.f) + ->Attribute(AZ::Edit::Attributes::Step, 0.001f) + ->Attribute(AZ::Edit::Attributes::Visibility, &ActorComponent::BoundingBoxConfiguration::m_autoUpdateBounds) + + ->DataElement(0, &ActorComponent::BoundingBoxConfiguration::m_updateItemFrequency, + "Update item skip factor", + "How many items (bones or vertices) to skip when automatically updating bounds." + "
i.e. =1 uses every single item, =2 uses every 2nd item, =3 uses every 3rd item... " + ) + ->Attribute(AZ::Edit::Attributes::Suffix, " items") + ->Attribute(AZ::Edit::Attributes::Min, (AZ::u32)1) + ->Attribute(AZ::Edit::Attributes::Visibility, &ActorComponent::BoundingBoxConfiguration::m_autoUpdateBounds) + ; + editContext->Class("Actor", "The Actor component manages an instance of an Actor") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Category, "Animation") @@ -118,6 +156,9 @@ namespace EMotionFX ->DataElement(0, &EditorActorComponent::m_forceUpdateJointsOOV, "Force update joints", "Force update the joint transforms of actor, even when the character is out of the camera view.") + ->DataElement(0, &EditorActorComponent::m_bboxConfig, + "Bounding box configuration", "") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorActorComponent::OnBBoxConfigChanged) ; } } @@ -433,6 +474,14 @@ namespace EMotionFX return refreshLevel; } + void EditorActorComponent::OnBBoxConfigChanged() + { + if (m_actorInstance) + { + m_bboxConfig.SetAndUpdate(m_actorInstance.get()); + } + } + void EditorActorComponent::LaunchAnimationEditor(const AZ::Data::AssetId& assetId, const AZ::Data::AssetType&) { // call to open must be done before LoadCharacter @@ -571,6 +620,7 @@ namespace EMotionFX cfg.m_attachmentJointIndex = m_attachmentJointIndex; cfg.m_lodLevel = m_lodLevel; cfg.m_skinningMethod = m_skinningMethod; + cfg.m_bboxConfig = m_bboxConfig; cfg.m_forceUpdateJointsOOV = m_forceUpdateJointsOOV; gameEntity->AddComponent(aznew ActorComponent(&cfg)); @@ -846,6 +896,7 @@ namespace EMotionFX // Force an update of node transforms so we can get an accurate bounding box. m_actorInstance->UpdateTransformations(0.0f, true, false); + OnBBoxConfigChanged(); // Apply BBox config // Creating the render actor AFTER both actor asset and mesh asset loaded. RenderBackend* renderBackend = AZ::Interface::Get()->GetRenderBackend(); @@ -898,4 +949,3 @@ namespace EMotionFX } } //namespace Integration } // namespace EMotionFX - diff --git a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.h b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.h index 5dabbe4ada..30bf0d405e 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.h +++ b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.h @@ -121,6 +121,7 @@ namespace EMotionFX AZ::Crc32 OnAttachmentTypeChanged(); AZ::Crc32 OnAttachmentTargetChanged(); AZ::Crc32 OnAttachmentTargetJointSelect(); + void OnBBoxConfigChanged(); bool AttachmentTargetVisibility(); bool AttachmentTargetJointVisibility(); AZStd::string AttachmentJointButtonText(); @@ -163,6 +164,7 @@ namespace EMotionFX AZStd::string m_attachmentJointName; ///< Joint name on target to which to attach (if ActorAttachment). AZ::u32 m_attachmentJointIndex; AZ::u32 m_lodLevel; + ActorComponent::BoundingBoxConfiguration m_bboxConfig; bool m_forceUpdateJointsOOV = false; // \todo attachmentTarget node nr From aade48e7513bd91cc7e71f678ee3cb286702ceec Mon Sep 17 00:00:00 2001 From: yuriy0 Date: Tue, 15 Jun 2021 13:36:35 -0400 Subject: [PATCH 5/6] Allow smaller values and increments for editor camera speed, allow custom speed values --- Code/Sandbox/Editor/ViewportTitleDlg.cpp | 3 ++- Code/Sandbox/Editor/ViewportTitleDlg.h | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Code/Sandbox/Editor/ViewportTitleDlg.cpp b/Code/Sandbox/Editor/ViewportTitleDlg.cpp index 48075597cd..2c4b29773d 100644 --- a/Code/Sandbox/Editor/ViewportTitleDlg.cpp +++ b/Code/Sandbox/Editor/ViewportTitleDlg.cpp @@ -181,7 +181,8 @@ void CViewportTitleDlg::SetupCameraDropdownMenu() auto comboBoxTextChanged = static_cast(&QComboBox::currentTextChanged); SetSpeedComboBox(cameraMoveSpeed); - m_cameraSpeed->setInsertPolicy(QComboBox::NoInsert); + m_cameraSpeed->setInsertPolicy(QComboBox::InsertAtBottom); + m_cameraSpeed->setDuplicatesEnabled(false); connect(m_cameraSpeed, comboBoxTextChanged, this, &CViewportTitleDlg::OnUpdateMoveSpeedText); connect(m_cameraSpeed->lineEdit(), &QLineEdit::returnPressed, this, &CViewportTitleDlg::OnSpeedComboBoxEnter); diff --git a/Code/Sandbox/Editor/ViewportTitleDlg.h b/Code/Sandbox/Editor/ViewportTitleDlg.h index 255354dcbb..7978277ce0 100644 --- a/Code/Sandbox/Editor/ViewportTitleDlg.h +++ b/Code/Sandbox/Editor/ViewportTitleDlg.h @@ -117,11 +117,11 @@ protected: // Speed combobox/lineEdit settings double m_minSpeed = 0.01; double m_maxSpeed = 100.0; - double m_speedStep = 0.01; + double m_speedStep = 0.001; int m_numDecimals = 3; // Speed presets - float m_speedPresetValues[3] = { 0.1f, 1.0f, 10.0f }; + float m_speedPresetValues[4] = { 0.01f, 0.1f, 1.0f, 10.0f }; double m_fieldWidthMultiplier = 1.8; From a9c55c1070dff2fbfd7c0c5d5afc03a40362bca2 Mon Sep 17 00:00:00 2001 From: yuriy0 Date: Tue, 15 Jun 2021 14:09:30 -0400 Subject: [PATCH 6/6] Update actor render bounding box (#991) * Extend MeshFeatureProcessor to allow changing the mesh bbox, which requires re-compute the culling data for that mesh * Update actor mesh bbox when EMFX actor instance bbox changes. Also use the actor instance global bbox to compute the local bbox for the skinned render mesh, instead of the using the static bounds based bbox, as not every actor instance is going to be using the static bounds bbox. * Store per-instance mesh AABB in the right place. In the MeshInstanceData, which is unique per instance, instead of in the Model, which is shared between all instances. For greater clarity, also remove Model::m_aabb and the corresponding getter and setter, as it isn't immediately obvious whether this gets the model asset bbox or the mesh instance bbox. Callers should instead be explicit about which bbox they want. * Bug fix: model asset is not necessarily ready in AcquireMesh * Remove now-unused forward declaration * Update MockMeshFeatureProcessor with SetLocalAabb/GetLocalAabb --- .../Atom/Feature/Mesh/MeshFeatureProcessor.h | 5 ++++ .../Mesh/MeshFeatureProcessorInterface.h | 4 +++ .../Code/Mocks/MockMeshFeatureProcessor.h | 2 ++ .../Code/Source/Mesh/MeshFeatureProcessor.cpp | 30 +++++++++++++++++-- .../Include/Atom/RPI.Public/Model/Model.h | 5 +--- .../Code/Source/RPI.Public/Model/Model.cpp | 16 +++++----- .../Source/RPI.Public/Model/ModelLodUtils.cpp | 2 +- .../Source/Mesh/MeshComponentController.cpp | 6 ++-- .../Code/Source/AtomActorInstance.cpp | 12 ++++++-- 9 files changed, 62 insertions(+), 20 deletions(-) diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessor.h index 0d61ef82d1..24c0658c61 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessor.h @@ -95,6 +95,8 @@ namespace AZ TransformServiceFeatureProcessorInterface::ObjectId m_objectId; + Aabb m_aabb = Aabb::CreateNull(); + bool m_cullBoundsNeedsUpdate = false; bool m_cullableNeedsRebuild = false; bool m_objectSrgNeedsUpdate = true; @@ -160,6 +162,9 @@ namespace AZ Transform GetTransform(const MeshHandle& meshHandle) override; Vector3 GetNonUniformScale(const MeshHandle& meshHandle) override; + void SetLocalAabb(const MeshHandle& meshHandle, const AZ::Aabb& localAabb) override; + AZ::Aabb GetLocalAabb(const MeshHandle& meshHandle) const override; + void SetSortKey(const MeshHandle& meshHandle, RHI::DrawItemSortKey sortKey) override; RHI::DrawItemSortKey GetSortKey(const MeshHandle& meshHandle) override; diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessorInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessorInterface.h index fb5bff5584..fdc6ea3cc8 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessorInterface.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessorInterface.h @@ -86,6 +86,10 @@ namespace AZ virtual Transform GetTransform(const MeshHandle& meshHandle) = 0; //! Gets the non-uniform scale for a given mesh handle. virtual Vector3 GetNonUniformScale(const MeshHandle& meshHandle) = 0; + //! Sets the local space bbox for a given mesh handle. You don't need to call this for static models, only skinned/animated models + virtual void SetLocalAabb(const MeshHandle& meshHandle, const AZ::Aabb& localAabb) = 0; + //! Gets the local space bbox for a given mesh handle. Unless SetLocalAabb has been called before, this will be the bbox of the model asset + virtual AZ::Aabb GetLocalAabb(const MeshHandle& meshHandle) const = 0; //! Sets the sort key for a given mesh handle. virtual void SetSortKey(const MeshHandle& meshHandle, RHI::DrawItemSortKey sortKey) = 0; //! Gets the sort key for a given mesh handle. diff --git a/Gems/Atom/Feature/Common/Code/Mocks/MockMeshFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Mocks/MockMeshFeatureProcessor.h index 418ee0cfb8..e6635906e9 100644 --- a/Gems/Atom/Feature/Common/Code/Mocks/MockMeshFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Mocks/MockMeshFeatureProcessor.h @@ -33,6 +33,8 @@ namespace UnitTest MOCK_METHOD2(SetMaterialAssignmentMap, void(const MeshHandle&, const AZ::Render::MaterialAssignmentMap&)); MOCK_METHOD1(GetTransform, AZ::Transform(const MeshHandle&)); MOCK_METHOD1(GetNonUniformScale, AZ::Vector3(const MeshHandle&)); + MOCK_METHOD2(SetLocalAabb, void(const MeshHandle&, const AZ::Aabb&)); + MOCK_CONST_METHOD1(GetLocalAabb, AZ::Aabb(const MeshHandle&)); MOCK_METHOD2(SetSortKey, void (const MeshHandle&, AZ::RHI::DrawItemSortKey)); MOCK_METHOD1(GetSortKey, AZ::RHI::DrawItemSortKey(const MeshHandle&)); MOCK_METHOD2(SetLodOverride, void(const MeshHandle&, AZ::RPI::Cullable::LodOverride)); diff --git a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp index b087269089..6fe6cf1f88 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp @@ -304,6 +304,30 @@ namespace AZ } } + void MeshFeatureProcessor::SetLocalAabb(const MeshHandle& meshHandle, const AZ::Aabb& localAabb) + { + if (meshHandle.IsValid()) + { + MeshDataInstance& meshData = *meshHandle; + meshData.m_aabb = localAabb; + meshData.m_cullBoundsNeedsUpdate = true; + meshData.m_objectSrgNeedsUpdate = true; + } + }; + + AZ::Aabb MeshFeatureProcessor::GetLocalAabb(const MeshHandle& meshHandle) const + { + if (meshHandle.IsValid()) + { + return meshHandle->m_aabb; + } + else + { + AZ_Assert(false, "Invalid mesh handle"); + return Aabb::CreateNull(); + } + } + Transform MeshFeatureProcessor::GetTransform(const MeshHandle& meshHandle) { if (meshHandle.IsValid()) @@ -603,6 +627,8 @@ namespace AZ SetRayTracingData(); } + m_aabb = model->GetModelAsset()->GetAabb(); + m_cullableNeedsRebuild = true; m_cullBoundsNeedsUpdate = true; m_objectSrgNeedsUpdate = true; @@ -996,7 +1022,7 @@ namespace AZ RPI::Cullable::CullData& cullData = m_cullable.m_cullData; RPI::Cullable::LodData& lodData = m_cullable.m_lodData; - const Aabb& localAabb = m_model->GetAabb(); + const Aabb& localAabb = m_aabb; lodData.m_lodSelectionRadius = 0.5f*localAabb.GetExtents().GetMaxElement(); const size_t modelLodCount = m_model->GetLodCount(); @@ -1077,7 +1103,7 @@ namespace AZ Vector3 center; float radius; - Aabb localAabb = m_model->GetAabb(); + Aabb localAabb = m_aabb; localAabb.MultiplyByScale(nonUniformScale); localAabb.GetTransformedAabb(localToWorld).GetAsSphere(center, radius); diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/Model.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/Model.h index 514e3e37a5..b403a86f00 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/Model.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/Model.h @@ -32,6 +32,7 @@ namespace AZ : public Data::InstanceData { friend class ModelSystem; + public: AZ_INSTANCE_DATA(Model, "{C30F5522-B381-4B38-BBAF-6E0B1885C8B9}"); AZ_CLASS_ALLOCATOR(Model, AZ::SystemAllocator, 0); @@ -53,8 +54,6 @@ namespace AZ //! Returns whether a buffer upload is pending. bool IsUploadPending() const; - const AZ::Aabb& GetAabb() const; - const Data::Asset& GetModelAsset() const; //! Checks a ray for intersection against this model. The ray must be in the same coordinate space as the model. @@ -105,8 +104,6 @@ namespace AZ // Tracks whether buffers have all been streamed up to the GPU. bool m_isUploadPending = false; - - AZ::Aabb m_aabb; }; } // namespace RPI } // namespace AZ diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Model/Model.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Model/Model.cpp index 17ff2c64c8..8809225150 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Model/Model.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Model/Model.cpp @@ -62,8 +62,6 @@ namespace AZ { AZ_PROFILE_FUNCTION(Debug::ProfileCategory::AzRender); - m_aabb = modelAsset.GetAabb(); - m_lods.resize(modelAsset.GetLodAssets().size()); for (size_t lodIndex = 0; lodIndex < m_lods.size(); ++lodIndex) @@ -127,11 +125,6 @@ namespace AZ return m_isUploadPending; } - const AZ::Aabb& Model::GetAabb() const - { - return m_aabb; - } - const Data::Asset& Model::GetModelAsset() const { return m_modelAsset; @@ -140,9 +133,16 @@ namespace AZ bool Model::LocalRayIntersection(const AZ::Vector3& rayStart, const AZ::Vector3& rayDir, float& distanceNormalized, AZ::Vector3& normal) const { AZ_PROFILE_FUNCTION(Debug::ProfileCategory::AzRender); + + if (!GetModelAsset()) + { + AZ_Assert(false, "Invalid Model - not created from a ModelAsset?"); + return false; + } + float start; float end; - const int result = Intersect::IntersectRayAABB2(rayStart, rayDir.GetReciprocal(), m_aabb, start, end); + const int result = Intersect::IntersectRayAABB2(rayStart, rayDir.GetReciprocal(), GetModelAsset()->GetAabb(), start, end); if (Intersect::ISECT_RAY_AABB_NONE != result) { if (ModelAsset* modelAssetPtr = m_modelAsset.Get()) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLodUtils.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLodUtils.cpp index b213e84bca..a4e5d1ba10 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLodUtils.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLodUtils.cpp @@ -49,7 +49,7 @@ namespace AZ With that percentage we can determine which Lod we want to use. */ - Aabb modelAabb = model.GetAabb(); + Aabb modelAabb = model.GetModelAsset()->GetAabb(); modelAabb.Translate(position); Vector3 center; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp index e7eecd3c7f..90d3763067 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp @@ -460,10 +460,10 @@ namespace AZ Aabb MeshComponentController::GetLocalBounds() { - const Data::Instance model = GetModel(); - if (model) + if (m_meshHandle.IsValid() && m_meshFeatureProcessor) { - Aabb aabb = model->GetAabb(); + Aabb aabb = m_meshFeatureProcessor->GetLocalAabb(m_meshHandle); + aabb.MultiplyByScale(m_cachedNonUniformScale); return aabb; } diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp index 532c8720b5..ff4b82ce92 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp @@ -83,12 +83,20 @@ namespace AZ void AtomActorInstance::UpdateBounds() { // Update RenderActorInstance world bounding box - // The bounding box is moving with the actor instance. It is static in the way that it does not change shape. + // The bounding box is moving with the actor instance. // The entity and actor transforms are kept in sync already. m_worldAABB = AZ::Aabb::CreateFromMinMax(m_actorInstance->GetAABB().GetMin(), m_actorInstance->GetAABB().GetMax()); // Update RenderActorInstance local bounding box - m_localAABB = AZ::Aabb::CreateFromMinMax(m_actorInstance->GetStaticBasedAABB().GetMin(), m_actorInstance->GetStaticBasedAABB().GetMax()); + // NB: computing the local bbox from the world bbox makes the local bbox artifically larger than it should be + // instead EMFX should support getting the local bbox from the actor instance directly + m_localAABB = m_worldAABB.GetTransformedAabb(m_transformInterface->GetWorldTM().GetInverse()); + + // Update bbox on mesh instance if it exists + if (m_meshFeatureProcessor && m_meshHandle && m_meshHandle->IsValid() && m_skinnedMeshInstance) + { + m_meshFeatureProcessor->SetLocalAabb(*m_meshHandle, m_localAABB); + } AZ::Interface::Get()->RefreshEntityLocalBoundsUnion(m_entityId); }