Motion Matching: Debug visualization improvements, enable test builds, updated camera controller for automatic demo #7430

Motion Matching: Debug visualization improvements
* Moved reposibility from the instance to the system component to render debug visualizations. Why? To make sure all motion extraction deltas got applied to the character already and avoid any mismatches between last and current frame (resulting in visual jittering).
* Added frame database stats to the ImGui monitor.
* Switched ImGuiMonitor from internal histogram group to the now shared version in LYImGuiUtils.
* Added a new debug draw bus that the motion matching instance hooks to, so that the system component can control when to render debug visualizations.
* Added class description for the motion matching instance.

Motion Matching: Enable test builds in CMakeLists.txt

Motion Matching: Updated camera controller graph for automatic demo
* Simplified the script canvas graph of the camera controller for the automatic demo.

Signed-off-by: Benjamin Jillich <jillich@amazon.com>
monroegm-disable-blank-issue-2
Benjamin Jillich 4 years ago committed by GitHub
commit 96f77f95fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f974fa29f3542311ee6a4b6fbb92047186c3124b3bd6f88b728646bf9d686c45
size 39099

@ -99,57 +99,49 @@ endif()
################################################################################ ################################################################################
# See if globally, tests are supported # See if globally, tests are supported
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
# We globally support tests, see if we support tests on this platform for MotionMatching.Static ly_add_target(
if(PAL_TRAIT_MOTIONMATCHING_TEST_SUPPORTED) NAME MotionMatching.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
# We support MotionMatching.Tests on this platform, add MotionMatching.Tests target which depends on MotionMatching.Static NAMESPACE Gem
ly_add_target( FILES_CMAKE
NAME MotionMatching.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} motionmatching_files.cmake
NAMESPACE Gem motionmatching_tests_files.cmake
FILES_CMAKE INCLUDE_DIRECTORIES
motionmatching_files.cmake PRIVATE
motionmatching_tests_files.cmake Tests
INCLUDE_DIRECTORIES Source
PRIVATE BUILD_DEPENDENCIES
Tests PRIVATE
Source AZ::AzTest
BUILD_DEPENDENCIES AZ::AzFramework
PRIVATE Gem::EMotionFX.Tests.Static
AZ::AzTest Gem::MotionMatching.Static
AZ::AzFramework )
Gem::EMotionFX.Tests.Static
Gem::MotionMatching.Static
)
# Add MotionMatching.Tests to googletest # Add MotionMatching.Tests to googletest
ly_add_googletest( ly_add_googletest(
NAME Gem::MotionMatching.Tests NAME Gem::MotionMatching.Tests
) )
endif() endif()
# If we are a host platform we want to add tools test like editor tests here # If we are a host platform we want to add tools test like editor tests here
if(PAL_TRAIT_BUILD_HOST_TOOLS) if(PAL_TRAIT_BUILD_HOST_TOOLS)
# We are a host platform, see if Editor tests are supported on this platform ly_add_target(
if(PAL_TRAIT_MOTIONMATCHING_EDITOR_TEST_SUPPORTED) NAME MotionMatching.Editor.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
# We support MotionMatching.Editor.Tests on this platform, add MotionMatching.Editor.Tests target which depends on MotionMatching.Editor NAMESPACE Gem
ly_add_target( FILES_CMAKE
NAME MotionMatching.Editor.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} motionmatching_editor_tests_files.cmake
NAMESPACE Gem INCLUDE_DIRECTORIES
FILES_CMAKE PRIVATE
motionmatching_editor_tests_files.cmake Tests
INCLUDE_DIRECTORIES Source
PRIVATE BUILD_DEPENDENCIES
Tests PRIVATE
Source AZ::AzTest
BUILD_DEPENDENCIES Gem::MotionMatching.Editor
PRIVATE )
AZ::AzTest
Gem::MotionMatching.Editor
)
# Add MotionMatching.Editor.Tests to googletest # Add MotionMatching.Editor.Tests to googletest
ly_add_googletest( ly_add_googletest(
NAME Gem::MotionMatching.Editor.Tests NAME Gem::MotionMatching.Editor.Tests
) )
endif()
endif()
endif() endif()

