diff --git a/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.cpp b/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.cpp index 74133997e4..b033e46a87 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.cpp +++ b/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.cpp @@ -58,27 +58,32 @@ namespace EMotionFX }; ////////////////////////////////////////////////////////////////////////// - void ActorComponent::BoundingBoxConfiguration::Set(ActorInstance* actor) const + void ActorComponent::BoundingBoxConfiguration::Set(ActorInstance* actorInstance) const { + actorInstance->SetExpandBoundsBy(m_expandBy * 0.01f); // Normalize percentage for internal use. (1% == 0.01f) + if (m_autoUpdateBounds) { - actor->SetupAutoBoundsUpdate(m_updateTimeFrequency, m_boundsType, m_updateItemFrequency); + actorInstance->SetupAutoBoundsUpdate(m_updateTimeFrequency, m_boundsType, m_updateItemFrequency); } else { - actor->SetBoundsUpdateType(m_boundsType); - actor->SetBoundsUpdateEnabled(false); + actorInstance->SetBoundsUpdateType(m_boundsType); + actorInstance->SetBoundsUpdateEnabled(false); } } - void ActorComponent::BoundingBoxConfiguration::SetAndUpdate(ActorInstance* actor) const + void ActorComponent::BoundingBoxConfiguration::SetAndUpdate(ActorInstance* actorInstance) const { - Set(actor); - const AZ::u32 freq = actor->GetBoundsUpdateEnabled() ? actor->GetBoundsUpdateItemFrequency() : 1; - actor->UpdateBounds(0, actor->GetBoundsUpdateType(), freq); + Set(actorInstance); + + const AZ::u32 updateFrequency = actorInstance->GetBoundsUpdateEnabled() ? actorInstance->GetBoundsUpdateItemFrequency() : 1; + const ActorInstance::EBoundsType boundUpdateType = actorInstance->GetBoundsUpdateType(); + + actorInstance->UpdateBounds(actorInstance->GetLODLevel(), boundUpdateType, updateFrequency); } - void ActorComponent::BoundingBoxConfiguration::Reflect(AZ::ReflectContext * context) + void ActorComponent::BoundingBoxConfiguration::Reflect(AZ::ReflectContext* context) { if (auto* serializeContext = azrtti_cast(context)) { @@ -105,10 +110,26 @@ namespace EMotionFX ->Field("m_autoUpdateBounds", &BoundingBoxConfiguration::m_autoUpdateBounds) ->Field("m_updateTimeFrequency", &BoundingBoxConfiguration::m_updateTimeFrequency) ->Field("m_updateItemFrequency", &BoundingBoxConfiguration::m_updateItemFrequency) + ->Field("expandBy", &BoundingBoxConfiguration::m_expandBy) ; } } + AZ::Crc32 ActorComponent::BoundingBoxConfiguration::GetVisibilityAutoUpdate() const + { + return m_boundsType != EMotionFX::ActorInstance::BOUNDS_STATIC_BASED ? AZ::Edit::PropertyVisibility::Show : AZ::Edit::PropertyVisibility::Hide; + } + + AZ::Crc32 ActorComponent::BoundingBoxConfiguration::GetVisibilityAutoUpdateSettings() const + { + if (m_boundsType == EMotionFX::ActorInstance::BOUNDS_STATIC_BASED || m_autoUpdateBounds == false) + { + return AZ::Edit::PropertyVisibility::Hide; + } + + return AZ::Edit::PropertyVisibility::Show; + } + ////////////////////////////////////////////////////////////////////////// void ActorComponent::Configuration::Reflect(AZ::ReflectContext* context) { diff --git a/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.h b/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.h index 0f36846a50..2bf85692be 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.h +++ b/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.h @@ -45,24 +45,29 @@ namespace EMotionFX AZ_COMPONENT(ActorComponent, "{BDC97E7F-A054-448B-A26F-EA2B5D78E377}"); friend class EditorActorComponent; - struct BoundingBoxConfiguration + class BoundingBoxConfiguration { + public: 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; + EMotionFX::ActorInstance::EBoundsType m_boundsType = EMotionFX::ActorInstance::BOUNDS_STATIC_BASED; + float m_expandBy = 25.0f; ///< Expand the bounding volume by the given percentage. + bool m_autoUpdateBounds = true; + float m_updateTimeFrequency = 0.0f; + 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 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* actorInstance) const; // Set the bounding box configuration, then update the bounds of the actor instance - void SetAndUpdate(ActorInstance* inst) const; + void SetAndUpdate(ActorInstance* actorInstance) const; static void Reflect(AZ::ReflectContext* context); + + AZ::Crc32 GetVisibilityAutoUpdate() const; + AZ::Crc32 GetVisibilityAutoUpdateSettings() const; }; /** diff --git a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp index efc5cbd9a2..ed5333da9c 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp +++ b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp @@ -62,39 +62,40 @@ namespace EMotionFX { 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, + "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 (Recommended)") + ->EnumAttribute(ActorInstance::BOUNDS_NODE_BASED, "Bone position-based") + ->EnumAttribute(ActorInstance::BOUNDS_MESH_BASED, "Mesh vertex-based (Expensive)") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree) + ->DataElement(AZ::Edit::UIHandlers::Default, &ActorComponent::BoundingBoxConfiguration::m_expandBy, + "Expand by", + "Percentage that the calculated bounding box should be automatically expanded with. " + "This can be used to add a tolerance area to the calculated bounding box to avoid clipping the character too early. " + "A static bounding box together with the expansion is the recommended way for maximum performance. (Default = 25%)") + ->Attribute(AZ::Edit::Attributes::Suffix, " %") + ->Attribute(AZ::Edit::Attributes::Min, 0.0f) + ->DataElement(AZ::Edit::UIHandlers::Default, &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, + "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::EntireTree) + ->Attribute(AZ::Edit::Attributes::Visibility, &ActorComponent::BoundingBoxConfiguration::GetVisibilityAutoUpdate) + ->DataElement(AZ::Edit::UIHandlers::Default, &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, + "How often to update bounds automatically") + ->Attribute(AZ::Edit::Attributes::Suffix, " Hz") + ->Attribute(AZ::Edit::Attributes::Min, 0.0f) + ->Attribute(AZ::Edit::Attributes::Max, FLT_MAX) + ->Attribute(AZ::Edit::Attributes::Step, 0.1f) + ->Attribute(AZ::Edit::Attributes::Visibility, &ActorComponent::BoundingBoxConfiguration::GetVisibilityAutoUpdateSettings) + ->DataElement(AZ::Edit::UIHandlers::Default, &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) + "
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::GetVisibilityAutoUpdateSettings) ; editContext->Class("Actor", "The Actor component manages an instance of an Actor")