Legacy Mesh component removal
* Removed legacy components * More legacy render component removal * Starting removal of legacy mesh component dependencies * Removed old light components that were allowing Atom test to succeed * Testing increasing the timeout to see if it lets it pass in Jenkins * put original timeout back * reordered components to test if it is component specific or not * Testing disabiling the test to see if we get a green * Fixed the removal of the test to sandbox * Removed Legacy Mesh Component and associated tendrils * Removed some missed references * Fixed some issues with unity builds and ambiguous naming * Addressed review feedback
This commit is contained in:
@@ -0,0 +1,355 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#include "AttachmentComponent.h"
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <MathConversion.h>
|
||||
#include <LmbrCentral/Rendering/MeshAsset.h>
|
||||
#include <LmbrCentral/Animation/AttachmentComponentBus.h>
|
||||
#include <LmbrCentral/Animation/SkeletalHierarchyRequestBus.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Render
|
||||
{
|
||||
/// Behavior Context handler for AttachmentComponentNotificationBus
|
||||
class BehaviorAttachmentComponentNotificationBusHandler : public LmbrCentral::AttachmentComponentNotificationBus::Handler,
|
||||
public AZ::BehaviorEBusHandler
|
||||
{
|
||||
public:
|
||||
AZ_EBUS_BEHAVIOR_BINDER(
|
||||
BehaviorAttachmentComponentNotificationBusHandler, "{636B95A0-5C7D-4EE7-8645-955665315451}", AZ::SystemAllocator,
|
||||
OnAttached, OnDetached);
|
||||
|
||||
void OnAttached(AZ::EntityId id) override
|
||||
{
|
||||
Call(FN_OnAttached, id);
|
||||
}
|
||||
|
||||
void OnDetached(AZ::EntityId id) override
|
||||
{
|
||||
Call(FN_OnDetached, id);
|
||||
}
|
||||
};
|
||||
|
||||
void AttachmentConfiguration::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
|
||||
if (serializeContext)
|
||||
{
|
||||
serializeContext->Class<AttachmentConfiguration>()
|
||||
->Version(1)
|
||||
->Field("Target ID", &AttachmentConfiguration::m_targetId)
|
||||
->Field("Target Bone Name", &AttachmentConfiguration::m_targetBoneName)
|
||||
->Field("Target Offset", &AttachmentConfiguration::m_targetOffset)
|
||||
->Field("Attached Initially", &AttachmentConfiguration::m_attachedInitially)
|
||||
->Field("Scale Source", &AttachmentConfiguration::m_scaleSource);
|
||||
}
|
||||
AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context);
|
||||
if (behaviorContext)
|
||||
{
|
||||
behaviorContext->EBus<LmbrCentral::AttachmentComponentRequestBus>("AttachmentComponentRequestBus")
|
||||
->Event("Attach", &LmbrCentral::AttachmentComponentRequestBus::Events::Attach)
|
||||
->Event("Detach", &LmbrCentral::AttachmentComponentRequestBus::Events::Detach)
|
||||
->Event("SetAttachmentOffset", &LmbrCentral::AttachmentComponentRequestBus::Events::SetAttachmentOffset);
|
||||
|
||||
behaviorContext->EBus<LmbrCentral::AttachmentComponentNotificationBus>("AttachmentComponentNotificationBus")
|
||||
->Handler<BehaviorAttachmentComponentNotificationBusHandler>();
|
||||
}
|
||||
}
|
||||
|
||||
void AttachmentComponent::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
AttachmentConfiguration::Reflect(context);
|
||||
|
||||
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
|
||||
if (serializeContext)
|
||||
{
|
||||
serializeContext->Class<AttachmentComponent, AZ::Component>()->Version(1)->Field(
|
||||
"Configuration", &AttachmentComponent::m_initialConfiguration);
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// BoneFollower
|
||||
//=========================================================================
|
||||
|
||||
void BoneFollower::Activate(AZ::Entity* owner, const AttachmentConfiguration& configuration, bool targetCanAnimate)
|
||||
{
|
||||
AZ_Assert(owner, "owner is required");
|
||||
AZ_Assert(!m_ownerId.IsValid(), "BoneFollower is already Activated");
|
||||
|
||||
m_ownerId = owner->GetId();
|
||||
m_targetCanAnimate = targetCanAnimate;
|
||||
m_isUpdatingOwnerTransform = false;
|
||||
m_scaleSource = configuration.m_scaleSource;
|
||||
|
||||
m_cachedOwnerTransform = AZ::Transform::CreateIdentity();
|
||||
EBUS_EVENT_ID_RESULT(m_cachedOwnerTransform, m_ownerId, AZ::TransformBus, GetWorldTM);
|
||||
|
||||
if (configuration.m_attachedInitially)
|
||||
{
|
||||
Attach(configuration.m_targetId, configuration.m_targetBoneName.c_str(), configuration.m_targetOffset);
|
||||
}
|
||||
|
||||
LmbrCentral::AttachmentComponentRequestBus::Handler::BusConnect(m_ownerId);
|
||||
}
|
||||
|
||||
void BoneFollower::Deactivate()
|
||||
{
|
||||
AZ_Assert(m_ownerId.IsValid(), "BoneFollower was never Activated");
|
||||
|
||||
LmbrCentral::AttachmentComponentRequestBus::Handler::BusDisconnect();
|
||||
Detach();
|
||||
m_ownerId.SetInvalid();
|
||||
}
|
||||
|
||||
AZ::EntityId BoneFollower::GetTargetEntityId()
|
||||
{
|
||||
return m_targetId;
|
||||
}
|
||||
|
||||
AZ::Transform BoneFollower::GetOffset()
|
||||
{
|
||||
return m_targetOffset;
|
||||
}
|
||||
|
||||
void BoneFollower::Attach(AZ::EntityId targetId, const char* targetBoneName, const AZ::Transform& offset)
|
||||
{
|
||||
AZ_Assert(m_ownerId.IsValid(), "BoneFollower must be Activated to use.")
|
||||
|
||||
// safe to try and detach, even if we weren't attached
|
||||
Detach();
|
||||
|
||||
if (!targetId.IsValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (targetId == m_ownerId)
|
||||
{
|
||||
AZ_Error("Attachment Component", false, "AttachmentComponent cannot target itself");
|
||||
return;
|
||||
}
|
||||
|
||||
// Note: the target entity may not be activated yet. That's ok.
|
||||
// When mesh is ready we are notified via MeshComponentEvents::OnModelReady
|
||||
// When transform is ready we are notified via TransformNotificationBus::OnTransformChanged
|
||||
|
||||
m_targetId = targetId;
|
||||
m_targetBoneName = targetBoneName;
|
||||
m_targetOffset = offset;
|
||||
|
||||
BindTargetBone();
|
||||
|
||||
m_targetBoneTransform = AZ::Transform::Identity();
|
||||
|
||||
m_isTargetEntityTransformKnown = false; // target's transform may not be available yet
|
||||
|
||||
AZ::TransformBus::EventResult(
|
||||
m_cachedOwnerTransform, m_ownerId, &AZ::TransformBus::Events::GetWorldTM); // owner query will always succeed
|
||||
|
||||
MeshComponentNotificationBus::Handler::BusConnect(m_targetId); // fires OnModelReady if asset is already ready
|
||||
AZ::TransformNotificationBus::Handler::BusConnect(m_targetId);
|
||||
if (m_targetCanAnimate)
|
||||
{
|
||||
// Only register for per-frame updates when target can animate
|
||||
AZ::TickBus::Handler::BusConnect();
|
||||
}
|
||||
|
||||
// update owner's transform
|
||||
UpdateOwnerTransformIfNecessary();
|
||||
|
||||
// alert others that we've attached
|
||||
LmbrCentral::AttachmentComponentNotificationBus::Event(m_targetId, &LmbrCentral::AttachmentComponentNotificationBus::Events::OnAttached, m_ownerId);
|
||||
}
|
||||
|
||||
void BoneFollower::Detach()
|
||||
{
|
||||
AZ_Assert(m_ownerId.IsValid(), "BoneFollower must be Activated to use.");
|
||||
|
||||
if (m_targetId.IsValid())
|
||||
{
|
||||
// alert others that we're detaching
|
||||
EBUS_EVENT_ID(m_targetId, LmbrCentral::AttachmentComponentNotificationBus, OnDetached, m_ownerId);
|
||||
|
||||
MeshComponentNotificationBus::Handler::BusDisconnect();
|
||||
AZ::TransformNotificationBus::Handler::BusDisconnect(m_targetId);
|
||||
AZ::TickBus::Handler::BusDisconnect();
|
||||
|
||||
m_targetId.SetInvalid();
|
||||
}
|
||||
}
|
||||
|
||||
const char* BoneFollower::GetJointName()
|
||||
{
|
||||
return m_targetBoneName.c_str();
|
||||
}
|
||||
|
||||
void BoneFollower::SetAttachmentOffset(const AZ::Transform& offset)
|
||||
{
|
||||
AZ_Assert(m_ownerId.IsValid(), "BoneFollower must be Activated to use.");
|
||||
|
||||
if (m_targetId.IsValid())
|
||||
{
|
||||
m_targetOffset = offset;
|
||||
UpdateOwnerTransformIfNecessary();
|
||||
}
|
||||
}
|
||||
|
||||
void BoneFollower::OnModelReady([[maybe_unused]] const AZ::Data::Asset<AZ::RPI::ModelAsset>& modelAsset, [[maybe_unused]] const AZ::Data::Instance<AZ::RPI::Model>& model)
|
||||
{
|
||||
// reset character values
|
||||
BindTargetBone();
|
||||
m_targetBoneTransform = QueryBoneTransform();
|
||||
|
||||
// move owner if necessary
|
||||
UpdateOwnerTransformIfNecessary();
|
||||
}
|
||||
|
||||
void BoneFollower::BindTargetBone()
|
||||
{
|
||||
m_targetBoneId = -1;
|
||||
LmbrCentral::SkeletalHierarchyRequestBus::EventResult(
|
||||
m_targetBoneId, m_targetId, &LmbrCentral::SkeletalHierarchyRequests::GetJointIndexByName, m_targetBoneName.c_str());
|
||||
}
|
||||
|
||||
void BoneFollower::UpdateOwnerTransformIfNecessary()
|
||||
{
|
||||
// Can't update until target entity's transform is known
|
||||
if (!m_isTargetEntityTransformKnown)
|
||||
{
|
||||
if (AZ::TransformBus::GetNumOfEventHandlers(m_targetId) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AZ::TransformBus::EventResult(m_targetEntityTransform, m_targetId, &AZ::TransformBus::Events::GetWorldTM);
|
||||
m_isTargetEntityTransformKnown = true;
|
||||
}
|
||||
|
||||
AZ::Transform finalTransform;
|
||||
if (m_scaleSource == AttachmentConfiguration::ScaleSource::WorldScale)
|
||||
{
|
||||
// apply offset in world-space
|
||||
finalTransform = m_targetEntityTransform * m_targetBoneTransform;
|
||||
finalTransform.SetScale(AZ::Vector3::CreateOne());
|
||||
finalTransform *= m_targetOffset;
|
||||
}
|
||||
else if (m_scaleSource == AttachmentConfiguration::ScaleSource::TargetEntityScale)
|
||||
{
|
||||
// apply offset in target-entity-space (ignoring bone scale)
|
||||
AZ::Transform boneNoScale = m_targetBoneTransform;
|
||||
boneNoScale.SetScale(AZ::Vector3::CreateOne());
|
||||
|
||||
finalTransform = m_targetEntityTransform * boneNoScale * m_targetOffset;
|
||||
}
|
||||
else // AttachmentConfiguration::ScaleSource::TargetEntityScale
|
||||
{
|
||||
// apply offset in target-bone-space
|
||||
finalTransform = m_targetEntityTransform * m_targetBoneTransform * m_targetOffset;
|
||||
}
|
||||
|
||||
if (m_cachedOwnerTransform != finalTransform)
|
||||
{
|
||||
AZ_Warning(
|
||||
"Attachment Component", !m_isUpdatingOwnerTransform,
|
||||
"AttachmentComponent detected a cycle when updating transform, do not target child entities.");
|
||||
if (!m_isUpdatingOwnerTransform)
|
||||
{
|
||||
m_cachedOwnerTransform = finalTransform;
|
||||
m_isUpdatingOwnerTransform = true;
|
||||
EBUS_EVENT_ID(m_ownerId, AZ::TransformBus, SetWorldTM, finalTransform);
|
||||
m_isUpdatingOwnerTransform = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AZ::Transform BoneFollower::QueryBoneTransform() const
|
||||
{
|
||||
AZ::Transform boneTransform = AZ::Transform::CreateIdentity();
|
||||
|
||||
if (m_targetBoneId >= 0)
|
||||
{
|
||||
LmbrCentral::SkeletalHierarchyRequestBus::EventResult(
|
||||
boneTransform, m_targetId, &LmbrCentral::SkeletalHierarchyRequests::GetJointTransformCharacterRelative, m_targetBoneId);
|
||||
}
|
||||
|
||||
return boneTransform;
|
||||
}
|
||||
|
||||
// fires when target's transform changes
|
||||
void BoneFollower::OnTransformChanged(const AZ::Transform& /*local*/, const AZ::Transform& world)
|
||||
{
|
||||
m_targetEntityTransform = world;
|
||||
m_isTargetEntityTransformKnown = true;
|
||||
UpdateOwnerTransformIfNecessary();
|
||||
}
|
||||
|
||||
void BoneFollower::OnTick(float /*deltaTime*/, AZ::ScriptTimePoint /*time*/)
|
||||
{
|
||||
m_targetBoneTransform = QueryBoneTransform();
|
||||
UpdateOwnerTransformIfNecessary();
|
||||
}
|
||||
|
||||
int BoneFollower::GetTickOrder()
|
||||
{
|
||||
return AZ::TICK_ATTACHMENT;
|
||||
}
|
||||
|
||||
void BoneFollower::Reattach(bool detachFirst)
|
||||
{
|
||||
#ifdef AZ_ENABLE_TRACING
|
||||
AZ::Entity* ownerEntity = nullptr;
|
||||
AZ::Entity* targetEntity = nullptr;
|
||||
AZ::ComponentApplicationBus::BroadcastResult(ownerEntity, &AZ::ComponentApplicationBus::Events::FindEntity, m_ownerId);
|
||||
AZ::ComponentApplicationBus::BroadcastResult(targetEntity, &AZ::ComponentApplicationBus::Events::FindEntity, m_targetId);
|
||||
AZ_TracePrintf(
|
||||
"BoneFollower", "Reattaching entity '%s' to entity '%s'", ownerEntity ? ownerEntity->GetName().c_str() : "",
|
||||
targetEntity ? targetEntity->GetName().c_str() : "");
|
||||
#endif
|
||||
|
||||
if (m_targetId.IsValid() && detachFirst)
|
||||
{
|
||||
LmbrCentral::AttachmentComponentNotificationBus::Event(m_targetId, &LmbrCentral::AttachmentComponentNotificationBus::Events::OnDetached, m_ownerId);
|
||||
}
|
||||
|
||||
if (m_targetId != m_ownerId)
|
||||
{
|
||||
LmbrCentral::AttachmentComponentNotificationBus::Event(m_targetId, &LmbrCentral::AttachmentComponentNotificationBus::Events::OnAttached, m_ownerId);
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// AttachmentComponent
|
||||
//=========================================================================
|
||||
|
||||
void AttachmentComponent::Activate()
|
||||
{
|
||||
#ifdef AZ_ENABLE_TRACING
|
||||
bool isStaticTransform = false;
|
||||
AZ::TransformBus::EventResult(isStaticTransform, GetEntityId(), &AZ::TransformBus::Events::IsStaticTransform);
|
||||
AZ_Warning(
|
||||
"Attachment Component", !isStaticTransform, "Attachment needs to move, but entity '%s' %s has a static transform.",
|
||||
GetEntity()->GetName().c_str(), GetEntityId().ToString().c_str());
|
||||
#endif
|
||||
|
||||
m_boneFollower.Activate(GetEntity(), m_initialConfiguration, true);
|
||||
}
|
||||
|
||||
void AttachmentComponent::Deactivate()
|
||||
{
|
||||
m_boneFollower.Deactivate();
|
||||
}
|
||||
} // namespace Render
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <LmbrCentral/Animation/AttachmentComponentBus.h>
|
||||
#include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentBus.h>
|
||||
#include <AzCore/Component/Component.h>
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
#include <AzCore/Component/TickBus.h>
|
||||
#include <AzCore/Math/Transform.h>
|
||||
|
||||
struct ISkeletonPose;
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Render
|
||||
{
|
||||
/*!
|
||||
* Configuration data for AttachmentComponent.
|
||||
*/
|
||||
struct AttachmentConfiguration
|
||||
{
|
||||
AZ_TYPE_INFO(AttachmentConfiguration, "{74B5DC69-DE44-4640-836A-55339E116795}");
|
||||
|
||||
virtual ~AttachmentConfiguration() = default;
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
//! Attach to this entity.
|
||||
AZ::EntityId m_targetId;
|
||||
|
||||
//! Attach to this bone on target entity.
|
||||
AZStd::string m_targetBoneName;
|
||||
|
||||
//! Offset from target.
|
||||
AZ::Transform m_targetOffset = AZ::Transform::Identity();
|
||||
|
||||
//! Whether to attach to target upon activation.
|
||||
//! If false, the entity remains detached until Attach() is called.
|
||||
bool m_attachedInitially = true;
|
||||
|
||||
//! Source from which to retrieve scale information.
|
||||
enum class ScaleSource : AZ::u8
|
||||
{
|
||||
WorldScale, // Scaled in world space.
|
||||
TargetEntityScale, // Adopt scaling of attachment target entity.
|
||||
TargetBoneScale, // Adopt scaling of attachment target entity/joint.
|
||||
};
|
||||
ScaleSource m_scaleSource = ScaleSource::WorldScale;
|
||||
};
|
||||
|
||||
/*
|
||||
* Common functionality for game and editor attachment components.
|
||||
* The BoneFollower tracks movement of the target's bone and
|
||||
* updates the owning entity's TransformComponent to follow.
|
||||
* This class should be a member within the attachment component
|
||||
* and be activated/deactivated along with the component.
|
||||
* \ref AttachmentComponent
|
||||
*/
|
||||
class BoneFollower
|
||||
: public LmbrCentral::AttachmentComponentRequestBus::Handler
|
||||
, public AZ::TransformNotificationBus::Handler
|
||||
, public AZ::Render::MeshComponentNotificationBus::Handler
|
||||
, public AZ::Data::AssetBus::Handler
|
||||
, public AZ::TickBus::Handler
|
||||
{
|
||||
public:
|
||||
void Activate(AZ::Entity* owner, const AttachmentConfiguration& initialConfiguration, bool targetCanAnimate);
|
||||
void Deactivate();
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// AttachmentComponentRequests
|
||||
void Reattach(bool detachFirst);
|
||||
void Attach(AZ::EntityId targetId, const char* targetBoneName, const AZ::Transform& offset) override;
|
||||
void Detach() override;
|
||||
void SetAttachmentOffset(const AZ::Transform& offset) override;
|
||||
const char* GetJointName() override;
|
||||
AZ::EntityId GetTargetEntityId() override;
|
||||
AZ::Transform GetOffset() override;
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private:
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// AZ::TickBus
|
||||
//! Check target bone transform every frame.
|
||||
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
|
||||
//! Make sure target bone transform updates after animation update.
|
||||
int GetTickOrder() override;
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// AZ::TransformNotificationBus
|
||||
//! When target's transform changes
|
||||
void OnTransformChanged(const AZ::Transform& local, const AZ::Transform& world) override;
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// MeshComponentEvents
|
||||
//! When target's mesh changes
|
||||
void OnModelReady(const AZ::Data::Asset<AZ::RPI::ModelAsset>& modelAsset, const AZ::Data::Instance<AZ::RPI::Model>& model) override;
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void BindTargetBone();
|
||||
|
||||
AZ::Transform QueryBoneTransform() const;
|
||||
|
||||
void UpdateOwnerTransformIfNecessary();
|
||||
|
||||
//! Entity which which is being attached.
|
||||
AZ::EntityId m_ownerId;
|
||||
|
||||
//! Whether to query bone position per-frame (false while in editor)
|
||||
bool m_targetCanAnimate = false;
|
||||
|
||||
AZ::EntityId m_targetId;
|
||||
AZStd::string m_targetBoneName;
|
||||
AZ::Transform m_targetOffset; //!< local transform
|
||||
AZ::Transform m_targetBoneTransform; //!< local transform of bone
|
||||
AZ::Transform m_targetEntityTransform; //!< world transform of target
|
||||
bool m_isTargetEntityTransformKnown = false;
|
||||
|
||||
//! Cached value, so we don't update owner's position unnecessarily.
|
||||
AZ::Transform m_cachedOwnerTransform;
|
||||
bool m_isUpdatingOwnerTransform = false; //!< detect infinite loops when updating owner's transform
|
||||
|
||||
// Cached character values to avoid repeated lookup.
|
||||
// These are set by calling ResetCharacter()
|
||||
int m_targetBoneId; //!< negative when bone not found
|
||||
|
||||
AttachmentConfiguration::ScaleSource m_scaleSource;
|
||||
};
|
||||
|
||||
/*!
|
||||
* The AttachmentComponent lets an entity stick to a particular bone on
|
||||
* a target entity. This is achieved by tracking movement of the target's
|
||||
* bone and updating the entity's TransformComponent accordingly.
|
||||
*/
|
||||
class AttachmentComponent
|
||||
: public AZ::Component
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(AttachmentComponent, "{2D17A64A-7AC5-4C02-AC36-C5E8141FFDDF}");
|
||||
|
||||
friend class EditorAttachmentComponent;
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
|
||||
{
|
||||
provided.push_back(AZ_CRC("AttachmentService", 0x5aaa7b63));
|
||||
}
|
||||
|
||||
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
|
||||
{
|
||||
incompatible.push_back(AZ_CRC("AttachmentService", 0x5aaa7b63));
|
||||
}
|
||||
|
||||
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
|
||||
{
|
||||
required.push_back(AZ_CRC("TransformService", 0x8ee22c50));
|
||||
}
|
||||
|
||||
~AttachmentComponent() override = default;
|
||||
|
||||
private:
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// AZ::Component
|
||||
void Activate() override;
|
||||
void Deactivate() override;
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//! Initial configuration for m_attachment
|
||||
AttachmentConfiguration m_initialConfiguration;
|
||||
|
||||
//! Implements actual attachment functionality
|
||||
BoneFollower m_boneFollower;
|
||||
};
|
||||
} // namespace Render
|
||||
} // namespace AZ
|
||||
+242
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "EditorAttachmentComponent.h"
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
#include <AzCore/Math/Quaternion.h>
|
||||
#include <AzCore/Math/Transform.h>
|
||||
#include <LmbrCentral/Animation/SkeletalHierarchyRequestBus.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Render
|
||||
{
|
||||
void EditorAttachmentComponent::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
|
||||
if (serializeContext)
|
||||
{
|
||||
serializeContext->Class<EditorAttachmentComponent, EditorComponentBase>()
|
||||
->Version(1)
|
||||
->Field("Target ID", &EditorAttachmentComponent::m_targetId)
|
||||
->Field("Target Bone Name", &EditorAttachmentComponent::m_targetBoneName)
|
||||
->Field("Position Offset", &EditorAttachmentComponent::m_positionOffset)
|
||||
->Field("Rotation Offset", &EditorAttachmentComponent::m_rotationOffset)
|
||||
->Field("Scale Offset", &EditorAttachmentComponent::m_scaleOffset)
|
||||
->Field("Attached Initially", &EditorAttachmentComponent::m_attachedInitially)
|
||||
->Field("Scale Source", &EditorAttachmentComponent::m_scaleSource);
|
||||
|
||||
AZ::EditContext* editContext = serializeContext->GetEditContext();
|
||||
if (editContext)
|
||||
{
|
||||
editContext
|
||||
->Class<EditorAttachmentComponent>(
|
||||
"Attachment", "The Attachment component lets an entity attach to a bone on the skeleton of another entity")
|
||||
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
||||
->Attribute(AZ::Edit::Attributes::Category, "Animation")
|
||||
->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/Attachment.svg")
|
||||
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Attachment.png")
|
||||
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
||||
->Attribute(
|
||||
AZ::Edit::Attributes::HelpPageURL,
|
||||
"https://docs.aws.amazon.com/lumberyard/latest/userguide/component-attachment.html")
|
||||
->DataElement(0, &EditorAttachmentComponent::m_targetId, "Target entity", "Attach to this entity.")
|
||||
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorAttachmentComponent::OnTargetIdChanged)
|
||||
->DataElement(
|
||||
AZ::Edit::UIHandlers::ComboBox, &EditorAttachmentComponent::m_targetBoneName, "Joint name",
|
||||
"Attach to this joint on target entity.")
|
||||
->Attribute(AZ::Edit::Attributes::StringList, &EditorAttachmentComponent::GetTargetBoneOptions)
|
||||
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorAttachmentComponent::OnTargetBoneChanged)
|
||||
->DataElement(
|
||||
0, &EditorAttachmentComponent::m_positionOffset, "Position offset", "Local position offset from target bone")
|
||||
->Attribute(AZ::Edit::Attributes::Suffix, "m")
|
||||
->Attribute(AZ::Edit::Attributes::Step, 0.01f)
|
||||
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorAttachmentComponent::OnTargetOffsetChanged)
|
||||
->DataElement(
|
||||
0, &EditorAttachmentComponent::m_rotationOffset, "Rotation offset", "Local rotation offset from target bone")
|
||||
->Attribute(AZ::Edit::Attributes::Suffix, "deg")
|
||||
->Attribute(AZ::Edit::Attributes::Step, 0.01f)
|
||||
->Attribute(AZ::Edit::Attributes::Min, -AZ::RadToDeg(AZ::Constants::TwoPi))
|
||||
->Attribute(AZ::Edit::Attributes::Max, AZ::RadToDeg(AZ::Constants::TwoPi))
|
||||
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorAttachmentComponent::OnTargetOffsetChanged)
|
||||
->DataElement(0, &EditorAttachmentComponent::m_scaleOffset, "Scale offset", "Local scale offset from target entity")
|
||||
->Attribute(AZ::Edit::Attributes::Step, 0.1f)
|
||||
->Attribute(AZ::Edit::Attributes::Min, 0.001f)
|
||||
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorAttachmentComponent::OnTargetOffsetChanged)
|
||||
->DataElement(
|
||||
0, &EditorAttachmentComponent::m_attachedInitially, "Attached initially",
|
||||
"Whether to attach to target upon activation.")
|
||||
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorAttachmentComponent::OnAttachedInitiallyChanged)
|
||||
->DataElement(
|
||||
AZ::Edit::UIHandlers::ComboBox, &EditorAttachmentComponent::m_scaleSource, "Scaling",
|
||||
"How object scale should be determined. "
|
||||
"Use world scale = Attached object is scaled in world space, Use target entity scale = Attached object adopts "
|
||||
"scale of target entity., Use target bone scale = Attached object adopts scale of target entity/joint.")
|
||||
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorAttachmentComponent::OnScaleSourceChanged)
|
||||
->EnumAttribute(AttachmentConfiguration::ScaleSource::WorldScale, "Use world scale")
|
||||
->EnumAttribute(AttachmentConfiguration::ScaleSource::TargetEntityScale, "Use target entity scale")
|
||||
->EnumAttribute(AttachmentConfiguration::ScaleSource::TargetBoneScale, "Use target bone scale");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EditorAttachmentComponent::Activate()
|
||||
{
|
||||
Base::Activate();
|
||||
m_boneFollower.Activate(GetEntity(), CreateAttachmentConfiguration(),
|
||||
false); // Entity's don't animate in Editor
|
||||
}
|
||||
|
||||
void EditorAttachmentComponent::Deactivate()
|
||||
{
|
||||
m_boneFollower.Deactivate();
|
||||
Base::Deactivate();
|
||||
}
|
||||
|
||||
void EditorAttachmentComponent::BuildGameEntity(AZ::Entity* gameEntity)
|
||||
{
|
||||
AttachmentComponent* component = gameEntity->CreateComponent<AttachmentComponent>();
|
||||
if (component)
|
||||
{
|
||||
component->m_initialConfiguration = CreateAttachmentConfiguration();
|
||||
}
|
||||
}
|
||||
|
||||
AttachmentConfiguration EditorAttachmentComponent::CreateAttachmentConfiguration() const
|
||||
{
|
||||
AttachmentConfiguration configuration;
|
||||
configuration.m_targetId = m_targetId;
|
||||
configuration.m_targetBoneName = m_targetBoneName;
|
||||
configuration.m_targetOffset = GetTargetOffset();
|
||||
configuration.m_attachedInitially = m_attachedInitially;
|
||||
configuration.m_scaleSource = m_scaleSource;
|
||||
return configuration;
|
||||
}
|
||||
|
||||
AZ::Transform EditorAttachmentComponent::GetTargetOffset() const
|
||||
{
|
||||
AZ::Transform offset = AZ::ConvertEulerDegreesToTransform(m_rotationOffset);
|
||||
offset.SetTranslation(m_positionOffset);
|
||||
offset.MultiplyByScale(m_scaleOffset);
|
||||
return offset;
|
||||
}
|
||||
|
||||
AZStd::vector<AZStd::string> EditorAttachmentComponent::GetTargetBoneOptions() const
|
||||
{
|
||||
AZStd::vector<AZStd::string> names;
|
||||
|
||||
// insert blank entry, so user may choose to bind to NO bone.
|
||||
names.push_back("");
|
||||
|
||||
// track whether currently-set bone is found
|
||||
bool currentTargetBoneFound = false;
|
||||
|
||||
// Get character and iterate over bones
|
||||
AZ::u32 jointCount = 0;
|
||||
LmbrCentral::SkeletalHierarchyRequestBus::EventResult(jointCount, m_targetId, &LmbrCentral::SkeletalHierarchyRequests::GetJointCount);
|
||||
for (AZ::u32 jointIndex = 0; jointIndex < jointCount; ++jointIndex)
|
||||
{
|
||||
const char* name = nullptr;
|
||||
LmbrCentral::SkeletalHierarchyRequestBus::EventResult(name, m_targetId, &LmbrCentral::SkeletalHierarchyRequests::GetJointNameByIndex, jointIndex);
|
||||
if (name)
|
||||
{
|
||||
names.push_back(name);
|
||||
|
||||
if (!currentTargetBoneFound)
|
||||
{
|
||||
currentTargetBoneFound = (m_targetBoneName == names.back());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we never found currently-set bone name,
|
||||
// stick it at top of list, just in case user wants to keep it anyway
|
||||
if (!currentTargetBoneFound && !m_targetBoneName.empty())
|
||||
{
|
||||
names.insert(names.begin(), m_targetBoneName);
|
||||
}
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
AZ::u32 EditorAttachmentComponent::OnTargetIdChanged()
|
||||
{
|
||||
// Warn about bad setups (it won't crash, but it's nice to handle this early)
|
||||
if (m_targetId == GetEntityId())
|
||||
{
|
||||
AZ_Warning(GetEntity()->GetName().c_str(), false, "AttachmentComponent cannot target self.") m_targetId.SetInvalid();
|
||||
}
|
||||
|
||||
// Warn about children attaching to a parent
|
||||
AZ::EntityId parentOfTarget;
|
||||
AZ::TransformBus::EventResult(parentOfTarget, m_targetId, &AZ::TransformBus::Events::GetParentId);
|
||||
while (parentOfTarget.IsValid())
|
||||
{
|
||||
if (parentOfTarget == GetEntityId())
|
||||
{
|
||||
AZ_Warning(
|
||||
GetEntity()->GetName().c_str(), parentOfTarget != GetEntityId(), "AttachmentComponent cannot target child entity");
|
||||
m_targetId.SetInvalid();
|
||||
break;
|
||||
}
|
||||
|
||||
AZ::EntityId currentParentId = parentOfTarget;
|
||||
parentOfTarget.SetInvalid();
|
||||
AZ::TransformBus::EventResult(parentOfTarget, currentParentId, &AZ::TransformBus::Events::GetParentId);
|
||||
}
|
||||
|
||||
AttachOrDetachAsNecessary();
|
||||
|
||||
return AZ::Edit::PropertyRefreshLevels::AttributesAndValues; // refresh bone options
|
||||
}
|
||||
|
||||
AZ::u32 EditorAttachmentComponent::OnTargetBoneChanged()
|
||||
{
|
||||
AttachOrDetachAsNecessary();
|
||||
return AZ::Edit::PropertyRefreshLevels::None;
|
||||
}
|
||||
|
||||
AZ::u32 EditorAttachmentComponent::OnTargetOffsetChanged()
|
||||
{
|
||||
EBUS_EVENT_ID(GetEntityId(), LmbrCentral::AttachmentComponentRequestBus, SetAttachmentOffset, GetTargetOffset());
|
||||
return AZ::Edit::PropertyRefreshLevels::None;
|
||||
}
|
||||
|
||||
AZ::u32 EditorAttachmentComponent::OnAttachedInitiallyChanged()
|
||||
{
|
||||
AttachOrDetachAsNecessary();
|
||||
return AZ::Edit::PropertyRefreshLevels::None;
|
||||
}
|
||||
|
||||
AZ::u32 EditorAttachmentComponent::OnScaleSourceChanged()
|
||||
{
|
||||
m_boneFollower.Deactivate();
|
||||
m_boneFollower.Activate(GetEntity(), CreateAttachmentConfiguration(), false);
|
||||
return AZ::Edit::PropertyRefreshLevels::None;
|
||||
}
|
||||
|
||||
void EditorAttachmentComponent::AttachOrDetachAsNecessary()
|
||||
{
|
||||
if (m_attachedInitially && m_targetId.IsValid())
|
||||
{
|
||||
EBUS_EVENT_ID(
|
||||
GetEntityId(), LmbrCentral::AttachmentComponentRequestBus, Attach, m_targetId, m_targetBoneName.c_str(), GetTargetOffset());
|
||||
}
|
||||
else
|
||||
{
|
||||
EBUS_EVENT_ID(GetEntityId(), LmbrCentral::AttachmentComponentRequestBus, Detach);
|
||||
}
|
||||
}
|
||||
} // namespace Render
|
||||
} // namespace AZ
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzToolsFramework/ToolsComponents/EditorComponentBase.h>
|
||||
#include "AttachmentComponent.h"
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Render
|
||||
{
|
||||
/*!
|
||||
* In-editor attachment component.
|
||||
* \ref AttachmentComponent
|
||||
*/
|
||||
class EditorAttachmentComponent
|
||||
: public AzToolsFramework::Components::EditorComponentBase
|
||||
{
|
||||
private:
|
||||
using Base = AzToolsFramework::Components::EditorComponentBase;
|
||||
public:
|
||||
AZ_COMPONENT(EditorAttachmentComponent, "{DA6072FD-E696-47D8-81D9-1F77D3464200}", Base);
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
|
||||
{
|
||||
AttachmentComponent::GetProvidedServices(provided);
|
||||
}
|
||||
|
||||
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
|
||||
{
|
||||
AttachmentComponent::GetIncompatibleServices(incompatible);
|
||||
}
|
||||
|
||||
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
|
||||
{
|
||||
AttachmentComponent::GetRequiredServices(required);
|
||||
}
|
||||
|
||||
~EditorAttachmentComponent() override = default;
|
||||
void BuildGameEntity(AZ::Entity* gameEntity) override;
|
||||
|
||||
protected:
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// AZ::Component interface implementation
|
||||
void Activate() override;
|
||||
void Deactivate() override;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AZ::u32 OnTargetIdChanged();
|
||||
AZ::u32 OnTargetBoneChanged();
|
||||
AZ::u32 OnTargetOffsetChanged();
|
||||
AZ::u32 OnAttachedInitiallyChanged();
|
||||
AZ::u32 OnScaleSourceChanged();
|
||||
|
||||
//! Invoked when an attachment property changes
|
||||
void AttachOrDetachAsNecessary();
|
||||
|
||||
//! For populating ComboBox
|
||||
AZStd::vector<AZStd::string> GetTargetBoneOptions() const;
|
||||
|
||||
//! Create runtime configuration from editor configuration
|
||||
AttachmentConfiguration CreateAttachmentConfiguration() const;
|
||||
|
||||
//! Create AZ::Transform from position and rotation
|
||||
AZ::Transform GetTargetOffset() const;
|
||||
|
||||
//! Attach to this entity.
|
||||
AZ::EntityId m_targetId;
|
||||
|
||||
//! Attach to this bone on target entity.
|
||||
AZStd::string m_targetBoneName;
|
||||
|
||||
//! Offset from target bone's position.
|
||||
AZ::Vector3 m_positionOffset = AZ::Vector3::CreateZero();
|
||||
|
||||
//! Offset from target bone's rotation.
|
||||
AZ::Vector3 m_rotationOffset = AZ::Vector3::CreateZero();
|
||||
|
||||
//! Offset from target entity's scale.
|
||||
AZ::Vector3 m_scaleOffset = AZ::Vector3::CreateOne();
|
||||
|
||||
//! Observe scale information from the specified source.
|
||||
AttachmentConfiguration::ScaleSource m_scaleSource = AttachmentConfiguration::ScaleSource::WorldScale;
|
||||
|
||||
//! Whether to attach to target upon activation.
|
||||
//! If false, the entity remains detached until Attach() is called.
|
||||
bool m_attachedInitially = true;
|
||||
|
||||
//! Implements actual attachment functionality
|
||||
AZ::Render::BoneFollower m_boneFollower;
|
||||
};
|
||||
} // namespace Render
|
||||
} // namespace AZ
|
||||
@@ -39,6 +39,7 @@
|
||||
#include <SkyBox/PhysicalSkyComponent.h>
|
||||
#include <Scripting/EntityReferenceComponent.h>
|
||||
#include <SurfaceData/SurfaceDataMeshComponent.h>
|
||||
#include <Animation/AttachmentComponent.h>
|
||||
|
||||
#ifdef ATOMLYINTEGRATION_FEATURE_COMMON_EDITOR
|
||||
#include <EditorCommonFeaturesSystemComponent.h>
|
||||
@@ -69,6 +70,7 @@
|
||||
#include <SkyBox/EditorPhysicalSkyComponent.h>
|
||||
#include <Scripting/EditorEntityReferenceComponent.h>
|
||||
#include <SurfaceData/EditorSurfaceDataMeshComponent.h>
|
||||
#include <Animation/EditorAttachmentComponent.h>
|
||||
#endif
|
||||
|
||||
namespace AZ
|
||||
@@ -111,6 +113,7 @@ namespace AZ
|
||||
DiffuseProbeGridComponent::CreateDescriptor(),
|
||||
DeferredFogComponent::CreateDescriptor(),
|
||||
SurfaceData::SurfaceDataMeshComponent::CreateDescriptor(),
|
||||
AttachmentComponent::CreateDescriptor(),
|
||||
|
||||
#ifdef ATOMLYINTEGRATION_FEATURE_COMMON_EDITOR
|
||||
EditorAreaLightComponent::CreateDescriptor(),
|
||||
@@ -141,6 +144,7 @@ namespace AZ
|
||||
EditorDiffuseProbeGridComponent::CreateDescriptor(),
|
||||
EditorDeferredFogComponent::CreateDescriptor(),
|
||||
SurfaceData::EditorSurfaceDataMeshComponent::CreateDescriptor(),
|
||||
EditorAttachmentComponent::CreateDescriptor(),
|
||||
#endif
|
||||
});
|
||||
}
|
||||
|
||||
+2
@@ -11,6 +11,8 @@
|
||||
|
||||
set(FILES
|
||||
Source/Module.cpp
|
||||
Source/Animation/EditorAttachmentComponent.h
|
||||
Source/Animation/EditorAttachmentComponent.cpp
|
||||
Include/AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentRequestBus.h
|
||||
Include/AtomLyIntegration/CommonFeatures/ReflectionProbe/EditorReflectionProbeBus.h
|
||||
Source/EditorCommonFeaturesSystemComponent.h
|
||||
|
||||
+2
@@ -10,6 +10,8 @@
|
||||
#
|
||||
|
||||
set(FILES
|
||||
Source/Animation/AttachmentComponent.h
|
||||
Source/Animation/AttachmentComponent.cpp
|
||||
Source/CoreLights/AreaLightComponent.h
|
||||
Source/CoreLights/AreaLightComponent.cpp
|
||||
Source/CoreLights/AreaLightComponentConfig.cpp
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
#include <Integration/Rendering/RenderActorInstance.h>
|
||||
|
||||
#include <LmbrCentral/Rendering/MeshComponentBus.h>
|
||||
#include <LmbrCentral/Animation/SkeletalHierarchyRequestBus.h>
|
||||
|
||||
#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h>
|
||||
#include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentBus.h>
|
||||
|
||||
Reference in New Issue
Block a user