Started working on evaluating embedded blend trees.

RefactorUnifiedBlendTreeStateMachineHandling
Martin Felis 2024-03-25 22:26:29 +01:00
parent 99f11e61d8
commit 0aebe44bd5
7 changed files with 96 additions and 37 deletions

View File

@ -73,7 +73,7 @@ void AnimGraphBlendTree::MarkActiveInputs() {
}
}
for (size_t i = m_eval_ordered_nodes.size() - 1; i > 0; i--) {
for (size_t i = m_eval_ordered_nodes.size() - 1; i >= 0; i--) {
AnimNode* node = m_eval_ordered_nodes[i];
if (checkIsNodeActive(node)) {
node->MarkActiveInputs();

View File

@ -29,10 +29,10 @@ struct AnimGraphBlendTree : public AnimNode {
AnimDataAllocator m_anim_data_allocator;
~AnimGraphBlendTree() { dealloc(); }
~AnimGraphBlendTree() override { dealloc(); }
// AnimNode overrides
bool Init(AnimGraphContext& context);
bool Init(AnimGraphContext& context) override;
void MarkActiveInputs() override;
void CalcSyncTrack() override;
void UpdateTime(float time_last, float time_now) override;

View File

@ -3,6 +3,7 @@
//
#include "AnimGraphNodes.h"
#include "AnimGraphBlendTree.h"
#include "ozz/base/log.h"
#include "ozz/animation/runtime/blending_job.h"
@ -10,6 +11,38 @@
#include "ozz/base/io/archive.h"
#include "ozz/base/io/stream.h"
AnimNode* AnimNodeFactory(const std::string& name) {
AnimNode* result;
if (name == "Blend2") {
result = new Blend2Node;
} else if (name == "SpeedScale") {
result = new SpeedScaleNode;
} else if (name == "AnimSampler") {
result = new AnimSamplerNode;
} else if (name == "LockTranslationNode") {
result = new LockTranslationNode;
} else if (name == "BlendTree") {
result = new AnimGraphBlendTree;
} else if (name == "BlendTreeSockets") {
result = new BlendTreeNode;
} else if (name == "MathAddNode") {
result = new MathAddNode;
} else if (name == "MathFloatToVec3Node") {
result = new MathFloatToVec3Node;
} else if (name == "ConstScalarNode") {
result = new ConstScalarNode;
}
if (result != nullptr) {
result->m_node_type_name = name;
return result;
}
std::cerr << "Invalid node type: " << name << std::endl;
return nullptr;
}
void Blend2Node::Evaluate(AnimGraphContext& context) {
assert (i_input0 != nullptr);
assert (i_input1 != nullptr);

View File

@ -250,36 +250,7 @@ struct NodeDescriptor<MathFloatToVec3Node> : public NodeDescriptorBase {
};
static inline AnimNode* AnimNodeFactory(const std::string& name) {
AnimNode* result;
if (name == "Blend2") {
result = new Blend2Node;
} else if (name == "SpeedScale") {
result = new SpeedScaleNode;
} else if (name == "AnimSampler") {
result = new AnimSamplerNode;
} else if (name == "LockTranslationNode") {
result = new LockTranslationNode;
} else if (name == "BlendTree") {
result = new BlendTreeNode;
} else if (name == "BlendTreeSockets") {
result = new BlendTreeNode;
} else if (name == "MathAddNode") {
result = new MathAddNode;
} else if (name == "MathFloatToVec3Node") {
result = new MathFloatToVec3Node;
} else if (name == "ConstScalarNode") {
result = new ConstScalarNode;
}
if (result != nullptr) {
result->m_node_type_name = name;
return result;
}
std::cerr << "Invalid node type: " << name << std::endl;
return nullptr;
}
AnimNode* AnimNodeFactory(const std::string& name);
static inline NodeDescriptorBase* AnimNodeDescriptorFactory(
const std::string& node_type_name,

View File

@ -483,6 +483,18 @@ void AnimGraphResource::CreateBlendTreeRuntimeNodeInstances(
AnimGraphBlendTree& result) const {
for (auto node_resource : m_blend_tree_resource.m_nodes) {
AnimNode* node = AnimNodeFactory(node_resource->m_node_type_name);
if (node_resource->m_node_type_name == "BlendTree") {
AnimGraphResource* embedded_blend_tree_resource = dynamic_cast<AnimGraphResource*>(node_resource);
assert(embedded_blend_tree_resource != nullptr);
AnimGraphBlendTree* embedded_blend_tree = dynamic_cast<AnimGraphBlendTree*>(node);
assert(embedded_blend_tree != nullptr);
embedded_blend_tree_resource->CreateBlendTreeInstance(*embedded_blend_tree);
embedded_blend_tree_resource->m_socket_accessor->m_inputs = embedded_blend_tree->m_node_descriptor->m_outputs;
embedded_blend_tree_resource->m_socket_accessor->m_outputs = embedded_blend_tree->m_node_descriptor->m_inputs;
}
node->m_name = node_resource->m_name;
node->m_node_type_name = node_resource->m_node_type_name;
result.m_nodes.push_back(node);
@ -497,6 +509,7 @@ void AnimGraphResource::PrepareBlendTreeIOData(
AnimGraphBlendTree& instance) const {
instance.m_node_descriptor =
AnimNodeDescriptorFactory("BlendTree", instance.m_nodes[0]);
instance.m_node_descriptor->m_outputs =
m_blend_tree_resource.m_nodes[1]->m_socket_accessor->m_outputs;
instance.m_node_descriptor->m_inputs =
@ -566,9 +579,16 @@ void AnimGraphResource::PrepareBlendTreeIOData(
m_blend_tree_resource.m_nodes.size(),
nullptr);
for (int i = 0; i < m_blend_tree_resource.m_nodes.size(); i++) {
instance_node_descriptors[i] = AnimNodeDescriptorFactory(
m_blend_tree_resource.m_nodes[i]->m_node_type_name,
instance.m_nodes[i]);
instance_node_descriptors[i] = AnimNodeDescriptorFactory(
m_blend_tree_resource.m_nodes[i]->m_node_type_name,
instance.m_nodes[i]);
if (i > 1 && m_blend_tree_resource.m_nodes[i]->m_node_type_name == "BlendTree") {
instance_node_descriptors[i]->m_inputs =
m_blend_tree_resource.m_nodes[i]->m_socket_accessor->m_inputs;
instance_node_descriptors[i]->m_outputs =
m_blend_tree_resource.m_nodes[i]->m_socket_accessor->m_outputs;
}
}
instance_node_descriptors[0]->m_inputs = instance.m_node_descriptor->m_inputs;

View File

@ -116,7 +116,6 @@ struct StateMachineResource {
struct AnimGraphResource: AnimNodeResource {
std::string m_graph_type_name;
std::string m_name;
BlendTreeResource m_blend_tree_resource;
StateMachineResource m_state_machine_resource;

View File

@ -181,6 +181,7 @@ class EmbeddedBlendTreeGraphResource {
parent_blend_tree_resource->m_nodes.back());
embedded_graph->m_name = "EmbeddedBlendTree";
embedded_graph->m_node_type_name = "BlendTree";
embedded_graph->m_graph_type_name = "BlendTree";
embedded_blend_tree_resource = &embedded_graph->m_blend_tree_resource;
// Embedded: outputs
@ -202,6 +203,7 @@ class EmbeddedBlendTreeGraphResource {
AnimNodeResourceFactory("SpeedScale"));
AnimNodeResource* embedded_speed_scale_resource =
embedded_blend_tree_resource->m_nodes.back();
embedded_speed_scale_resource->m_socket_accessor->SetInputValue("SpeedScale", 0.1f);
// Embedded: setup connections
embedded_blend_tree_resource->ConnectSockets(
@ -1021,4 +1023,38 @@ TEST_CASE_METHOD(
embedded_connection.target_socket_name
== embedded_connection_loaded.target_socket_name);
}
}
TEST_CASE_METHOD(
EmbeddedBlendTreeGraphResource,
"EmbeddedBlendTreeGraphResource instantiation",
"[EmbeddedBlendTreeGraphResource]") {
AnimGraphBlendTree blend_tree;
parent_graph_resource.CreateBlendTreeInstance(blend_tree);
AnimGraphContext graph_context;
ozz::animation::Skeleton skeleton;
REQUIRE(load_skeleton(skeleton, "media/skeleton.ozz"));
graph_context.m_skeleton = &skeleton;
blend_tree.Init(graph_context);
// Marking of active inputs is not properly working as we do not properly
// populate AnimNode::m_inputs.
//
// Here comes an iffy problem: How to mark nodes in a parent blend tree
// as active?
//
// - Simplest would be if the AnimNodes don't distinguish between nodes within
// the tree they are contained or in a possible parent (or nested) tree.
// - But then how to propagate active connections across multiple layers?
// - Therefore probably better to have a blend tree store which sockets are
// active and use that within the graph.
// - But then: AnimGraphConnection already stores raw AnimNode pointers of the
// connected nodes. Still... populating them accross layers could be messy.
blend_tree.MarkActiveInputs();
blend_tree.UpdateTime(0.f, 0.1f);
blend_tree.Evaluate(graph_context);
graph_context.freeAnimations();
}