EMotion FX: Apply motion extraction only in game-mode and for anim editor entities (#5401)

* Motion extraction should only be applied when in game mode or for entities that belong to the Animation Editor. We don't want our entities to move in editor mode.
* Moved code into separate function that actually applies the trajectory delta to the entity/character controller.

Signed-off-by: Benjamin Jillich <jillich@amazon.com>
This commit is contained in:
Benjamin Jillich
2021-11-09 13:07:48 +01:00
committed by GitHub
parent f918b12b5a
commit 0ce2a7b2f4
2 changed files with 123 additions and 89 deletions
@@ -65,36 +65,38 @@
#if defined(EMOTIONFXANIMATION_EDITOR) // EMFX tools / editor includes
// Qt
# include <QtGui/QSurfaceFormat>
#include <QtGui/QSurfaceFormat>
// EMStudio tools and main window registration
# include <LyViewPaneNames.h>
# include <AzToolsFramework/API/ViewPaneOptions.h>
# include <AzCore/std/string/wildcard.h>
# include <QApplication>
# include <EMotionStudio/EMStudioSDK/Source/MainWindow.h>
# include <EMotionStudio/EMStudioSDK/Source/PluginManager.h>
#include <LyViewPaneNames.h>
#include <AzToolsFramework/API/ViewPaneOptions.h>
#include <AzCore/std/string/wildcard.h>
#include <QApplication>
#include <EMotionStudio/EMStudioSDK/Source/MainWindow.h>
#include <EMotionStudio/EMStudioSDK/Source/PluginManager.h>
// EMStudio plugins
# include <EMotionStudio/Plugins/StandardPlugins/Source/LogWindow/LogWindowPlugin.h>
# include <EMotionStudio/Plugins/StandardPlugins/Source/CommandBar/CommandBarPlugin.h>
# include <EMotionStudio/Plugins/StandardPlugins/Source/ActionHistory/ActionHistoryPlugin.h>
# include <EMotionStudio/Plugins/StandardPlugins/Source/MotionWindow/MotionWindowPlugin.h>
# include <EMotionStudio/Plugins/StandardPlugins/Source/MorphTargetsWindow/MorphTargetsWindowPlugin.h>
# include <EMotionStudio/Plugins/StandardPlugins/Source/TimeView/TimeViewPlugin.h>
# include <EMotionStudio/Plugins/StandardPlugins/Source/Attachments/AttachmentsPlugin.h>
# include <EMotionStudio/Plugins/StandardPlugins/Source/SceneManager/SceneManagerPlugin.h>
# include <EMotionStudio/Plugins/StandardPlugins/Source/NodeWindow/NodeWindowPlugin.h>
# include <EMotionStudio/Plugins/StandardPlugins/Source/MotionEvents/MotionEventsPlugin.h>
# include <EMotionStudio/Plugins/StandardPlugins/Source/MotionSetsWindow/MotionSetsWindowPlugin.h>
# include <EMotionStudio/Plugins/StandardPlugins/Source/NodeGroups/NodeGroupsPlugin.h>
# include <EMotionStudio/Plugins/StandardPlugins/Source/AnimGraph/AnimGraphPlugin.h>
# include <EMotionStudio/Plugins/RenderPlugins/Source/OpenGLRender/OpenGLRenderPlugin.h>
# include <Editor/Plugins/HitDetection/HitDetectionJointInspectorPlugin.h>
# include <Editor/Plugins/SkeletonOutliner/SkeletonOutlinerPlugin.h>
# include <Editor/Plugins/Ragdoll/RagdollNodeInspectorPlugin.h>
# include <Editor/Plugins/Cloth/ClothJointInspectorPlugin.h>
# include <Editor/Plugins/SimulatedObject/SimulatedObjectWidget.h>
# include <Source/Editor/PropertyWidgets/PropertyTypes.h>
# include <EMotionFX_Traits_Platform.h>
#include <EMotionStudio/Plugins/StandardPlugins/Source/LogWindow/LogWindowPlugin.h>
#include <EMotionStudio/Plugins/StandardPlugins/Source/CommandBar/CommandBarPlugin.h>
#include <EMotionStudio/Plugins/StandardPlugins/Source/ActionHistory/ActionHistoryPlugin.h>
#include <EMotionStudio/Plugins/StandardPlugins/Source/MotionWindow/MotionWindowPlugin.h>
#include <EMotionStudio/Plugins/StandardPlugins/Source/MorphTargetsWindow/MorphTargetsWindowPlugin.h>
#include <EMotionStudio/Plugins/StandardPlugins/Source/TimeView/TimeViewPlugin.h>
#include <EMotionStudio/Plugins/StandardPlugins/Source/Attachments/AttachmentsPlugin.h>
#include <EMotionStudio/Plugins/StandardPlugins/Source/SceneManager/SceneManagerPlugin.h>
#include <EMotionStudio/Plugins/StandardPlugins/Source/NodeWindow/NodeWindowPlugin.h>
#include <EMotionStudio/Plugins/StandardPlugins/Source/MotionEvents/MotionEventsPlugin.h>
#include <EMotionStudio/Plugins/StandardPlugins/Source/MotionSetsWindow/MotionSetsWindowPlugin.h>
#include <EMotionStudio/Plugins/StandardPlugins/Source/NodeGroups/NodeGroupsPlugin.h>
#include <EMotionStudio/Plugins/StandardPlugins/Source/AnimGraph/AnimGraphPlugin.h>
#include <EMotionStudio/Plugins/RenderPlugins/Source/OpenGLRender/OpenGLRenderPlugin.h>
#include <Editor/Plugins/HitDetection/HitDetectionJointInspectorPlugin.h>
#include <Editor/Plugins/SkeletonOutliner/SkeletonOutlinerPlugin.h>
#include <Editor/Plugins/Ragdoll/RagdollNodeInspectorPlugin.h>
#include <Editor/Plugins/Cloth/ClothJointInspectorPlugin.h>
#include <Editor/Plugins/SimulatedObject/SimulatedObjectWidget.h>
#include <Source/Editor/PropertyWidgets/PropertyTypes.h>
#include <EMotionFX_Traits_Platform.h>
#include <IEditor.h>
#endif // EMOTIONFXANIMATION_EDITOR
#include <IConsole.h>
@@ -589,14 +591,12 @@ namespace EMotionFX
#endif
REGISTER_CVAR2("emfx_updateEnabled", &CVars::emfx_updateEnabled, 1, VF_DEV_ONLY, "Enable main EMFX update");
REGISTER_CVAR2("emfx_actorRenderEnabled", &CVars::emfx_actorRenderEnabled, 1, VF_DEV_ONLY, "Enable ActorRenderNode rendering");
}
//////////////////////////////////////////////////////////////////////////
void SystemComponent::OnCrySystemShutdown(ISystem&)
{
gEnv->pConsole->UnregisterVariable("emfx_updateEnabled");
gEnv->pConsole->UnregisterVariable("emfx_actorRenderEnabled");
#if !defined(AZ_MONOLITHIC_BUILD)
gEnv = nullptr;
@@ -620,75 +620,102 @@ namespace EMotionFX
{
// Main EMotionFX runtime update.
GetEMotionFX().Update(delta);
}
const ActorManager* actorManager = GetEMotionFX().GetActorManager();
const size_t numActorInstances = actorManager->GetNumActorInstances();
for (size_t i = 0; i < numActorInstances; ++i)
{
const ActorInstance* actorInstance = actorManager->GetActorInstance(i);
bool inGameMode = true;
#if defined (EMOTIONFXANIMATION_EDITOR)
// Check if we are in game mode.
IEditor* editor = nullptr;
AzToolsFramework::EditorRequestBus::BroadcastResult(editor, &AzToolsFramework::EditorRequests::GetEditor);
inGameMode = editor->IsInGameMode();
#endif
if (actorInstance && actorInstance->GetIsEnabled() && actorInstance->GetIsOwnedByRuntime())
// Apply the motion extraction deltas to the character controller / entity transform for all entities.
const ActorManager* actorManager = GetEMotionFX().GetActorManager();
const size_t numActorInstances = actorManager->GetNumActorInstances();
for (size_t i = 0; i < numActorInstances; ++i)
{
AZ::Entity* entity = actorInstance->GetEntity();
const Actor* actor = actorInstance->GetActor();
ActorInstance* actorInstance = actorManager->GetActorInstance(i);
if (entity && actor && actor->GetMotionExtractionNode())
// Apply motion extraction only in game mode or in case the actor instance belongs to the Animation Editor.
const bool applyMotionExtraction = inGameMode || !actorInstance->GetIsOwnedByRuntime();
if (applyMotionExtraction)
{
const AZ::EntityId entityId = entity->GetId();
// Check if we have any physics character controllers.
bool hasCustomMotionExtractionController = false;
bool hasPhysicsController = false;
Physics::CharacterRequestBus::EventResult(hasPhysicsController, entityId, &Physics::CharacterRequests::IsPresent);
if (!hasPhysicsController)
{
hasCustomMotionExtractionController = MotionExtractionRequestBus::FindFirstHandler(entityId) != nullptr;
}
// If we have a physics controller.
if (hasCustomMotionExtractionController || hasPhysicsController)
{
const float deltaTimeInv = (delta > 0.0f) ? (1.0f / delta) : 0.0f;
AZ::Transform currentTransform = AZ::Transform::CreateIdentity();
AZ::TransformBus::EventResult(currentTransform, entityId, &AZ::TransformBus::Events::GetWorldTM);
const AZ::Vector3 actorInstancePosition = actorInstance->GetWorldSpaceTransform().m_position;
const AZ::Vector3 positionDelta = actorInstancePosition - currentTransform.GetTranslation();
if (hasPhysicsController)
{
Physics::CharacterRequestBus::Event(
entityId, &Physics::CharacterRequests::AddVelocity, positionDelta * deltaTimeInv);
}
else if (hasCustomMotionExtractionController)
{
MotionExtractionRequestBus::Event(entityId, &MotionExtractionRequestBus::Events::ExtractMotion, positionDelta, delta);
AZ::TransformBus::EventResult(currentTransform, entityId, &AZ::TransformBus::Events::GetWorldTM);
}
// Update the entity rotation.
const AZ::Quaternion actorInstanceRotation = actorInstance->GetWorldSpaceTransform().m_rotation;
const AZ::Quaternion currentRotation = currentTransform.GetRotation();
if (!currentRotation.IsClose(actorInstanceRotation, AZ::Constants::FloatEpsilon))
{
AZ::Transform newTransform = currentTransform;
newTransform.SetRotation(actorInstanceRotation);
AZ::TransformBus::Event(entityId, &AZ::TransformBus::Events::SetWorldTM, newTransform);
}
}
else // There is no physics controller, just use EMotion FX's actor instance transform directly.
{
const AZ::Transform newTransform = actorInstance->GetWorldSpaceTransform().ToAZTransform();
AZ::TransformBus::Event(entityId, &AZ::TransformBus::Events::SetWorldTM, newTransform);
}
actorInstance->SetMotionExtractionEnabled(true);
ApplyMotionExtraction(actorInstance, delta);
}
else
{
actorInstance->SetMotionExtractionEnabled(false);
}
}
}
}
void SystemComponent::ApplyMotionExtraction(const ActorInstance* actorInstance, float timeDelta)
{
AZ_Assert(actorInstance, "Cannot apply motion extraction. Actor instance is not valid.");
AZ_Assert(actorInstance->GetActor(), "Cannot apply motion extraction. Actor instance is not linked to a valid actor.");
AZ::Entity* entity = actorInstance->GetEntity();
const Actor* actor = actorInstance->GetActor();
if (!actorInstance->GetIsEnabled() ||
!entity ||
!actor->GetMotionExtractionNode())
{
return;
}
const AZ::EntityId entityId = entity->GetId();
// Check if we have any physics character controllers.
bool hasCustomMotionExtractionController = false;
bool hasPhysicsController = false;
Physics::CharacterRequestBus::EventResult(hasPhysicsController, entityId, &Physics::CharacterRequests::IsPresent);
if (!hasPhysicsController)
{
hasCustomMotionExtractionController = MotionExtractionRequestBus::FindFirstHandler(entityId) != nullptr;
}
// If we have a physics controller.
if (hasCustomMotionExtractionController || hasPhysicsController)
{
const float deltaTimeInv = (timeDelta > 0.0f) ? (1.0f / timeDelta) : 0.0f;
AZ::Transform currentTransform = AZ::Transform::CreateIdentity();
AZ::TransformBus::EventResult(currentTransform, entityId, &AZ::TransformBus::Events::GetWorldTM);
const AZ::Vector3 actorInstancePosition = actorInstance->GetWorldSpaceTransform().m_position;
const AZ::Vector3 positionDelta = actorInstancePosition - currentTransform.GetTranslation();
if (hasPhysicsController)
{
Physics::CharacterRequestBus::Event(
entityId, &Physics::CharacterRequests::AddVelocity, positionDelta * deltaTimeInv);
}
else if (hasCustomMotionExtractionController)
{
MotionExtractionRequestBus::Event(entityId, &MotionExtractionRequestBus::Events::ExtractMotion, positionDelta, timeDelta);
AZ::TransformBus::EventResult(currentTransform, entityId, &AZ::TransformBus::Events::GetWorldTM);
}
// Update the entity rotation.
const AZ::Quaternion actorInstanceRotation = actorInstance->GetWorldSpaceTransform().m_rotation;
const AZ::Quaternion currentRotation = currentTransform.GetRotation();
if (!currentRotation.IsClose(actorInstanceRotation, AZ::Constants::FloatEpsilon))
{
AZ::Transform newTransform = currentTransform;
newTransform.SetRotation(actorInstanceRotation);
AZ::TransformBus::Event(entityId, &AZ::TransformBus::Events::SetWorldTM, newTransform);
}
}
else // There is no physics controller, just use EMotion FX's actor instance transform directly.
{
const AZ::Transform newTransform = actorInstance->GetWorldSpaceTransform().ToAZTransform();
AZ::TransformBus::Event(entityId, &AZ::TransformBus::Events::SetWorldTM, newTransform);
}
}
int SystemComponent::GetTickOrder()
{
return AZ::TICK_ANIMATION;
@@ -124,6 +124,13 @@ namespace EMotionFX
AZ::u32 m_numThreads;
private:
//! Synchronize the actor instance location with the entity or character controller.
//! In case no character controller component is available, the entity will be moved
//! to the actor instance position. The spatial difference between the entity and the
//! actor instance will be calculated in case a character controller is present, and the
//! velocity will be applied to it to move it towards the actor instance.
void ApplyMotionExtraction(const ActorInstance* actorInstance, float timeDelta);
AZStd::vector<AZStd::unique_ptr<AZ::Data::AssetHandler> > m_assetHandlers;
AZStd::unique_ptr<EMotionFXEventHandler> m_eventHandler;
AZStd::unique_ptr<RenderBackendManager> m_renderBackendManager;