godot_synced_blend_tree/synced_animation_graph.h

173 lines
4.5 KiB
C
Raw Normal View History

2025-11-09 17:22:44 +01:00
#pragma once
2025-11-23 18:14:02 +01:00
#include "scene/animation/animation_player.h"
2025-11-09 17:22:44 +01:00
#include "scene/animation/animation_tree.h"
2025-11-22 16:45:05 +01:00
#include <cassert>
2025-11-23 18:14:02 +01:00
class Skeleton3D;
class SyncedAnimationGraph : public Node {
GDCLASS(SyncedAnimationGraph, Node);
2025-11-09 17:22:44 +01:00
private:
NodePath animation_tree_path;
NodePath skeleton_path;
2025-11-09 17:22:44 +01:00
void set_animation_tree(const NodePath &p_path);
NodePath get_animation_tree() const;
2025-11-09 17:22:44 +01:00
void set_skeleton(const NodePath &p_path);
NodePath get_skeleton() const;
// AnimationMixer::TrackCache
2025-11-22 16:45:05 +01:00
2025-11-09 17:22:44 +01:00
protected:
void _notification(int p_what);
2025-11-09 17:22:44 +01:00
static void _bind_methods();
/* ---- General settings for animation ---- */
AnimationMixer::AnimationCallbackModeProcess callback_mode_process = AnimationMixer::ANIMATION_CALLBACK_MODE_PROCESS_IDLE;
AnimationMixer::AnimationCallbackModeMethod callback_mode_method = AnimationMixer::ANIMATION_CALLBACK_MODE_METHOD_DEFERRED;
AnimationMixer::AnimationCallbackModeDiscrete callback_mode_discrete = AnimationMixer::ANIMATION_CALLBACK_MODE_DISCRETE_RECESSIVE;
bool processing = false;
bool active = true;
2025-11-09 17:22:44 +01:00
public:
void _ready(const NodePath &p_path);
void _process_graph(double p_delta, bool p_update_only = false);
void set_active(bool p_active);
bool is_active() const;
void set_callback_mode_process(AnimationMixer::AnimationCallbackModeProcess p_mode);
AnimationMixer::AnimationCallbackModeProcess get_callback_mode_process() const;
void set_callback_mode_method(AnimationMixer::AnimationCallbackModeMethod p_mode);
AnimationMixer::AnimationCallbackModeMethod get_callback_mode_method() const;
void set_callback_mode_discrete(AnimationMixer::AnimationCallbackModeDiscrete p_mode);
AnimationMixer::AnimationCallbackModeDiscrete get_callback_mode_discrete() const;
2025-11-09 17:22:44 +01:00
SyncedAnimationGraph();
private:
void _set_process(bool p_process, bool p_force = false);
2025-11-22 16:45:05 +01:00
};
struct AnimationData {
struct TrackValue {
Animation::Track *track = nullptr;
};
struct PositionTrackValue : public TrackValue {
int bone_idx = -1;
Vector3 position = Vector3(0, 0, 0);
};
struct RotationTrackValue : public TrackValue {
int bone_idx = -1;
Quaternion rotation = Quaternion(0, 0, 0, 1);
};
struct ScaleTrackValue : public TrackValue {
int bone_idx = -1;
Vector3 scale;
};
2025-11-23 18:14:02 +01:00
AnimationData() = default;
~AnimationData() {
_clear_values();
2025-11-28 00:07:45 +01:00
}
2025-11-23 18:14:02 +01:00
void set_value(Animation::TypeHash thash, TrackValue *value) {
if (!track_values.has(thash)) {
track_values.insert(thash, value);
} else {
track_values[thash] = value;
}
}
void clear() {
_clear_values();
}
AHashMap<Animation::TypeHash, TrackValue *, HashHasher> track_values; // Animation::Track to TrackValue
protected:
void _clear_values() {
for (KeyValue<Animation::TypeHash, TrackValue *> &K : track_values) {
memdelete(K.value);
}
}
2025-11-22 16:45:05 +01:00
};
struct GraphEvaluationContext {
AnimationTree *animation_tree = nullptr;
2025-11-23 18:14:02 +01:00
AnimationPlayer *animation_player = nullptr;
Skeleton3D *skeleton_3d = nullptr;
2025-11-22 16:45:05 +01:00
};
2025-11-28 00:07:45 +01:00
struct SyncTrack {
};
2025-11-22 16:45:05 +01:00
class SyncedAnimationNode {
2025-11-28 00:07:45 +01:00
friend class SyncedAnimationGraph;
2025-11-22 16:45:05 +01:00
public:
struct NodeTimeInfo {
double length = 0.0;
double position = 0.0;
double delta = 0.0;
Animation::LoopMode loop_mode = Animation::LOOP_NONE;
2025-11-28 00:07:45 +01:00
SyncTrack sync_track;
2025-11-22 16:45:05 +01:00
};
NodeTimeInfo node_time_info;
virtual ~SyncedAnimationNode() = default;
virtual void initialize(GraphEvaluationContext &context) {}
2025-11-28 00:07:45 +01:00
virtual void activate_inputs(GraphEvaluationContext &context, Vector<StringName> input_names) {}
2025-11-22 16:45:05 +01:00
virtual void calculate_sync_track() {}
virtual void update_time(double 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;
}
break;
}
case Animation::LOOP_PINGPONG: {
assert(false && !"Not yet implemented.");
break;
}
}
}
}
2025-11-28 00:07:45 +01:00
virtual void evaluate(GraphEvaluationContext &context) {}
bool is_active() const { return active; }
bool set_input_node(const StringName &socket_name, SyncedAnimationNode *node);
void get_input_names(Array<StringName> &inputs);
private:
AnimationData *output = nullptr;
bool active = false;
2025-11-22 16:45:05 +01:00
};
class AnimationSamplerNode : public SyncedAnimationNode {
StringName animation_name;
Ref<Animation> animation;
void initialize(GraphEvaluationContext &context) override;
2025-11-23 18:14:02 +01:00
void evaluate(GraphEvaluationContext &context, AnimationData &output) override;
2025-11-09 17:22:44 +01:00
};