Files
o3de/Gems/EMotionFX/Code/Source/Integration/Components/SimpleMotionComponent.cpp
T
Esteban Papp 1f9b284de2 Merge branch 'development' into cmake/SPEC-7179
Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com>

# Conflicts:
#	Code/Editor/Plugins/ComponentEntityEditorPlugin/ComponentEntityEditorPlugin_precompiled.h
#	Code/Editor/Plugins/EditorCommon/EditorCommon_precompiled.h
#	Code/Editor/Plugins/EditorCommon/stdafx.cpp
#	Code/Editor/Plugins/FFMPEGPlugin/FFMPEGPlugin_precompiled.h
#	Code/Editor/Plugins/PerforcePlugin/PerforcePlugin_precompiled.h
#	Code/Editor/Plugins/ProjectSettingsTool/ProjectSettingsTool_precompiled.h
#	Code/Framework/AzToolsFramework/AzToolsFramework/AzToolsFramework_precompiled.h
#	Code/Tools/AssetProcessor/native/precompiled.h
#	Code/Tools/Standalone/StandaloneTools_precompiled.h
#	Gems/AssetMemoryAnalyzer/Code/Source/AssetMemoryAnalyzer_precompiled.h
#	Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageProcessing_precompiled.h
#	Gems/Atom/RHI/DX12/Code/Source/RHI/Atom_RHI_DX12_precompiled.h
#	Gems/Atom/RHI/Metal/Code/Include/Platform/Mac/Atom_RHI_Metal_precompiled_Platform.h
#	Gems/Atom/RHI/Metal/Code/Include/Platform/iOS/Atom_RHI_Metal_precompiled_Platform.h
#	Gems/Atom/RHI/Metal/Code/Source/Atom_RHI_Metal_precompiled.h
#	Gems/Atom/RHI/Metal/Code/atom_rhi_metal_common_files.cmake
#	Gems/Atom/RHI/Null/Code/Source/Atom_RHI_Null_precompiled.h
#	Gems/Atom/RHI/Null/Code/atom_rhi_null_common_files.cmake
#	Gems/Atom/RHI/Vulkan/Code/Include/Platform/Android/Atom_RHI_Vulkan_precompiled_Platform.h
#	Gems/Atom/RHI/Vulkan/Code/Include/Platform/Linux/Atom_RHI_Vulkan_precompiled_Platform.h
#	Gems/Atom/RHI/Vulkan/Code/Include/Platform/Mac/Atom_RHI_Vulkan_precompiled_Platform.h
#	Gems/Atom/RHI/Vulkan/Code/Include/Platform/Windows/Atom_RHI_Vulkan_precompiled_Platform.h
#	Gems/Atom/RHI/Vulkan/Code/Source/Atom_RHI_Vulkan_precompiled.h
#	Gems/Atom/RHI/Vulkan/Code/Source/RHI/SwapChain.cpp
#	Gems/Atom/RHI/Vulkan/Code/atom_rhi_vulkan_common_files.cmake
#	Gems/AtomLyIntegration/AtomFont/Code/Include/AtomLyIntegration/AtomFont/AtomFont_precompiled.h
#	Gems/Blast/Code/Source/StdAfx.cpp
#	Gems/Camera/Code/Source/Camera_precompiled.h
#	Gems/EMotionFX/Code/Source/EMotionFX_precompiled.h
#	Gems/FastNoise/Code/Source/FastNoise_precompiled.h
#	Gems/Gestures/Code/Source/Gestures_precompiled.h
#	Gems/GradientSignal/Code/Source/GradientSignal_precompiled.h
#	Gems/GraphCanvas/Code/precompiled.h
#	Gems/ImGui/Code/Source/ImGui_precompiled.h
#	Gems/InAppPurchases/Code/Source/InAppPurchases_precompiled.h
#	Gems/LmbrCentral/Code/Source/LmbrCentral_precompiled.h
#	Gems/LmbrCentral/Code/Tests/ShapeGeometryUtilTest.cpp
#	Gems/LyShine/Code/Editor/UiCanvasEditor_precompiled.h
#	Gems/LyShine/Code/Source/Animation/LyShine_precompiled.h
#	Gems/LyShine/Code/Source/LyShine_precompiled.h
#	Gems/LyShineExamples/Code/Source/LyShineExamples_precompiled.h
#	Gems/Maestro/Code/Source/Cinematics/Maestro_precompiled.h
#	Gems/Maestro/Code/Source/Maestro_precompiled.h
#	Gems/MessagePopup/Code/Source/MessagePopup_precompiled.h
#	Gems/Metastream/Code/Source/Metastream_precompiled.h
#	Gems/Microphone/Code/Source/Microphone_precompiled.h
#	Gems/Multiplayer/Code/Source/Multiplayer_precompiled.h
#	Gems/PhysX/Code/NumericalMethods/Source/NumericalMethods_precompiled.h
#	Gems/PhysX/Code/Source/PhysXUnsupported_precompiled.h
#	Gems/PhysX/Code/Source/PhysX_precompiled.h
#	Gems/PhysX/Code/physx_unsupported_files.cmake
#	Gems/PhysXDebug/Code/Source/PhysXDebugUnsupported_precompiled.h
#	Gems/PhysXDebug/Code/Source/PhysXDebug_precompiled.h
#	Gems/ScriptCanvas/Code/Editor/precompiled.h
#	Gems/ScriptCanvas/Code/Source/precompiled.h
#	Gems/ScriptCanvasDeveloper/Code/Source/precompiled.h
#	Gems/ScriptCanvasPhysics/Code/Source/ScriptCanvasPhysics_precompiled.h
#	Gems/ScriptEvents/Code/Source/precompiled.h
#	Gems/ScriptEvents/Code/Tests/Editor/EditorTests.cpp
#	Gems/ScriptedEntityTweener/Code/Source/ScriptedEntityTweener_precompiled.h
#	Gems/SliceFavorites/Code/Source/SliceFavorites_precompiled.h
#	Gems/StartingPointCamera/Code/Source/StartingPointCamera_precompiled.h
#	Gems/StartingPointInput/Code/Source/StartingPointInput_precompiled.h
#	Gems/StartingPointMovement/Code/Source/StartingPointMovement_precompiled.h
#	Gems/SurfaceData/Code/Source/SurfaceData_precompiled.h
#	Gems/TextureAtlas/Code/Source/TextureAtlas_precompiled.h
#	Gems/TickBusOrderViewer/Code/Source/TickBusOrderViewer_precompiled.h
#	Gems/Twitch/Code/Source/Twitch_precompiled.h
#	Gems/VirtualGamepad/Code/Source/VirtualGamepad_precompiled.h
#	Gems/WhiteBox/Code/Source/WhiteBoxUnsupported_precompiled.h
#	Gems/WhiteBox/Code/Source/WhiteBox_precompiled.h
2021-07-16 15:42:37 -07:00