@ -11,8 +11,20 @@
#include <AzCore/EBus/EBus.h> #include <AzCore/EBus/EBus.h>
#include <AzCore/Interface/Interface.h> #include <AzCore/Interface/Interface.h>
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
namespace EMotionFX::MotionMatching namespace EMotionFX::MotionMatching
{ {
class DebugDrawRequests
: public AZ::EBusTraits
{
public:
AZ_RTTI(DebugDrawRequests, "{7BBA4249-EC00-445C-8A0C-4472841049C3}");
virtual void DebugDraw(AzFramework::DebugDisplayRequests& debugDisplay) = 0;
};
using DebugDrawRequestBus = AZ::EBus<DebugDrawRequests>;
class MotionMatchingRequests class MotionMatchingRequests
{ {
public: public:

@ -297,8 +297,6 @@ namespace EMotionFX::MotionMatching
ImGuiMonitorRequestBus::Broadcast(&ImGuiMonitorRequests::PushPerformanceHistogramValue, "Output", m_outputTimeInMs); ImGuiMonitorRequestBus::Broadcast(&ImGuiMonitorRequests::PushPerformanceHistogramValue, "Output", m_outputTimeInMs);
#endif #endif
} }
instance->DebugDraw();
} }
AZ::Crc32 BlendTreeMotionMatchNode::GetTrajectoryPathSettingsVisibility() const AZ::Crc32 BlendTreeMotionMatchNode::GetTrajectoryPathSettingsVisibility() const

