diff --git a/sync_track.h b/sync_track.h index 63852ee..52be59a 100644 --- a/sync_track.h +++ b/sync_track.h @@ -57,11 +57,11 @@ struct SyncTrack { return -1.f; } - float calc_ratio_from_sync_time(float sync_time) const { - float interval_ratio = fmodf(sync_time, 1.0f); + double calc_ratio_from_sync_time(double sync_time) const { + float interval_ratio = fmod(sync_time, 1.0f); int interval = int(sync_time - interval_ratio); - return fmodf( + return fmod( interval_start_ratio[interval] + interval_duration_ratio[interval] * interval_ratio, 1.0f); } diff --git a/synced_animation_node.cpp b/synced_animation_node.cpp index b16deeb..32a5563 100644 --- a/synced_animation_node.cpp +++ b/synced_animation_node.cpp @@ -202,6 +202,20 @@ bool AnimationSamplerNode::initialize(GraphEvaluationContext &context) { return false; } + if (animation_name == "animation_library/TestAnimationA") { + // Corresponds to the walking animation + node_time_info.sync_track = SyncTrack::create_from_markers(animation->get_length(), { 0.8117, 0.314 }); + print_line(vformat("Using hardcoded sync track for animation %s.", animation_name)); + } else if (animation_name == "animation_library/TestAnimationB") { + // Corresponds to the running animation + node_time_info.sync_track = SyncTrack::create_from_markers(animation->get_length(), { 0.6256, 0.2721 }); + print_line(vformat("Using hardcoded sync track for animation %s.", animation_name)); + } else if (animation_name == "animation_library/TestAnimationC") { + // Corresponds to the limping animation + node_time_info.sync_track = SyncTrack::create_from_markers(animation->get_length(), { 0.0674, 1.1047 }); + print_line(vformat("Using hardcoded sync track for animation %s.", animation_name)); + } + node_time_info.length = animation->get_length(); node_time_info.loop_mode = Animation::LOOP_LINEAR; @@ -211,8 +225,14 @@ bool AnimationSamplerNode::initialize(GraphEvaluationContext &context) { void AnimationSamplerNode::evaluate(GraphEvaluationContext &context, const LocalVector &inputs, AnimationData &output) { assert(inputs.size() == 0); + double sample_time = node_time_info.position; + + if (node_time_info.is_synced) { + sample_time = node_time_info.sync_track.calc_ratio_from_sync_time(node_time_info.sync_position) * animation->get_length(); + } + output.clear(); - output.sample_from_animation(animation, context.skeleton_3d, node_time_info.position); + output.sample_from_animation(animation, context.skeleton_3d, sample_time); } void AnimationSamplerNode::set_animation(const StringName &p_name) { diff --git a/synced_animation_node.h b/synced_animation_node.h index ee0fb29..5776ce6 100644 --- a/synced_animation_node.h +++ b/synced_animation_node.h @@ -230,7 +230,7 @@ public: double sync_delta = 0.0; bool is_synced = false; - Animation::LoopMode loop_mode = Animation::LOOP_NONE; + Animation::LoopMode loop_mode = Animation::LOOP_LINEAR; SyncTrack sync_track; }; NodeTimeInfo node_time_info; @@ -246,6 +246,7 @@ public: // By default, all inputs nodes are activated. for (const Ref &node : input_nodes) { node->active = true; + node->node_time_info.is_synced = node_time_info.is_synced; } } virtual void calculate_sync_track(Vector> input_nodes) { @@ -254,25 +255,29 @@ public: node_time_info.sync_track = input_nodes[0]->node_time_info.sync_track; } } - virtual void update_time(double p_delta) { - node_time_info.delta = p_delta; - node_time_info.position += p_delta; - if (node_time_info.position > node_time_info.length) { - switch (node_time_info.loop_mode) { - case Animation::LOOP_NONE: { - node_time_info.position = node_time_info.length; - break; - } - case Animation::LOOP_LINEAR: { - assert(node_time_info.length > 0.0); - while (node_time_info.position > node_time_info.length) { - node_time_info.position -= node_time_info.length; + virtual void update_time(double p_time) { + if (node_time_info.is_synced) { + node_time_info.sync_position = p_time; + } else { + node_time_info.delta = p_time; + node_time_info.position += p_time; + if (node_time_info.position > node_time_info.length) { + switch (node_time_info.loop_mode) { + case Animation::LOOP_NONE: { + node_time_info.position = node_time_info.length; + break; + } + case Animation::LOOP_LINEAR: { + assert(node_time_info.length > 0.0); + while (node_time_info.position > node_time_info.length) { + node_time_info.position -= node_time_info.length; + } + break; + } + case Animation::LOOP_PINGPONG: { + assert(false && !"Not yet implemented."); + break; } - break; - } - case Animation::LOOP_PINGPONG: { - assert(false && !"Not yet implemented."); - break; } } } @@ -336,13 +341,33 @@ class AnimationBlend2Node : public SyncedAnimationNode { public: StringName blend_amount = PNAME("blend_amount"); float blend_weight = 0.0f; - bool sync = false; + bool sync = true; void get_input_names(Vector &inputs) const override { inputs.push_back("Input0"); inputs.push_back("Input1"); } + void activate_inputs(Vector> input_nodes) override { + for (const Ref &node : input_nodes) { + node->active = true; + // If this Blend2 node is already synced then inputs are also synced. Otherwise, inputs are only set to synced if synced blending is active in this node. + node->node_time_info.is_synced = node_time_info.is_synced || sync; + } + } + void calculate_sync_track(Vector> input_nodes) override { + if (node_time_info.is_synced || sync) { + node_time_info.sync_track = SyncTrack::blend(blend_weight, input_nodes[0]->node_time_info.sync_track, input_nodes[1]->node_time_info.sync_track); + node_time_info.length = node_time_info.sync_track.duration; + } + } + void update_time(double p_delta) override { + SyncedAnimationNode::update_time(p_delta); + + if (sync && !node_time_info.is_synced) { + node_time_info.sync_position = node_time_info.sync_track.calc_sync_from_abs_time(node_time_info.position); + } + } void evaluate(GraphEvaluationContext &context, const LocalVector &inputs, AnimationData &output) override; void set_use_sync(bool p_sync); @@ -717,7 +742,7 @@ public: const Ref &node_parent = tree_graph.nodes[tree_graph.node_connection_info[i].parent_node_index]; if (node->node_time_info.is_synced) { - node->update_time(node_parent->node_time_info.position); + node->update_time(node_parent->node_time_info.sync_position); } else { node->update_time(node_parent->node_time_info.delta); }