BlendTreeMotionMatchNode.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <AzCore/Serialization/EditContext.h>
  9. #include <AzCore/Serialization/SerializeContext.h>
  10. #include <AzCore/std/smart_ptr/make_shared.h>
  11. #include <EMotionFX/Source/AnimGraph.h>
  12. #include <EMotionFX/Source/AnimGraphManager.h>
  13. #include <EMotionFX/Source/EventManager.h>
  14. #include <EMotionFX/Source/Motion.h>
  15. #include <BlendTreeMotionMatchNode.h>
  16. #include <FeatureSchemaDefault.h>
  17. #include <MotionMatching/MotionMatchingBus.h>
  18. #include <EMotionFX/Source/MotionSet.h>
  19. #include <EMotionFX/Source/Node.h>
  20. #include <EMotionFX/Source/Recorder.h>
  21. #include <EMotionFX/Source/TransformData.h>
  22. #include <FeaturePosition.h>
  23. namespace EMotionFX::MotionMatching
  24. {
  25. AZ_CLASS_ALLOCATOR_IMPL(BlendTreeMotionMatchNode, AnimGraphAllocator)
  26. AZ_CLASS_ALLOCATOR_IMPL(BlendTreeMotionMatchNode::UniqueData, AnimGraphObjectUniqueDataAllocator)
  27. BlendTreeMotionMatchNode::BlendTreeMotionMatchNode()
  28. : AnimGraphNode()
  29. {
  30. // Setup the input ports.
  31. InitInputPorts(3);
  32. SetupInputPort("Goal Pos", INPUTPORT_TARGETPOS, MCore::AttributeVector3::TYPE_ID, PORTID_INPUT_TARGETPOS);
  33. SetupInputPort("Goal Facing Dir", INPUTPORT_TARGETFACINGDIR, MCore::AttributeVector3::TYPE_ID, PORTID_INPUT_TARGETFACINGDIR);
  34. SetupInputPort("Use Facing Dir", INPUTPORT_USEFACINGDIR, MCore::AttributeBool::TYPE_ID, PORTID_INPUT_USEFACINGDIR);
  35. // Setup the output ports.
  36. InitOutputPorts(1);
  37. SetupOutputPortAsPose("Output Pose", OUTPUTPORT_POSE, PORTID_OUTPUT_POSE);
  38. }
  39. BlendTreeMotionMatchNode::~BlendTreeMotionMatchNode()
  40. {
  41. FeatureSchema* usedSchema = nullptr;
  42. MotionMatchingEditorRequestBus::BroadcastResult(usedSchema, &MotionMatchingEditorRequests::GetDebugDrawFeatureSchema);
  43. if (usedSchema == &m_featureSchema)
  44. {
  45. MotionMatchingEditorRequestBus::Broadcast(&MotionMatchingEditorRequests::SetDebugDrawFeatureSchema, nullptr);
  46. }
  47. }
  48. bool BlendTreeMotionMatchNode::InitAfterLoading(AnimGraph* animGraph)
  49. {
  50. if (!AnimGraphNode::InitAfterLoading(animGraph))
  51. {
  52. return false;
  53. }
  54. // Automatically register the default feature schema in case the schema is empty after loading the node.
  55. if (m_featureSchema.GetNumFeatures() == 0)
  56. {
  57. AZStd::string rootJointName;
  58. if (m_animGraph->GetNumAnimGraphInstances() > 0)
  59. {
  60. const Actor* actor = m_animGraph->GetAnimGraphInstance(0)->GetActorInstance()->GetActor();
  61. const Node* rootJoint = actor->GetMotionExtractionNode();
  62. if (rootJoint)
  63. {
  64. rootJointName = rootJoint->GetNameString();
  65. }
  66. }
  67. DefaultFeatureSchemaInitSettings defaultSettings;
  68. defaultSettings.m_rootJointName = rootJointName.c_str();
  69. defaultSettings.m_leftFootJointName = "L_foot_JNT";
  70. defaultSettings.m_rightFootJointName = "R_foot_JNT";
  71. defaultSettings.m_pelvisJointName = "C_pelvis_JNT";
  72. DefaultFeatureSchema(m_featureSchema, defaultSettings);
  73. }
  74. InitInternalAttributesForAllInstances();
  75. Reinit();
  76. return true;
  77. }
  78. const char* BlendTreeMotionMatchNode::GetPaletteName() const
  79. {
  80. return "Motion Matching";
  81. }
  82. AnimGraphObject::ECategory BlendTreeMotionMatchNode::GetPaletteCategory() const
  83. {
  84. return AnimGraphObject::CATEGORY_SOURCES;
  85. }
  86. void BlendTreeMotionMatchNode::UniqueData::Update()
  87. {
  88. AZ_PROFILE_SCOPE(Animation, "BlendTreeMotionMatchNode::UniqueData::Update");
  89. auto animGraphNode = azdynamic_cast<BlendTreeMotionMatchNode*>(m_object);
  90. AZ_Assert(animGraphNode, "Unique data linked to incorrect node type.");
  91. ActorInstance* actorInstance = m_animGraphInstance->GetActorInstance();
  92. // Clear existing data.
  93. delete m_instance;
  94. delete m_data;
  95. m_data = aznew MotionMatching::MotionMatchingData(animGraphNode->m_featureSchema);
  96. m_instance = aznew MotionMatching::MotionMatchingInstance();
  97. MotionSet* motionSet = m_animGraphInstance->GetMotionSet();
  98. if (!motionSet)
  99. {
  100. SetHasError(true);
  101. return;
  102. }
  103. //---------------------------------
  104. AZ::Debug::Timer timer;
  105. timer.Stamp();
  106. // Build a list of motions we want to import the frames from.
  107. AZ_Printf("Motion Matching", "Importing motion database...");
  108. MotionMatching::MotionMatchingData::InitSettings settings;
  109. settings.m_actorInstance = actorInstance;
  110. settings.m_frameImportSettings.m_sampleRate = animGraphNode->m_sampleRate;
  111. settings.m_importMirrored = animGraphNode->m_mirror;
  112. settings.m_maxKdTreeDepth = animGraphNode->m_maxKdTreeDepth;
  113. settings.m_minFramesPerKdTreeNode = animGraphNode->m_minFramesPerKdTreeNode;
  114. settings.m_motionList.reserve(animGraphNode->m_motionIds.size());
  115. settings.m_normalizeData = animGraphNode->m_normalizeData;
  116. settings.m_featureScalerType = animGraphNode->m_featureScalerType;
  117. settings.m_featureTansformerSettings.m_featureMin = animGraphNode->m_featureMin;
  118. settings.m_featureTansformerSettings.m_featureMax = animGraphNode->m_featureMax;
  119. settings.m_featureTansformerSettings.m_clip = animGraphNode->m_clipFeatures;
  120. for (const AZStd::string& id : animGraphNode->m_motionIds)
  121. {
  122. Motion* motion = motionSet->RecursiveFindMotionById(id);
  123. if (motion)
  124. {
  125. settings.m_motionList.emplace_back(motion);
  126. }
  127. else
  128. {
  129. AZ_Warning("Motion Matching", false, "Failed to get motion for motionset entry id '%s'", id.c_str());
  130. }
  131. }
  132. // Initialize the motion matching data (slow).
  133. AZ_Printf("Motion Matching", "Initializing motion matching...");
  134. if (!m_data->Init(settings))
  135. {
  136. AZ_Warning("Motion Matching", false, "Failed to initialize motion matching for anim graph node '%s'!", animGraphNode->GetName());
  137. SetHasError(true);
  138. return;
  139. }
  140. // Initialize the instance.
  141. AZ_Printf("Motion Matching", "Initializing instance...");
  142. MotionMatching::MotionMatchingInstance::InitSettings initSettings;
  143. initSettings.m_actorInstance = actorInstance;
  144. initSettings.m_data = m_data;
  145. m_instance->Init(initSettings);
  146. const float initTime = timer.GetDeltaTimeInSeconds();
  147. const size_t memUsage = m_data->GetFrameDatabase().CalcMemoryUsageInBytes();
  148. AZ_Printf("Motion Matching", "Finished in %.2f seconds (mem usage=%d bytes or %.2f mb)", initTime, memUsage, memUsage / (float)(1024 * 1024));
  149. //---------------------------------
  150. SetHasError(false);
  151. }
  152. void BlendTreeMotionMatchNode::Update(AnimGraphInstance* animGraphInstance, float timePassedInSeconds)
  153. {
  154. AZ_PROFILE_SCOPE(Animation, "BlendTreeMotionMatchNode::Update");
  155. m_timer.Stamp();
  156. UniqueData* uniqueData = static_cast<UniqueData*>(FindOrCreateUniqueNodeData(animGraphInstance));
  157. UpdateAllIncomingNodes(animGraphInstance, timePassedInSeconds);
  158. uniqueData->Clear();
  159. if (uniqueData->GetHasError())
  160. {
  161. m_updateTimeInMs = 0.0f;
  162. m_postUpdateTimeInMs = 0.0f;
  163. m_outputTimeInMs = 0.0f;
  164. return;
  165. }
  166. AZ::Vector3 targetPos = AZ::Vector3::CreateZero();
  167. TryGetInputVector3(animGraphInstance, INPUTPORT_TARGETPOS, targetPos);
  168. AZ::Vector3 targetFacingDir = AZ::Vector3::CreateAxisY();
  169. TryGetInputVector3(animGraphInstance, INPUTPORT_TARGETFACINGDIR, targetFacingDir);
  170. bool useFacingDir = GetInputNumberAsBool(animGraphInstance, INPUTPORT_USEFACINGDIR);
  171. MotionMatching::MotionMatchingInstance* instance = uniqueData->m_instance;
  172. instance->Update(timePassedInSeconds, targetPos, targetFacingDir, useFacingDir, m_trajectoryQueryMode, m_pathRadius, m_pathSpeed);
  173. // set the current time to the new calculated time
  174. uniqueData->ClearInheritFlags();
  175. if (instance->GetMotionInstance())
  176. {
  177. uniqueData->SetPreSyncTime(instance->GetMotionInstance()->GetCurrentTime());
  178. }
  179. uniqueData->SetCurrentPlayTime(instance->GetNewMotionTime());
  180. if (uniqueData->GetPreSyncTime() > uniqueData->GetCurrentPlayTime())
  181. {
  182. uniqueData->SetPreSyncTime(uniqueData->GetCurrentPlayTime());
  183. }
  184. m_updateTimeInMs = m_timer.GetDeltaTimeInSeconds() * 1000.0f;
  185. }
  186. void BlendTreeMotionMatchNode::PostUpdate(AnimGraphInstance* animGraphInstance, float timePassedInSeconds)
  187. {
  188. AZ_PROFILE_SCOPE(Animation, "BlendTreeMotionMatchNode::PostUpdate");
  189. AZ_UNUSED(animGraphInstance);
  190. AZ_UNUSED(timePassedInSeconds);
  191. m_timer.Stamp();
  192. for (AZ::u32 i = 0; i < GetNumConnections(); ++i)
  193. {
  194. AnimGraphNode* node = GetConnection(i)->GetSourceNode();
  195. node->PerformPostUpdate(animGraphInstance, timePassedInSeconds);
  196. }
  197. UniqueData* uniqueData = static_cast<UniqueData*>(FindOrCreateUniqueNodeData(animGraphInstance));
  198. MotionMatching::MotionMatchingInstance* instance = uniqueData->m_instance;
  199. RequestRefDatas(animGraphInstance);
  200. AnimGraphRefCountedData* data = uniqueData->GetRefCountedData();
  201. data->ClearEventBuffer();
  202. data->ZeroTrajectoryDelta();
  203. if (uniqueData->GetHasError())
  204. {
  205. return;
  206. }
  207. MotionInstance* motionInstance = instance->GetMotionInstance();
  208. if (motionInstance)
  209. {
  210. motionInstance->UpdateByTimeValues(uniqueData->GetPreSyncTime(), uniqueData->GetCurrentPlayTime(), &data->GetEventBuffer());
  211. uniqueData->SetCurrentPlayTime(motionInstance->GetCurrentTime());
  212. }
  213. data->GetEventBuffer().UpdateEmitters(this);
  214. instance->PostUpdate(timePassedInSeconds);
  215. const Transform& trajectoryDelta = instance->GetMotionExtractionDelta();
  216. data->SetTrajectoryDelta(trajectoryDelta);
  217. data->SetTrajectoryDeltaMirrored(trajectoryDelta); // TODO: use a real mirrored version here.
  218. m_postUpdateTimeInMs = m_timer.GetDeltaTimeInSeconds() * 1000.0f;
  219. }
  220. void BlendTreeMotionMatchNode::Output(AnimGraphInstance* animGraphInstance)
  221. {
  222. AZ_PROFILE_SCOPE(Animation, "BlendTreeMotionMatchNode::Output");
  223. AZ_UNUSED(animGraphInstance);
  224. m_timer.Stamp();
  225. // Initialize to bind pose.
  226. ActorInstance* actorInstance = animGraphInstance->GetActorInstance();
  227. RequestPoses(animGraphInstance);
  228. AnimGraphPose* outputPose = GetOutputPose(animGraphInstance, OUTPUTPORT_POSE)->GetValue();
  229. outputPose->InitFromBindPose(actorInstance);
  230. if (m_disabled)
  231. {
  232. return;
  233. }
  234. UniqueData* uniqueData = static_cast<UniqueData*>(FindOrCreateUniqueNodeData(animGraphInstance));
  235. if (GetEMotionFX().GetIsInEditorMode())
  236. {
  237. SetHasError(uniqueData, uniqueData->GetHasError());
  238. }
  239. if (uniqueData->GetHasError())
  240. {
  241. return;
  242. }
  243. OutputIncomingNode(animGraphInstance, GetInputNode(INPUTPORT_TARGETPOS));
  244. OutputIncomingNode(animGraphInstance, GetInputNode(INPUTPORT_TARGETFACINGDIR));
  245. MotionMatching::MotionMatchingInstance* instance = uniqueData->m_instance;
  246. instance->SetLowestCostSearchFrequency(m_lowestCostSearchFrequency);
  247. Pose& outTransformPose = outputPose->GetPose();
  248. instance->Output(outTransformPose);
  249. // Performance metrics
  250. m_outputTimeInMs = m_timer.GetDeltaTimeInSeconds() * 1000.0f;
  251. {
  252. #ifdef IMGUI_ENABLED
  253. ImGuiMonitorRequestBus::Broadcast(&ImGuiMonitorRequests::PushPerformanceHistogramValue, "Update", m_updateTimeInMs);
  254. ImGuiMonitorRequestBus::Broadcast(&ImGuiMonitorRequests::PushPerformanceHistogramValue, "Post Update", m_postUpdateTimeInMs);
  255. ImGuiMonitorRequestBus::Broadcast(&ImGuiMonitorRequests::PushPerformanceHistogramValue, "Output", m_outputTimeInMs);
  256. #endif
  257. }
  258. }
  259. AZ::Crc32 BlendTreeMotionMatchNode::GetTrajectoryPathSettingsVisibility() const
  260. {
  261. if (m_trajectoryQueryMode == TrajectoryQuery::MODE_TARGETDRIVEN)
  262. {
  263. return AZ::Edit::PropertyVisibility::Hide;
  264. }
  265. return AZ::Edit::PropertyVisibility::Show;
  266. }
  267. AZ::Crc32 BlendTreeMotionMatchNode::GetFeatureScalerTypeSettingsVisibility() const
  268. {
  269. if (m_normalizeData)
  270. {
  271. return AZ::Edit::PropertyVisibility::Show;
  272. }
  273. return AZ::Edit::PropertyVisibility::Hide;
  274. }
  275. AZ::Crc32 BlendTreeMotionMatchNode::GetMinMaxSettingsVisibility() const
  276. {
  277. if (m_normalizeData && m_featureScalerType == MotionMatchingData::MinMaxScalerType)
  278. {
  279. return AZ::Edit::PropertyVisibility::Show;
  280. }
  281. return AZ::Edit::PropertyVisibility::Hide;
  282. }
  283. AZ::Crc32 BlendTreeMotionMatchNode::OnVisualizeSchemaButtonClicked()
  284. {
  285. FeatureSchema* usedSchema = nullptr;
  286. MotionMatchingEditorRequestBus::BroadcastResult(usedSchema, &MotionMatchingEditorRequests::GetDebugDrawFeatureSchema);
  287. if (usedSchema == &m_featureSchema)
  288. {
  289. MotionMatchingEditorRequestBus::Broadcast(&MotionMatchingEditorRequests::SetDebugDrawFeatureSchema, nullptr);
  290. }
  291. else
  292. {
  293. MotionMatchingEditorRequestBus::Broadcast(&MotionMatchingEditorRequests::SetDebugDrawFeatureSchema, &m_featureSchema);
  294. }
  295. return AZ::Edit::PropertyRefreshLevels::AttributesAndValues;
  296. }
  297. AZStd::string BlendTreeMotionMatchNode::OnVisualizeSchemaButtonText() const
  298. {
  299. FeatureSchema* usedSchema = nullptr;
  300. MotionMatchingEditorRequestBus::BroadcastResult(usedSchema, &MotionMatchingEditorRequests::GetDebugDrawFeatureSchema);
  301. if (usedSchema == &m_featureSchema)
  302. {
  303. return "Disable Visualize Feature Schema";
  304. }
  305. return "Enable Visualize Feature Schema";
  306. }
  307. void BlendTreeMotionMatchNode::Reflect(AZ::ReflectContext* context)
  308. {
  309. AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
  310. if (!serializeContext)
  311. {
  312. return;
  313. }
  314. serializeContext->Class<BlendTreeMotionMatchNode, AnimGraphNode>()
  315. ->Version(11)
  316. ->Field("lowestCostSearchFrequency", &BlendTreeMotionMatchNode::m_lowestCostSearchFrequency)
  317. ->Field("sampleRate", &BlendTreeMotionMatchNode::m_sampleRate)
  318. ->Field("controlSplineMode", &BlendTreeMotionMatchNode::m_trajectoryQueryMode)
  319. ->Field("pathRadius", &BlendTreeMotionMatchNode::m_pathRadius)
  320. ->Field("pathSpeed", &BlendTreeMotionMatchNode::m_pathSpeed)
  321. ->Field("normalizeData", &BlendTreeMotionMatchNode::m_normalizeData)
  322. ->Field("featureMin", &BlendTreeMotionMatchNode::m_featureMin)
  323. ->Field("featureMax", &BlendTreeMotionMatchNode::m_featureMax)
  324. ->Field("clipFeatures", &BlendTreeMotionMatchNode::m_clipFeatures)
  325. ->Field("maxKdTreeDepth", &BlendTreeMotionMatchNode::m_maxKdTreeDepth)
  326. ->Field("minFramesPerKdTreeNode", &BlendTreeMotionMatchNode::m_minFramesPerKdTreeNode)
  327. ->Field("mirror", &BlendTreeMotionMatchNode::m_mirror)
  328. ->Field("featureSchema", &BlendTreeMotionMatchNode::m_featureSchema)
  329. ->Field("motionIds", &BlendTreeMotionMatchNode::m_motionIds)
  330. ->Field("featureScalerType", &BlendTreeMotionMatchNode::m_featureScalerType)
  331. ;
  332. AZ::EditContext* editContext = serializeContext->GetEditContext();
  333. if (!editContext)
  334. {
  335. return;
  336. }
  337. editContext->Class<BlendTreeMotionMatchNode>("Motion Matching Node", "Motion Matching Attributes")
  338. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  339. ->Attribute(AZ::Edit::Attributes::AutoExpand, "")
  340. ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
  341. ->DataElement(AZ::Edit::UIHandlers::Default, &BlendTreeMotionMatchNode::m_lowestCostSearchFrequency, "Search frequency", "How often per second we apply the motion matching search and find the lowest cost / best matching frame, and start to blend towards it.")
  342. ->Attribute(AZ::Edit::Attributes::Min, 0.001f)
  343. ->Attribute(AZ::Edit::Attributes::Max, std::numeric_limits<float>::max())
  344. ->Attribute(AZ::Edit::Attributes::Step, 0.05f)
  345. ->DataElement(AZ::Edit::UIHandlers::Default, &BlendTreeMotionMatchNode::m_sampleRate, "Feature sample rate", "The sample rate (in Hz) used for extracting the features from the animations. The higher the sample rate, the more data will be used and the more options the motion matching search has available for the best matching frame.")
  346. ->Attribute(AZ::Edit::Attributes::Min, 1)
  347. ->Attribute(AZ::Edit::Attributes::Max, 240)
  348. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &BlendTreeMotionMatchNode::Reinit)
  349. ->DataElement(AZ::Edit::UIHandlers::ComboBox, &BlendTreeMotionMatchNode::m_trajectoryQueryMode, "Trajectory Prediction", "Desired future trajectory generation mode.")
  350. ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
  351. ->EnumAttribute(TrajectoryQuery::MODE_TARGETDRIVEN, "Target-driven")
  352. ->EnumAttribute(TrajectoryQuery::MODE_AUTOMATIC, "Automatic (Demo)")
  353. ->DataElement(AZ::Edit::UIHandlers::Default, &BlendTreeMotionMatchNode::m_pathRadius, "Path radius", "")
  354. ->Attribute(AZ::Edit::Attributes::Visibility, &BlendTreeMotionMatchNode::GetTrajectoryPathSettingsVisibility)
  355. ->Attribute(AZ::Edit::Attributes::Min, 0.0001f)
  356. ->Attribute(AZ::Edit::Attributes::Max, std::numeric_limits<float>::max())
  357. ->Attribute(AZ::Edit::Attributes::Step, 0.01f)
  358. ->DataElement(AZ::Edit::UIHandlers::Default, &BlendTreeMotionMatchNode::m_pathSpeed, "Path speed", "")
  359. ->Attribute(AZ::Edit::Attributes::Visibility, &BlendTreeMotionMatchNode::GetTrajectoryPathSettingsVisibility)
  360. ->Attribute(AZ::Edit::Attributes::Min, 0.0001f)
  361. ->Attribute(AZ::Edit::Attributes::Max, std::numeric_limits<float>::max())
  362. ->Attribute(AZ::Edit::Attributes::Step, 0.01f)
  363. ->ClassElement(AZ::Edit::ClassElements::Group, "Data Normalization")
  364. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  365. ->DataElement(AZ::Edit::UIHandlers::Default, &BlendTreeMotionMatchNode::m_normalizeData, "Normalize Data", "Normalize feature data for more intuitive control over weighting the cost factors.")
  366. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &BlendTreeMotionMatchNode::Reinit)
  367. ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
  368. ->DataElement(AZ::Edit::UIHandlers::ComboBox, &BlendTreeMotionMatchNode::m_featureScalerType, "Type", "Feature scaler type to be used to normalize the data.")
  369. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &BlendTreeMotionMatchNode::Reinit)
  370. ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
  371. ->Attribute(AZ::Edit::Attributes::Visibility, &BlendTreeMotionMatchNode::GetFeatureScalerTypeSettingsVisibility)
  372. ->EnumAttribute(MotionMatchingData::StandardScalerType, "Standard Scaler")
  373. ->EnumAttribute(MotionMatchingData::MinMaxScalerType, "Min-max Scaler")
  374. ->DataElement(AZ::Edit::UIHandlers::Default, &BlendTreeMotionMatchNode::m_featureMin, "Feature Minimum", "Minimum value after data transformation.")
  375. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &BlendTreeMotionMatchNode::Reinit)
  376. ->Attribute(AZ::Edit::Attributes::Visibility, &BlendTreeMotionMatchNode::GetMinMaxSettingsVisibility)
  377. ->DataElement(AZ::Edit::UIHandlers::Default, &BlendTreeMotionMatchNode::m_featureMax, "Feature Maximum", "Maximum value after data transformation.")
  378. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &BlendTreeMotionMatchNode::Reinit)
  379. ->Attribute(AZ::Edit::Attributes::Visibility, &BlendTreeMotionMatchNode::GetMinMaxSettingsVisibility)
  380. ->DataElement(AZ::Edit::UIHandlers::Default, &BlendTreeMotionMatchNode::m_clipFeatures, "Clip Features", "Clip feature values for outliers to the above range.")
  381. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &BlendTreeMotionMatchNode::Reinit)
  382. ->Attribute(AZ::Edit::Attributes::Visibility, &BlendTreeMotionMatchNode::GetMinMaxSettingsVisibility)
  383. ->ClassElement(AZ::Edit::ClassElements::Group, "Acceleration Structure")
  384. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  385. ->DataElement(AZ::Edit::UIHandlers::Default, &BlendTreeMotionMatchNode::m_maxKdTreeDepth, "Max kd-tree depth", "The maximum number of hierarchy levels in the kdTree.")
  386. ->Attribute(AZ::Edit::Attributes::Min, 1)
  387. ->Attribute(AZ::Edit::Attributes::Max, 20)
  388. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &BlendTreeMotionMatchNode::Reinit)
  389. ->DataElement(AZ::Edit::UIHandlers::Default, &BlendTreeMotionMatchNode::m_minFramesPerKdTreeNode, "Min kd-tree node size", "The minimum number of frames to store per kdTree node.")
  390. ->Attribute(AZ::Edit::Attributes::Min, 1)
  391. ->Attribute(AZ::Edit::Attributes::Max, 100000)
  392. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &BlendTreeMotionMatchNode::Reinit)
  393. ->EndGroup()
  394. ->DataElement(AZ::Edit::UIHandlers::Default, &BlendTreeMotionMatchNode::m_featureSchema, "FeatureSchema", "")
  395. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &BlendTreeMotionMatchNode::Reinit)
  396. ->UIElement(AZ::Edit::UIHandlers::Button, "", "")
  397. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &BlendTreeMotionMatchNode::OnVisualizeSchemaButtonClicked)
  398. ->Attribute(AZ::Edit::Attributes::ButtonText, &BlendTreeMotionMatchNode::OnVisualizeSchemaButtonText)
  399. ->DataElement(AZ_CRC_CE("MotionSetMotionIds"), &BlendTreeMotionMatchNode::m_motionIds, "Motions", "")
  400. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &BlendTreeMotionMatchNode::Reinit)
  401. ->Attribute(AZ::Edit::Attributes::ContainerCanBeModified, false)
  402. ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::HideChildren)
  403. ;
  404. }
  405. } // namespace EMotionFX::MotionMatching