@ -16,10 +16,11 @@ namespace EMotionFX::MotionMatching
ImGuiMonitor::ImGuiMonitor() ImGuiMonitor::ImGuiMonitor()
{ {
m_performanceStats.m_name = "Performance Statistics"; m_performanceStats.SetName("Performance Statistics");
m_performanceStats.SetHistogramBinCount(500);
m_featureCosts.m_name = "Feature Costs"; m_featureCosts.SetName("Feature Costs");
m_featureCosts.m_histogramContainerCount = 100; m_featureCosts.SetHistogramBinCount(100);
ImGui::ImGuiUpdateListenerBus::Handler::BusConnect(); ImGui::ImGuiUpdateListenerBus::Handler::BusConnect();
ImGuiMonitorRequestBus::Handler::BusConnect(); ImGuiMonitorRequestBus::Handler::BusConnect();
@ -40,18 +41,40 @@ namespace EMotionFX::MotionMatching
if (ImGui::Begin("Motion Matching")) if (ImGui::Begin("Motion Matching"))
{ {
if (ImGui::CollapsingHeader("Motion Database", ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_Framed))
{
if (ImGui::BeginTable("MDB", 2))
{
ImGui::TableNextColumn(); ImGui::Text("Memory Usage: %.2f MB", m_frameDatabaseInfo.m_memoryUsedInBytes / 1024.0f / 1024.0f);
ImGui::TableNextColumn(); ImGui::Text("Motion Data: %.0f minutes", m_frameDatabaseInfo.m_motionDataInSeconds / 60.0f);
ImGui::TableNextColumn(); ImGui::Text("Num Frames: %zu", m_frameDatabaseInfo.m_numFrames);
ImGui::TableNextColumn(); ImGui::Text("Num Motions: %zu", m_frameDatabaseInfo.m_numMotions);
ImGui::EndTable();
}
}
if (ImGui::CollapsingHeader("Feature Matrix", ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_Framed)) if (ImGui::CollapsingHeader("Feature Matrix", ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_Framed))
{ {
ImGui::Text("Memory Usage: %.2f MB", m_featureMatrixMemoryUsageInBytes / 1024.0f / 1024.0f); if (ImGui::BeginTable("FM", 2))
ImGui::Text("Num Frames: %zu", m_featureMatrixNumFrames); {
ImGui::Text("Num Feature Components: %zu", m_featureMatrixNumComponents); ImGui::TableNextColumn(); ImGui::Text("Memory Usage: %.2f MB", m_featurMatrixInfo.m_memoryUsedInBytes / 1024.0f / 1024.0f);
ImGui::TableNextColumn();
ImGui::TableNextColumn(); ImGui::Text("Num Frames: %zu", m_featurMatrixInfo.m_numFrames);
ImGui::TableNextColumn(); ImGui::Text("Num Feature Components: %zu", m_featurMatrixInfo.m_numDimensions);
ImGui::EndTable();
}
} }
if (ImGui::CollapsingHeader("Kd-Tree", ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_Framed)) if (ImGui::CollapsingHeader("Kd-Tree", ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_Framed))
{ {
ImGui::Text("Memory Usage: %.2f MB", m_kdTreeMemoryUsageInBytes / 1024.0f / 1024.0f); if (ImGui::BeginTable("KDT", 2))
ImGui::Text("Num Nodes: %zu", m_kdTreeNumNodes); {
ImGui::Text("Num Dimensions: %zu", m_kdTreeNumDimensions); ImGui::TableNextColumn(); ImGui::Text("Memory Usage: %.2f MB", m_kdTreeInfo.m_memoryUsedInBytes / 1024.0f / 1024.0f);
ImGui::TableNextColumn();
ImGui::TableNextColumn(); ImGui::Text("Num Nodes: %zu", m_kdTreeInfo.m_numNodes);
ImGui::TableNextColumn(); ImGui::Text("Num Dimensions: %zu", m_kdTreeInfo.m_numDimensions);
ImGui::EndTable();
}
} }
m_performanceStats.OnImGuiUpdate(); m_performanceStats.OnImGuiUpdate();
@ -63,8 +86,8 @@ namespace EMotionFX::MotionMatching
{ {
if (ImGui::BeginMenu("Motion Matching")) if (ImGui::BeginMenu("Motion Matching"))
{ {
ImGui::MenuItem(m_performanceStats.m_name.c_str(), "", &m_performanceStats.m_show); ImGui::MenuItem(m_performanceStats.GetName(), "", &m_performanceStats.m_show);
ImGui::MenuItem(m_featureCosts.m_name.c_str(), "", &m_featureCosts.m_show); ImGui::MenuItem(m_featureCosts.GetName(), "", &m_featureCosts.m_show);
ImGui::EndMenu(); ImGui::EndMenu();
} }
} }
@ -78,67 +101,6 @@ namespace EMotionFX::MotionMatching
{ {
m_featureCosts.PushHistogramValue(costName, value, color); m_featureCosts.PushHistogramValue(costName, value, color);
} }
void ImGuiMonitor::HistogramGroup::PushHistogramValue(const char* valueName, float value, const AZ::Color& color)
{
auto iterator = m_histogramIndexByName.find(valueName);
if (iterator != m_histogramIndexByName.end())
{
ImGui::LYImGuiUtils::HistogramContainer& histogramContiner = m_histograms[iterator->second];
histogramContiner.PushValue(value);
histogramContiner.SetBarLineColor(ImColor(color.GetR(), color.GetG(), color.GetB(), color.GetA()));
}
else
{
ImGui::LYImGuiUtils::HistogramContainer newHistogram;
newHistogram.Init(/*histogramName=*/valueName,
/*containerCount=*/m_histogramContainerCount,
/*viewType=*/ImGui::LYImGuiUtils::HistogramContainer::ViewType::Histogram,
/*displayOverlays=*/true,
/*min=*/0.0f,
/*max=*/0.0f);
newHistogram.SetMoveDirection(ImGui::LYImGuiUtils::HistogramContainer::PushRightMoveLeft);
newHistogram.PushValue(value);
m_histogramIndexByName[valueName] = m_histograms.size();
m_histograms.push_back(newHistogram);
}
}
void ImGuiMonitor::HistogramGroup::OnImGuiUpdate()
{
if (!m_show)
{
return;
}
if (ImGui::CollapsingHeader(m_name.c_str(), ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_Framed))
{
for (auto& histogram : m_histograms)
{
ImGui::BeginGroup();
{
histogram.Draw(ImGui::GetColumnWidth() - 70, s_histogramHeight);
ImGui::SameLine();
ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(0,0,0,255));
{
const ImColor color = histogram.GetBarLineColor();
ImGui::PushStyleColor(ImGuiCol_Button, color.Value);
{
const AZStd::string valueString = AZStd::string::format("%.2f", histogram.GetLastValue());
ImGui::Button(valueString.c_str());
}
ImGui::PopStyleColor();
}
ImGui::PopStyleColor();
}
ImGui::EndGroup();
}
}
}
} // namespace EMotionFX::MotionMatching } // namespace EMotionFX::MotionMatching
#endif // IMGUI_ENABLED #endif // IMGUI_ENABLED

@ -20,7 +20,7 @@
#include <imgui/imgui.h> #include <imgui/imgui.h>
#include <ImGuiBus.h> #include <ImGuiBus.h>
#include <ImGuiMonitorBus.h> #include <ImGuiMonitorBus.h>
#include <LYImGuiUtils/HistogramContainer.h> #include <LYImGuiUtils/HistogramGroup.h>
namespace EMotionFX::MotionMatching namespace EMotionFX::MotionMatching
{ {
@ -43,41 +43,17 @@ namespace EMotionFX::MotionMatching
void PushPerformanceHistogramValue(const char* performanceMetricName, float value) override; void PushPerformanceHistogramValue(const char* performanceMetricName, float value) override;
void PushCostHistogramValue(const char* costName, float value, const AZ::Color& color) override; void PushCostHistogramValue(const char* costName, float value, const AZ::Color& color) override;
void SetFeatureMatrixMemoryUsage(size_t sizeInBytes) override { m_featureMatrixMemoryUsageInBytes = sizeInBytes; } void SetFrameDatabaseInfo(const ImGuiMonitorRequests::FrameDatabaseInfo& info) override { m_frameDatabaseInfo = info; }
void SetFeatureMatrixNumFrames(size_t numFrames) override { m_featureMatrixNumFrames = numFrames; } void SetFeatureMatrixInfo(const ImGuiMonitorRequests::FeatureMatrixInfo& info) override { m_featurMatrixInfo = info; }
void SetFeatureMatrixNumComponents(size_t numFeatureComponents) override { m_featureMatrixNumComponents = numFeatureComponents; } void SetKdTreeInfo(const ImGuiMonitorRequests::KdTreeInfo& info) override { m_kdTreeInfo = info; }
void SetKdTreeMemoryUsage(size_t sizeInBytes) override { m_kdTreeMemoryUsageInBytes = sizeInBytes; }
void SetKdTreeNumNodes(size_t numNodes) override { m_kdTreeNumNodes = numNodes; }
void SetKdTreeNumDimensions(size_t numDimensions) override { m_kdTreeNumDimensions = numDimensions; }
private: private:
//! Named and sub-divided group containing several histograms. ImGui::LYImGuiUtils::HistogramGroup m_performanceStats;
struct HistogramGroup ImGui::LYImGuiUtils::HistogramGroup m_featureCosts;
{
void OnImGuiUpdate();
void PushHistogramValue(const char* valueName, float value, const AZ::Color& color);
bool m_show = true;
AZStd::string m_name;
using HistogramIndexByNames = AZStd::unordered_map<const char*, size_t>;
HistogramIndexByNames m_histogramIndexByName;
AZStd::vector<ImGui::LYImGuiUtils::HistogramContainer> m_histograms;
int m_histogramContainerCount = 500;
static constexpr float s_histogramHeight = 95.0f;
};
HistogramGroup m_performanceStats;
HistogramGroup m_featureCosts;
size_t m_featureMatrixMemoryUsageInBytes = 0;
size_t m_featureMatrixNumFrames = 0;
size_t m_featureMatrixNumComponents = 0;
size_t m_kdTreeMemoryUsageInBytes = 0; ImGuiMonitorRequests::FrameDatabaseInfo m_frameDatabaseInfo;
size_t m_kdTreeNumNodes = 0; ImGuiMonitorRequests::FeatureMatrixInfo m_featurMatrixInfo;
size_t m_kdTreeNumDimensions = 0; ImGuiMonitorRequests::KdTreeInfo m_kdTreeInfo;
}; };
} // namespace EMotionFX::MotionMatching } // namespace EMotionFX::MotionMatching

@ -25,13 +25,30 @@ namespace EMotionFX::MotionMatching
virtual void PushPerformanceHistogramValue(const char* performanceMetricName, float value) = 0; virtual void PushPerformanceHistogramValue(const char* performanceMetricName, float value) = 0;
virtual void PushCostHistogramValue(const char* costName, float value, const AZ::Color& color) = 0; virtual void PushCostHistogramValue(const char* costName, float value, const AZ::Color& color) = 0;
virtual void SetFeatureMatrixMemoryUsage(size_t sizeInBytes) = 0; struct FrameDatabaseInfo
virtual void SetFeatureMatrixNumFrames(size_t numFrames) = 0; {
virtual void SetFeatureMatrixNumComponents(size_t numFeatureComponents) = 0; size_t m_memoryUsedInBytes = 0;
size_t m_numFrames;
size_t m_numMotions;
float m_motionDataInSeconds;
};
virtual void SetFrameDatabaseInfo(const FrameDatabaseInfo& info) = 0;
virtual void SetKdTreeMemoryUsage(size_t sizeInBytes) = 0; struct FeatureMatrixInfo
virtual void SetKdTreeNumNodes(size_t numNodes) = 0; {
virtual void SetKdTreeNumDimensions(size_t numDimensions) = 0; size_t m_memoryUsedInBytes = 0;
size_t m_numFrames = 0;
size_t m_numDimensions = 0;
};
virtual void SetFeatureMatrixInfo(const FeatureMatrixInfo& info) = 0;
struct KdTreeInfo
{
size_t m_memoryUsedInBytes = 0;
size_t m_numNodes = 0;
size_t m_numDimensions = 0;
};
virtual void SetKdTreeInfo(const KdTreeInfo& info) = 0;
}; };
using ImGuiMonitorRequestBus = AZ::EBus<ImGuiMonitorRequests>; using ImGuiMonitorRequestBus = AZ::EBus<ImGuiMonitorRequests>;
} // namespace EMotionFX::MotionMatching } // namespace EMotionFX::MotionMatching