465 lines
19 KiB
C++

/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/Component/Entity.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Script/ScriptContext.h>
#include <Integration/Components/SimpleMotionComponent.h>
#include <MCore/Source/AttributeString.h>
#include <EMotionFX/Source/ActorInstance.h>
#include <EMotionFX/Source/MotionSystem.h>
#include <EMotionFX/Source/MotionInstance.h>
namespace EMotionFX
{
namespace Integration
{
void SimpleMotionComponent::Configuration::Reflect(AZ::ReflectContext *context)
{
auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<Configuration>()
->Version(2)
->Field("MotionAsset", &Configuration::m_motionAsset)
->Field("Loop", &Configuration::m_loop)
->Field("Retarget", &Configuration::m_retarget)
->Field("Reverse", &Configuration::m_reverse)
->Field("Mirror", &Configuration::m_mirror)
->Field("PlaySpeed", &Configuration::m_playspeed)
->Field("BlendIn", &Configuration::m_blendInTime)
->Field("BlendOut", &Configuration::m_blendOutTime)
->Field("PlayOnActivation", &Configuration::m_playOnActivation)
->Field("InPlace", &Configuration::m_inPlace)
;
AZ::EditContext* editContext = serializeContext->GetEditContext();
if (editContext)
{
editContext->Class<Configuration>( "Configuration", "Settings for this Simple Motion")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->DataElement(AZ::Edit::UIHandlers::Default, &Configuration::m_motionAsset, "Motion", "EMotion FX motion to be loaded for this actor")
->DataElement(AZ::Edit::UIHandlers::Default, &Configuration::m_loop, "Loop motion", "Toggles looping of the animation")
->DataElement(AZ::Edit::UIHandlers::Default, &Configuration::m_retarget, "Retarget motion", "Toggles retargeting of the animation")
->DataElement(AZ::Edit::UIHandlers::Default, &Configuration::m_reverse, "Reverse motion", "Toggles reversing of the animation")
->DataElement(AZ::Edit::UIHandlers::Default, &Configuration::m_mirror, "Mirror motion", "Toggles mirroring of the animation")
->DataElement(AZ::Edit::UIHandlers::Default, &Configuration::m_playspeed, "Play speed", "Determines the rate at which the motion is played")
->Attribute(AZ::Edit::Attributes::Min, 0.0f)
->DataElement(AZ::Edit::UIHandlers::Default, &Configuration::m_blendInTime, "Blend In Time", "Determines the blend in time in seconds")
->Attribute(AZ::Edit::Attributes::Min, 0.0f)
->DataElement(AZ::Edit::UIHandlers::Default, &Configuration::m_blendOutTime, "Blend Out Time", "Determines the blend out time in seconds")
->Attribute(AZ::Edit::Attributes::Min, 0.0f)
->DataElement(AZ::Edit::UIHandlers::Default, &Configuration::m_playOnActivation, "Play on active", "Playing animation immediately after activation.")
->DataElement(AZ::Edit::UIHandlers::Default, &Configuration::m_inPlace, "In-place",
"Plays the animation in-place and removes any positional and rotational changes from root joints.")
;
}
}
}
void SimpleMotionComponent::Reflect(AZ::ReflectContext* context)
{
Configuration::Reflect(context);
auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<SimpleMotionComponent, AZ::Component>()
->Version(1)
->Field("Configuration", &SimpleMotionComponent::m_configuration)
;
}
auto* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context);
if (behaviorContext)
{
behaviorContext->EBus<SimpleMotionComponentRequestBus>("SimpleMotionComponentRequestBus")
->Event("LoopMotion", &SimpleMotionComponentRequestBus::Events::LoopMotion)
->Event("GetLoopMotion", &SimpleMotionComponentRequestBus::Events::GetLoopMotion)
->Attribute("Hidden", AZ::Edit::Attributes::PropertyHidden)
->VirtualProperty("LoopMotion", "GetLoopMotion", "LoopMotion")
->Event("RetargetMotion", &SimpleMotionComponentRequestBus::Events::RetargetMotion)
->Event("ReverseMotion", &SimpleMotionComponentRequestBus::Events::ReverseMotion)
->Event("MirrorMotion", &SimpleMotionComponentRequestBus::Events::MirrorMotion)
->Event("SetPlaySpeed", &SimpleMotionComponentRequestBus::Events::SetPlaySpeed)
->Event("GetPlaySpeed", &SimpleMotionComponentRequestBus::Events::GetPlaySpeed)
->Attribute("Hidden", AZ::Edit::Attributes::PropertyHidden)
->VirtualProperty("PlaySpeed", "GetPlaySpeed", "SetPlaySpeed")
->Event("PlayTime", &SimpleMotionComponentRequestBus::Events::PlayTime)
->Event("GetPlayTime", &SimpleMotionComponentRequestBus::Events::GetPlayTime)
->Attribute("Hidden", AZ::Edit::Attributes::PropertyHidden)
->VirtualProperty("PlayTime", "GetPlayTime", "PlayTime")
->Event("Motion", &SimpleMotionComponentRequestBus::Events::Motion)
->Event("GetMotion", &SimpleMotionComponentRequestBus::Events::GetMotion)
->VirtualProperty("Motion", "GetMotion", "Motion")
->Event("BlendInTime", &SimpleMotionComponentRequestBus::Events::BlendInTime)
->Event("GetBlendInTime", &SimpleMotionComponentRequestBus::Events::GetBlendInTime)
->Attribute("Hidden", AZ::Edit::Attributes::PropertyHidden)
->VirtualProperty("BlendInTime", "GetBlendInTime", "BlendInTime")
->Event("BlendOutTime", &SimpleMotionComponentRequestBus::Events::BlendOutTime)
->Event("GetBlendOutTime", &SimpleMotionComponentRequestBus::Events::GetBlendOutTime)
->Attribute("Hidden", AZ::Edit::Attributes::PropertyHidden)
->VirtualProperty("BlendOutTime", "GetBlendOutTime", "BlendOutTime")
->Event("PlayMotion", &SimpleMotionComponentRequestBus::Events::PlayMotion)
;
behaviorContext->Class<SimpleMotionComponent>()->RequestBus("SimpleMotionComponentRequestBus");
}
}
SimpleMotionComponent::Configuration::Configuration()
: m_loop(false)
, m_retarget(false)
, m_reverse(false)
, m_mirror(false)
, m_playspeed(1.f)
, m_blendInTime(0.0f)
, m_blendOutTime(0.0f)
, m_playOnActivation(true)
, m_inPlace(false)
{
}
SimpleMotionComponent::SimpleMotionComponent(const Configuration* config)
: m_actorInstance(nullptr)
, m_motionInstance(nullptr)
, m_lastMotionInstance(nullptr)
{
if (config)
{
m_configuration = *config;
}
}
SimpleMotionComponent::~SimpleMotionComponent()
{
}
void SimpleMotionComponent::Init()
{
}
void SimpleMotionComponent::Activate()
{
m_motionInstance = nullptr;
AZ::Data::AssetBus::MultiHandler::BusDisconnect();
SimpleMotionComponentRequestBus::Handler::BusConnect(GetEntityId());
auto& cfg = m_configuration;
if (cfg.m_motionAsset.GetId().IsValid())
{
AZ::Data::AssetBus::MultiHandler::BusConnect(cfg.m_motionAsset.GetId());
cfg.m_motionAsset.QueueLoad();
}
ActorComponentNotificationBus::Handler::BusConnect(GetEntityId());
}
void SimpleMotionComponent::Deactivate()
{
SimpleMotionComponentRequestBus::Handler::BusDisconnect();
ActorComponentNotificationBus::Handler::BusDisconnect();
AZ::Data::AssetBus::MultiHandler::BusDisconnect();
RemoveMotionInstanceFromActor(m_motionInstance);
m_motionInstance = nullptr;
RemoveMotionInstanceFromActor(m_lastMotionInstance);
m_lastMotionInstance = nullptr;
m_configuration.m_motionAsset.Release();
m_lastMotionAsset.Release();
m_actorInstance.reset();
}
const MotionInstance* SimpleMotionComponent::GetMotionInstance()
{
return m_motionInstance;
}
void SimpleMotionComponent::SetMotionAssetId(const AZ::Data::AssetId& assetId)
{
m_configuration.m_motionAsset = AZ::Data::Asset<MotionAsset>(assetId, azrtti_typeid<MotionAsset>());
}
void SimpleMotionComponent::OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset)
{
auto& cfg = m_configuration;
if (asset.GetId() == cfg.m_motionAsset.GetId())
{
cfg.m_motionAsset = asset;
if (m_configuration.m_playOnActivation)
{
PlayMotion();
}
}
}
void SimpleMotionComponent::OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset)
{
OnAssetReady(asset);
}
void SimpleMotionComponent::OnActorInstanceCreated(EMotionFX::ActorInstance* actorInstance)
{
m_actorInstance = actorInstance;
if (m_configuration.m_playOnActivation)
{
PlayMotion();
}
}
void SimpleMotionComponent::OnActorInstanceDestroyed(EMotionFX::ActorInstance* actorInstance)
{
AZ_UNUSED(actorInstance);
RemoveMotionInstanceFromActor(m_motionInstance);
m_motionInstance = nullptr;
RemoveMotionInstanceFromActor(m_lastMotionInstance);
m_lastMotionInstance = nullptr;
m_actorInstance.reset();
}
void SimpleMotionComponent::PlayMotion()
{
m_motionInstance = PlayMotionInternal(m_actorInstance.get(), m_configuration, /*deleteOnZeroWeight*/true);
}
void SimpleMotionComponent::RemoveMotionInstanceFromActor(EMotionFX::MotionInstance* motionInstance)
{
if (motionInstance)
{
if (m_actorInstance && m_actorInstance->GetMotionSystem())
{
m_actorInstance->GetMotionSystem()->RemoveMotionInstance(motionInstance);
}
}
}
void SimpleMotionComponent::LoopMotion(bool enable)
{
m_configuration.m_loop = enable;
if (m_motionInstance)
{
m_motionInstance->SetMaxLoops(enable ? EMFX_LOOPFOREVER : 1);
}
}
bool SimpleMotionComponent::GetLoopMotion() const
{
return m_configuration.m_loop;
}
void SimpleMotionComponent::RetargetMotion(bool enable)
{
m_configuration.m_retarget = enable;
if (m_motionInstance)
{
m_motionInstance->SetRetargetingEnabled(enable);
}
}
void SimpleMotionComponent::ReverseMotion(bool enable)
{
m_configuration.m_reverse = enable;
if (m_motionInstance)
{
m_motionInstance->SetPlayMode(enable ? EMotionFX::EPlayMode::PLAYMODE_BACKWARD : EMotionFX::EPlayMode::PLAYMODE_FORWARD);
}
}
void SimpleMotionComponent::MirrorMotion(bool enable)
{
m_configuration.m_mirror = enable;
if (m_motionInstance)
{
m_motionInstance->SetMirrorMotion(enable);
}
}
void SimpleMotionComponent::SetPlaySpeed(float speed)
{
m_configuration.m_playspeed = speed;
if (m_motionInstance)
{
m_motionInstance->SetPlaySpeed(speed);
}
}
float SimpleMotionComponent::GetPlaySpeed() const
{
return m_configuration.m_playspeed;
}
void SimpleMotionComponent::PlayTime(float time)
{
if (m_motionInstance)
{
float delta = time - m_motionInstance->GetLastCurrentTime();
m_motionInstance->SetCurrentTime(time, false);
// Apply the same time step to the last animation
// so blend out will be good. Otherwise we are just blending
// from the last frame played of the last animation.
if (m_lastMotionInstance && m_lastMotionInstance->GetIsBlending())
{
m_lastMotionInstance->SetCurrentTime(m_lastMotionInstance->GetLastCurrentTime() + delta, false);
}
}
}
float SimpleMotionComponent::GetPlayTime() const
{
float result = 0.0f;
if (m_motionInstance)
{
result = m_motionInstance->GetCurrentTimeNormalized();
}
return result;
}
void SimpleMotionComponent::Motion(AZ::Data::AssetId assetId)
{
if (m_configuration.m_motionAsset.GetId() != assetId)
{
// Disconnect the old asset bus
if (AZ::Data::AssetBus::MultiHandler::BusIsConnectedId(m_configuration.m_motionAsset.GetId()))
{
AZ::Data::AssetBus::MultiHandler::BusDisconnect(m_configuration.m_motionAsset.GetId());
}
// Save the motion asset that we are about to be remove in case it can be reused.
AZ::Data::Asset<MotionAsset> oldLastMotionAsset = m_lastMotionAsset;
if (m_lastMotionInstance)
{
RemoveMotionInstanceFromActor(m_lastMotionInstance);
}
// Store the current motion asset as the last one for possible blending.
// If we don't keep a reference to the motion asset, the motion instance will be
// automatically released.
if (m_configuration.m_motionAsset.GetId().IsValid())
{
m_lastMotionAsset = m_configuration.m_motionAsset;
}
// Set the current motion instance as the last motion instance. The new current motion
// instance will then be set when the load is complete.
m_lastMotionInstance = m_motionInstance;
m_motionInstance = nullptr;
// Start the fade out if there is a blend out time. Otherwise just leave the
// m_lastMotionInstance where it is at so the next anim can blend from that frame.
if (m_lastMotionInstance && m_configuration.m_blendOutTime > 0.0f)
{
m_lastMotionInstance->Stop(m_configuration.m_blendOutTime);
}
// Reuse the old, last motion asset if possible. Otherwise, request a load.
if (assetId.IsValid() && oldLastMotionAsset.GetData() && assetId == oldLastMotionAsset.GetId())
{
// Even though we are not calling GetAsset here, OnAssetReady
// will be fired when the bus is connected because this asset is already loaded.
m_configuration.m_motionAsset = oldLastMotionAsset;
}
else
{
// Won't be able to reuse oldLastMotionAsset, release it.
oldLastMotionAsset.Release();
// Clear the old asset.
m_configuration.m_motionAsset.Release();
// Create a new asset
if (assetId.IsValid())
{
m_configuration.m_motionAsset = AZ::Data::AssetManager::Instance().GetAsset<MotionAsset>(assetId, m_configuration.m_motionAsset.GetAutoLoadBehavior());
}
}
// Connect the bus if the asset is is valid.
if (m_configuration.m_motionAsset.GetId().IsValid())
{
AZ::Data::AssetBus::MultiHandler::BusConnect(m_configuration.m_motionAsset.GetId());
}
}
}
AZ::Data::AssetId SimpleMotionComponent::GetMotion() const
{
return m_configuration.m_motionAsset.GetId();
}
void SimpleMotionComponent::BlendInTime(float time)
{
m_configuration.m_blendInTime = time;
}
float SimpleMotionComponent::GetBlendInTime() const
{
return m_configuration.m_blendInTime;
}
void SimpleMotionComponent::BlendOutTime(float time)
{
m_configuration.m_blendOutTime = time;
}
float SimpleMotionComponent::GetBlendOutTime() const
{
return m_configuration.m_blendOutTime;
}
EMotionFX::MotionInstance* SimpleMotionComponent::PlayMotionInternal(const EMotionFX::ActorInstance* actorInstance, const SimpleMotionComponent::Configuration& cfg, bool deleteOnZeroWeight)
{
if (!actorInstance || !cfg.m_motionAsset.IsReady())
{
return nullptr;
}
if (!actorInstance->GetMotionSystem())
{
return nullptr;
}
auto* motionAsset = cfg.m_motionAsset.GetAs<MotionAsset>();
if (!motionAsset)
{
AZ_Error("EMotionFX", motionAsset, "Motion asset is not valid.");
return nullptr;
}
//init the PlaybackInfo based on our config
EMotionFX::PlayBackInfo info;
info.mNumLoops = cfg.m_loop ? EMFX_LOOPFOREVER : 1;
info.mRetarget = cfg.m_retarget;
info.mPlayMode = cfg.m_reverse ? EMotionFX::EPlayMode::PLAYMODE_BACKWARD : EMotionFX::EPlayMode::PLAYMODE_FORWARD;
info.mFreezeAtLastFrame = info.mNumLoops == 1;
info.mMirrorMotion = cfg.m_mirror;
info.mPlaySpeed = cfg.m_playspeed;
info.mPlayNow = true;
info.mDeleteOnZeroWeight = deleteOnZeroWeight;
info.mCanOverwrite = false;
info.mBlendInTime = cfg.m_blendInTime;
info.mBlendOutTime = cfg.m_blendOutTime;
info.mInPlace = cfg.m_inPlace;
return actorInstance->GetMotionSystem()->PlayMotion(motionAsset->m_emfxMotion.get(), &info);
}
} // namespace integration
} // namespace EMotionFX