@ -28,14 +28,14 @@
#include <EMotionFX/Source/TransformData.h> #include <EMotionFX/Source/TransformData.h>
#include <PoseDataJointVelocities.h> #include <PoseDataJointVelocities.h>
#include <EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/ViewportPluginBus.h>
namespace EMotionFX::MotionMatching namespace EMotionFX::MotionMatching
{ {
AZ_CLASS_ALLOCATOR_IMPL(MotionMatchingInstance, MotionMatchAllocator, 0) AZ_CLASS_ALLOCATOR_IMPL(MotionMatchingInstance, MotionMatchAllocator, 0)
MotionMatchingInstance::~MotionMatchingInstance() MotionMatchingInstance::~MotionMatchingInstance()
{ {
DebugDrawRequestBus::Handler::BusDisconnect();
if (m_motionInstance) if (m_motionInstance)
{ {
GetMotionInstancePool().Free(m_motionInstance); GetMotionInstancePool().Free(m_motionInstance);
@ -58,6 +58,8 @@ namespace EMotionFX::MotionMatching
AZ_Assert(settings.m_actorInstance, "The actor instance cannot be a nullptr."); AZ_Assert(settings.m_actorInstance, "The actor instance cannot be a nullptr.");
AZ_Assert(settings.m_data, "The motion match data cannot be nullptr."); AZ_Assert(settings.m_data, "The motion match data cannot be nullptr.");
DebugDrawRequestBus::Handler::BusConnect();
// Update the cached pointer to the trajectory feature. // Update the cached pointer to the trajectory feature.
const FeatureSchema& featureSchema = settings.m_data->GetFeatureSchema(); const FeatureSchema& featureSchema = settings.m_data->GetFeatureSchema();
for (Feature* feature : featureSchema.GetFeatures()) for (Feature* feature : featureSchema.GetFeatures())
@ -69,29 +71,6 @@ namespace EMotionFX::MotionMatching
} }
} }
// Debug display initialization.
const auto AddDebugDisplay = [=](AZ::s32 debugDisplayId)
{
if (debugDisplayId == -1)
{
return;
}
AzFramework::DebugDisplayRequestBus::BusPtr debugDisplayBus;
AzFramework::DebugDisplayRequestBus::Bind(debugDisplayBus, debugDisplayId);
AzFramework::DebugDisplayRequests* debugDisplay = AzFramework::DebugDisplayRequestBus::FindFirstHandler(debugDisplayBus);
if (debugDisplay)
{
m_debugDisplays.emplace_back(debugDisplay);
}
};
// Draw the debug visualizations to the Animation Editor as well as the LY Editor viewport.
AZ::s32 animationEditorViewportId = -1;
EMStudio::ViewportPluginRequestBus::BroadcastResult(animationEditorViewportId, &EMStudio::ViewportPluginRequestBus::Events::GetViewportId);
AddDebugDisplay(animationEditorViewportId);
AddDebugDisplay(AzFramework::g_defaultSceneEntityDebugDisplayId);
m_actorInstance = settings.m_actorInstance; m_actorInstance = settings.m_actorInstance;
m_data = settings.m_data; m_data = settings.m_data;
if (settings.m_data->GetFrameDatabase().GetNumFrames() == 0) if (settings.m_data->GetFrameDatabase().GetNumFrames() == 0)
@ -123,30 +102,17 @@ namespace EMotionFX::MotionMatching
m_queryFeatureValues.resize(numValuesInKdTree); m_queryFeatureValues.resize(numValuesInKdTree);
// Initialize the trajectory history. // Initialize the trajectory history.
size_t rootJointIndex = m_actorInstance->GetActor()->GetMotionExtractionNodeIndex(); if (m_cachedTrajectoryFeature)
if (rootJointIndex == InvalidIndex32)
{ {
rootJointIndex = 0; size_t rootJointIndex = m_actorInstance->GetActor()->GetMotionExtractionNodeIndex();
} if (rootJointIndex == InvalidIndex32)
m_trajectoryHistory.Init(*m_actorInstance->GetTransformData()->GetCurrentPose(),
rootJointIndex,
m_cachedTrajectoryFeature->GetFacingAxisDir(),
m_trajectorySecsToTrack);
}
void MotionMatchingInstance::DebugDraw()
{
if (m_data && !m_debugDisplays.empty())
{
for (AzFramework::DebugDisplayRequests* debugDisplay : m_debugDisplays)
{ {
if (debugDisplay) rootJointIndex = 0;
{
const AZ::u32 prevState = debugDisplay->GetState();
DebugDraw(*debugDisplay);
debugDisplay->SetState(prevState);
}
} }
m_trajectoryHistory.Init(*m_actorInstance->GetTransformData()->GetCurrentPose(),
rootJointIndex,
m_cachedTrajectoryFeature->GetFacingAxisDir(),
m_trajectorySecsToTrack);
} }
} }
@ -306,7 +272,7 @@ namespace EMotionFX::MotionMatching
{ {
AZ_PROFILE_SCOPE(Animation, "MotionMatchingInstance::Update"); AZ_PROFILE_SCOPE(Animation, "MotionMatchingInstance::Update");
if (!m_data) if (!m_data || !m_motionInstance)
{ {
return; return;
} }
@ -322,7 +288,7 @@ namespace EMotionFX::MotionMatching
// Update the time. After this there is no sample for the updated time in the history as we're about to prepare this with the current update. // Update the time. After this there is no sample for the updated time in the history as we're about to prepare this with the current update.
m_trajectoryHistory.Update(timePassedInSeconds); m_trajectoryHistory.Update(timePassedInSeconds);
// Register the current actor instance position to the history data of the spline. // Update the trajectory query control points.
m_trajectoryQuery.Update(m_actorInstance, m_trajectoryQuery.Update(m_actorInstance,
m_cachedTrajectoryFeature, m_cachedTrajectoryFeature,
m_trajectoryHistory, m_trajectoryHistory,
@ -371,8 +337,7 @@ namespace EMotionFX::MotionMatching
SamplePose(m_motionInstance->GetMotion(), m_queryPose, newMotionTime); SamplePose(m_motionInstance->GetMotion(), m_queryPose, newMotionTime);
// Copy over the motion extraction joint transform from the current pose to the newly sampled pose. // Copy over the motion extraction joint transform from the current pose to the newly sampled pose.
// When sampling a motion, the motion extraction joint is in animation space, while we need the query pose to be in // When sampling a motion, the motion extraction joint is in animation space, while we need the query pose to be in world space.
// world space.
// Note: This does not yet take the extraction delta from the current tick into account. // Note: This does not yet take the extraction delta from the current tick into account.
if (m_actorInstance->GetActor()->GetMotionExtractionNode()) if (m_actorInstance->GetActor()->GetMotionExtractionNode())
{ {
@ -433,16 +398,17 @@ namespace EMotionFX::MotionMatching
// ImGui monitor // ImGui monitor
{ {
#ifdef IMGUI_ENABLED #ifdef IMGUI_ENABLED
const KdTree& kdTree = m_data->GetKdTree(); const FrameDatabase& frameDatabase = m_data->GetFrameDatabase();
ImGuiMonitorRequestBus::Broadcast(&ImGuiMonitorRequests::SetKdTreeMemoryUsage, kdTree.CalcMemoryUsageInBytes()); ImGuiMonitorRequests::FrameDatabaseInfo frameDatabaseInfo{frameDatabase.CalcMemoryUsageInBytes(), frameDatabase.GetNumFrames(), frameDatabase.GetNumUsedMotions(), frameDatabase.GetNumFrames() / (float)frameDatabase.GetSampleRate()};
ImGuiMonitorRequestBus::Broadcast(&ImGuiMonitorRequests::SetKdTreeNumNodes, kdTree.GetNumNodes()); ImGuiMonitorRequestBus::Broadcast(&ImGuiMonitorRequests::SetFrameDatabaseInfo, frameDatabaseInfo);
ImGuiMonitorRequestBus::Broadcast(&ImGuiMonitorRequests::SetKdTreeNumDimensions, kdTree.GetNumDimensions());
// TODO: add memory usage for frame database
const KdTree& kdTree = m_data->GetKdTree();
ImGuiMonitorRequests::KdTreeInfo kdTreeInfo{kdTree.CalcMemoryUsageInBytes(), kdTree.GetNumNodes(), kdTree.GetNumDimensions()};
ImGuiMonitorRequestBus::Broadcast(&ImGuiMonitorRequests::SetKdTreeInfo, kdTreeInfo);
const FeatureMatrix& featureMatrix = m_data->GetFeatureMatrix(); const FeatureMatrix& featureMatrix = m_data->GetFeatureMatrix();
ImGuiMonitorRequestBus::Broadcast(&ImGuiMonitorRequests::SetFeatureMatrixMemoryUsage, featureMatrix.CalcMemoryUsageInBytes()); ImGuiMonitorRequests::FeatureMatrixInfo featureMatrixInfo{featureMatrix.CalcMemoryUsageInBytes(), static_cast<size_t>(featureMatrix.rows()), static_cast<size_t>(featureMatrix.cols())};
ImGuiMonitorRequestBus::Broadcast(&ImGuiMonitorRequests::SetFeatureMatrixNumFrames, featureMatrix.rows()); ImGuiMonitorRequestBus::Broadcast(&ImGuiMonitorRequests::SetFeatureMatrixInfo, featureMatrixInfo);
ImGuiMonitorRequestBus::Broadcast(&ImGuiMonitorRequests::SetFeatureMatrixNumComponents, featureMatrix.cols());
#endif #endif
} }
} }

@ -19,6 +19,8 @@
#include <TrajectoryHistory.h> #include <TrajectoryHistory.h>
#include <TrajectoryQuery.h> #include <TrajectoryQuery.h>
#include <MotionMatching/MotionMatchingBus.h>
namespace AZ namespace AZ
{ {
class ReflectContext; class ReflectContext;
@ -34,12 +36,17 @@ namespace EMotionFX::MotionMatching
{ {
class MotionMatchingData; class MotionMatchingData;
//! The instance is where everything comes together. It stores the trajectory history, the trajectory query along with the query vector, knows about the
//! last lowest cost frame frame index and stores the time of the animation that the instance is currently playing. It is responsible for motion extraction,
//! blending towards a new frame in the motion capture database in case the algorithm found a better matching frame and executes the actual search.
class EMFX_API MotionMatchingInstance class EMFX_API MotionMatchingInstance
: public DebugDrawRequestBus::Handler
{ {
public: public:
AZ_RTTI(MotionMatchingInstance, "{1ED03AD8-0FB2-431B-AF01-02F7E930EB73}") AZ_RTTI(MotionMatchingInstance, "{1ED03AD8-0FB2-431B-AF01-02F7E930EB73}")
AZ_CLASS_ALLOCATOR_DECL AZ_CLASS_ALLOCATOR_DECL
MotionMatchingInstance() = default;
virtual ~MotionMatchingInstance(); virtual ~MotionMatchingInstance();
struct EMFX_API InitSettings struct EMFX_API InitSettings
@ -49,8 +56,8 @@ namespace EMotionFX::MotionMatching
}; };
void Init(const InitSettings& settings); void Init(const InitSettings& settings);
void DebugDraw(); // DebugDrawRequestBus::Handler overrides
void DebugDraw(AzFramework::DebugDisplayRequests& debugDisplay); void DebugDraw(AzFramework::DebugDisplayRequests& debugDisplay) override;
void Update(float timePassedInSeconds, const AZ::Vector3& targetPos, const AZ::Vector3& targetFacingDir, TrajectoryQuery::EMode mode, float pathRadius, float pathSpeed); void Update(float timePassedInSeconds, const AZ::Vector3& targetPos, const AZ::Vector3& targetFacingDir, TrajectoryQuery::EMode mode, float pathRadius, float pathSpeed);
void PostUpdate(float timeDelta); void PostUpdate(float timeDelta);
@ -64,10 +71,8 @@ namespace EMotionFX::MotionMatching
void SetLowestCostSearchFrequency(float frequency) { m_lowestCostSearchFrequency = frequency; } void SetLowestCostSearchFrequency(float frequency) { m_lowestCostSearchFrequency = frequency; }
float GetNewMotionTime() const { return m_newMotionTime; } float GetNewMotionTime() const { return m_newMotionTime; }
/** //! Get the cached trajectory feature.
* Get the cached trajectory feature. //! The trajectory feature is searched in the feature schema used in the current instance at init time.
* The trajectory feature is searched in the feature schema used in the current instance at init time.
*/
FeatureTrajectory* GetTrajectoryFeature() const { return m_cachedTrajectoryFeature; } FeatureTrajectory* GetTrajectoryFeature() const { return m_cachedTrajectoryFeature; }
const TrajectoryQuery& GetTrajectoryQuery() const { return m_trajectoryQuery; } const TrajectoryQuery& GetTrajectoryQuery() const { return m_trajectoryQuery; }
const TrajectoryHistory& GetTrajectoryHistory() const { return m_trajectoryHistory; } const TrajectoryHistory& GetTrajectoryHistory() const { return m_trajectoryHistory; }
@ -90,10 +95,10 @@ namespace EMotionFX::MotionMatching
Transform m_motionExtractionDelta = Transform::CreateIdentity(); Transform m_motionExtractionDelta = Transform::CreateIdentity();
/// Buffers used for the broad-phase KD-tree search. /// Buffers used for the broad-phase KD-tree search.
AZStd::vector<float> m_queryFeatureValues; /** The input query features to be compared to every entry/row in the feature matrix with the motion matching search. */ AZStd::vector<float> m_queryFeatureValues; //< The input query features to be compared to every entry/row in the feature matrix with the motion matching search.
AZStd::vector<size_t> m_nearestFrames; /** Stores the nearest matching frames / search result from the KD-tree. */ AZStd::vector<size_t> m_nearestFrames; //< Stores the nearest matching frames / search result from the KD-tree.
FeatureTrajectory* m_cachedTrajectoryFeature = nullptr; /** Cached pointer to the trajectory feature in the feature schema. */ FeatureTrajectory* m_cachedTrajectoryFeature = nullptr; //< Cached pointer to the trajectory feature in the feature schema.
TrajectoryQuery m_trajectoryQuery; TrajectoryQuery m_trajectoryQuery;
TrajectoryHistory m_trajectoryHistory; TrajectoryHistory m_trajectoryHistory;
static constexpr float m_trajectorySecsToTrack = 5.0f; static constexpr float m_trajectorySecsToTrack = 5.0f;
@ -105,7 +110,7 @@ namespace EMotionFX::MotionMatching
bool m_blending = false; bool m_blending = false;
float m_blendWeight = 1.0f; float m_blendWeight = 1.0f;
float m_blendProgressTime = 0.0f; // How long are we already blending? In seconds. float m_blendProgressTime = 0.0f; //< How long are we already blending? In seconds.
/// Buffers used for FindLowestCostFrameIndex(). /// Buffers used for FindLowestCostFrameIndex().
AZStd::vector<float> m_tempCosts; AZStd::vector<float> m_tempCosts;

@ -16,6 +16,8 @@
#include <Integration/EMotionFXBus.h> #include <Integration/EMotionFXBus.h>
#include <EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/ViewportPluginBus.h>
#include <BlendTreeMotionMatchNode.h> #include <BlendTreeMotionMatchNode.h>
#include <Feature.h> #include <Feature.h>
#include <FeaturePosition.h> #include <FeaturePosition.h>
@ -39,7 +41,7 @@ namespace EMotionFX::MotionMatching
{ {
ec->Class<MotionMatchingSystemComponent>("MotionMatching", "[Description of functionality provided by this System Component]") ec->Class<MotionMatchingSystemComponent>("MotionMatching", "[Description of functionality provided by this System Component]")
->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("System")) ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("System"))
->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
; ;
} }
@ -69,9 +71,9 @@ namespace EMotionFX::MotionMatching
incompatible.push_back(AZ_CRC_CE("MotionMatchingService")); incompatible.push_back(AZ_CRC_CE("MotionMatchingService"));
} }
void MotionMatchingSystemComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required) void MotionMatchingSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{ {
required.push_back(AZ_CRC("EMotionFXAnimationService", 0x3f8a6369)); required.push_back(AZ_CRC_CE("EMotionFXAnimationService"));
} }
void MotionMatchingSystemComponent::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent) void MotionMatchingSystemComponent::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent)
@ -122,7 +124,33 @@ namespace EMotionFX::MotionMatching
MotionMatchingRequestBus::Handler::BusDisconnect(); MotionMatchingRequestBus::Handler::BusDisconnect();
} }
void MotionMatchingSystemComponent::DebugDraw(AZ::s32 debugDisplayId)
{
AZ_PROFILE_SCOPE(Animation, "MotionMatchingSystemComponent::DebugDraw");
if (debugDisplayId == -1)
{
return;
}
AzFramework::DebugDisplayRequestBus::BusPtr debugDisplayBus;
AzFramework::DebugDisplayRequestBus::Bind(debugDisplayBus, debugDisplayId);
AzFramework::DebugDisplayRequests* debugDisplay = AzFramework::DebugDisplayRequestBus::FindFirstHandler(debugDisplayBus);
if (debugDisplay)
{
const AZ::u32 prevState = debugDisplay->GetState();
EMotionFX::MotionMatching::DebugDrawRequestBus::Broadcast(&EMotionFX::MotionMatching::DebugDrawRequests::DebugDraw, *debugDisplay);
debugDisplay->SetState(prevState);
}
}
void MotionMatchingSystemComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time) void MotionMatchingSystemComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
{ {
// Draw the debug visualizations to the Animation Editor as well as the LY Editor viewport.
AZ::s32 animationEditorViewportId = -1;
EMStudio::ViewportPluginRequestBus::BroadcastResult(animationEditorViewportId, &EMStudio::ViewportPluginRequestBus::Events::GetViewportId);
DebugDraw(animationEditorViewportId);
DebugDraw(AzFramework::g_defaultSceneEntityDebugDisplayId);
} }
} // namespace EMotionFX::MotionMatching } // namespace EMotionFX::MotionMatching

@ -33,8 +33,7 @@ namespace EMotionFX::MotionMatching
~MotionMatchingSystemComponent(); ~MotionMatchingSystemComponent();
protected: protected:
//////////////////////////////////////////////////////////////////////// void DebugDraw(AZ::s32 debugDisplayId);
// MotionMatchingRequestBus interface implementation
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
// AZ::Component interface implementation // AZ::Component interface implementation
@ -45,6 +44,10 @@ namespace EMotionFX::MotionMatching
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
// AZTickBus interface implementation // AZTickBus interface implementation
int GetTickOrder() override
{
return AZ::TICK_PRE_RENDER;
}
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override; void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
}; };

Loading…
Cancel
Save