Using AnimationSamplerNode for evaluation.
Known issues: - It seems that only rotations are applied. - Memory is likely not properly freed.
This commit is contained in:
parent
f5893df6b0
commit
757c5ee51c
@ -31,7 +31,12 @@ void SyncedAnimationGraph::_bind_methods() {
|
|||||||
void SyncedAnimationGraph::_notification(int p_what) {
|
void SyncedAnimationGraph::_notification(int p_what) {
|
||||||
switch (p_what) {
|
switch (p_what) {
|
||||||
case Node::NOTIFICATION_READY: {
|
case Node::NOTIFICATION_READY: {
|
||||||
_set_process(true);
|
_setup_evaluation_context();
|
||||||
|
_setup_graph();
|
||||||
|
|
||||||
|
if (active) {
|
||||||
|
_set_process(true);
|
||||||
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Node::NOTIFICATION_INTERNAL_PROCESS: {
|
case Node::NOTIFICATION_INTERNAL_PROCESS: {
|
||||||
@ -108,7 +113,7 @@ void SyncedAnimationGraph::set_animation_player(const NodePath &p_path) {
|
|||||||
// remove_animation_library(animation_libraries[0].name);
|
// remove_animation_library(animation_libraries[0].name);
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
_graph_context.animation_player = Object::cast_to<AnimationPlayer>(get_node_or_null(animation_player_path));
|
graph_context.animation_player = Object::cast_to<AnimationPlayer>(get_node_or_null(animation_player_path));
|
||||||
|
|
||||||
emit_signal(SNAME("animation_player_changed")); // Needs to unpin AnimationPlayerEditor.
|
emit_signal(SNAME("animation_player_changed")); // Needs to unpin AnimationPlayerEditor.
|
||||||
}
|
}
|
||||||
@ -125,6 +130,8 @@ void SyncedAnimationGraph::set_skeleton(const NodePath &p_path) {
|
|||||||
// remove_animation_library(animation_libraries[0].name);
|
// remove_animation_library(animation_libraries[0].name);
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
graph_context.skeleton_3d = Object::cast_to<Skeleton3D>(get_node_or_null(skeleton_path));
|
||||||
|
|
||||||
emit_signal(SNAME("skeleton_changed")); // Needs to unpin AnimationPlayerEditor.
|
emit_signal(SNAME("skeleton_changed")); // Needs to unpin AnimationPlayerEditor.
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,84 +139,60 @@ NodePath SyncedAnimationGraph::get_skeleton() const {
|
|||||||
return skeleton_path;
|
return skeleton_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SyncedAnimationGraph::_ready(const NodePath &p_path) {
|
void SyncedAnimationGraph::_process_graph(double p_delta, bool p_update_only) {
|
||||||
print_line(vformat("synced animation graph ready!"));
|
if (root_node == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
root_node->activate_inputs();
|
||||||
|
root_node->calculate_sync_track();
|
||||||
|
root_node->update_time(p_delta);
|
||||||
|
AnimationData output_data;
|
||||||
|
root_node->evaluate(graph_context, output_data);
|
||||||
|
|
||||||
|
_apply_animation_data(output_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SyncedAnimationGraph::_process_graph(double p_delta, bool p_update_only) {
|
void SyncedAnimationGraph::_apply_animation_data(AnimationData output_data) const {
|
||||||
if (skeleton_path.is_empty()) {
|
for (KeyValue<Animation::TypeHash, AnimationData::TrackValue *> &K : output_data.track_values) {
|
||||||
return;
|
const AnimationData::TrackValue *track_value = K.value;
|
||||||
}
|
switch (track_value->type) {
|
||||||
|
case AnimationData::TrackType::TYPE_POSITION_3D: {
|
||||||
|
const AnimationData::PositionTrackValue *position_value = static_cast<const AnimationData::PositionTrackValue *>(track_value);
|
||||||
|
|
||||||
float current_time = Time::get_singleton()->get_unix_time_from_system();
|
NodePath path = position_value->track->path;
|
||||||
Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(get_node_or_null(skeleton_path));
|
|
||||||
if (!skeleton) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ref<Animation> animation = _graph_context.animation_player->get_animation("animation_library/Walk-InPlace");
|
|
||||||
if (!animation.is_valid()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
static double debug_time = 0.;
|
|
||||||
debug_time += p_delta;
|
|
||||||
if (debug_time > 2.0) {
|
|
||||||
debug_time = 0.;
|
|
||||||
}
|
|
||||||
|
|
||||||
current_time = debug_time;
|
|
||||||
|
|
||||||
// LocalVector<AnimationMixer::TrackCache *> &track_num_to_track_cache = animation_track_num_to_track_cache[a];
|
|
||||||
const Vector<Animation::Track *> tracks = animation->get_tracks();
|
|
||||||
Animation::Track *const *tracks_ptr = tracks.ptr();
|
|
||||||
// real_t a_length = animation->get_length();
|
|
||||||
int count = tracks.size();
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
const Animation::Track *animation_track = tracks_ptr[i];
|
|
||||||
if (!animation_track->enabled) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Animation::TrackType ttype = animation_track->type;
|
|
||||||
switch (ttype) {
|
|
||||||
case Animation::TYPE_POSITION_3D: {
|
|
||||||
NodePath path = animation->track_get_path(i);
|
|
||||||
|
|
||||||
double animation_time = Math::fposmod(current_time, animation->get_length());
|
|
||||||
if (path.get_subname_count() == 1) {
|
if (path.get_subname_count() == 1) {
|
||||||
|
int bone_idx = graph_context.skeleton_3d->find_bone(path.get_subname(0));
|
||||||
int bone_idx = skeleton->find_bone(path.get_subname(0));
|
|
||||||
if (bone_idx != -1) {
|
if (bone_idx != -1) {
|
||||||
Vector3 pos;
|
graph_context.skeleton_3d->set_bone_pose_position(position_value->bone_idx, position_value->position);
|
||||||
animation->try_position_track_interpolate(i, animation_time, &pos);
|
|
||||||
skeleton->set_bone_pose_position(bone_idx, pos);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Animation::TYPE_ROTATION_3D: {
|
case AnimationData::TrackType::TYPE_ROTATION_3D: {
|
||||||
NodePath path = animation->track_get_path(i);
|
const AnimationData::RotationTrackValue *rotation_value = static_cast<const AnimationData::RotationTrackValue *>(track_value);
|
||||||
|
|
||||||
|
NodePath path = rotation_value->track->path;
|
||||||
|
|
||||||
double animation_time = Math::fposmod(current_time, animation->get_length());
|
|
||||||
if (path.get_subname_count() == 1) {
|
if (path.get_subname_count() == 1) {
|
||||||
int bone_idx = skeleton->find_bone(path.get_subname(0));
|
int bone_idx = graph_context.skeleton_3d->find_bone(path.get_subname(0));
|
||||||
if (bone_idx != -1) {
|
if (bone_idx != -1) {
|
||||||
Quaternion rot;
|
graph_context.skeleton_3d->set_bone_pose_rotation(rotation_value->bone_idx, rotation_value->rotation);
|
||||||
animation->try_rotation_track_interpolate(i, animation_time, &rot);
|
|
||||||
skeleton->set_bone_pose_rotation(bone_idx, rot);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
|
print_line(vformat("Unsupported track type %d", track_value->type));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// skeleton->set_bone_pose_position(3, Vector3(sin(current_time) * 10., 0., 0.));
|
graph_context.skeleton_3d->force_update_all_bone_transforms();
|
||||||
skeleton->force_update_all_bone_transforms();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SyncedAnimationGraph::_set_process(bool p_process, bool p_force) {
|
void SyncedAnimationGraph::_set_process(bool p_process, bool p_force) {
|
||||||
@ -223,70 +206,38 @@ void SyncedAnimationGraph::_set_process(bool p_process, bool p_force) {
|
|||||||
processing = p_process;
|
processing = p_process;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SyncedAnimationGraph::_setup_evaluation_context() {
|
||||||
|
_cleanup_evaluation_context();
|
||||||
|
|
||||||
|
graph_context.animation_player = Object::cast_to<AnimationPlayer>(get_node_or_null(animation_player_path));
|
||||||
|
graph_context.skeleton_3d = Object::cast_to<Skeleton3D>(get_node_or_null(skeleton_path));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SyncedAnimationGraph::_cleanup_evaluation_context() {
|
||||||
|
graph_context.animation_player = nullptr;
|
||||||
|
graph_context.skeleton_3d = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SyncedAnimationGraph::_setup_graph() {
|
||||||
|
if (root_node != nullptr) {
|
||||||
|
_cleanup_graph();
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimationSamplerNode *sampler_node = memnew(AnimationSamplerNode);
|
||||||
|
sampler_node->animation_name = "animation_library/Walk-InPlace";
|
||||||
|
|
||||||
|
root_node = sampler_node;
|
||||||
|
|
||||||
|
root_node->initialize(graph_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SyncedAnimationGraph::_cleanup_graph() {
|
||||||
|
if (root_node == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memfree(root_node);
|
||||||
|
}
|
||||||
|
|
||||||
SyncedAnimationGraph::SyncedAnimationGraph() {
|
SyncedAnimationGraph::SyncedAnimationGraph() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimationSamplerNode::initialize(GraphEvaluationContext &context) {
|
|
||||||
animation = context.animation_tree->get_animation(animation_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void AnimationSamplerNode::evaluate(GraphEvaluationContext &context, AnimationData &output) {
|
|
||||||
const Vector<Animation::Track *> tracks = animation->get_tracks();
|
|
||||||
Animation::Track *const *tracks_ptr = tracks.ptr();
|
|
||||||
// real_t a_length = animation->get_length();
|
|
||||||
int count = tracks.size();
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
AnimationData::TrackValue *track_value = nullptr;
|
|
||||||
const Animation::Track *animation_track = tracks_ptr[i];
|
|
||||||
const NodePath& track_node_path = animation_track->path;
|
|
||||||
if (!animation_track->enabled) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Animation::TrackType ttype = animation_track->type;
|
|
||||||
switch (ttype) {
|
|
||||||
case Animation::TYPE_POSITION_3D: {
|
|
||||||
AnimationData::PositionTrackValue *position_track_value = memnew(AnimationData::PositionTrackValue);
|
|
||||||
|
|
||||||
if (track_node_path.get_subname_count() == 1) {
|
|
||||||
int bone_idx = context.skeleton_3d->find_bone(track_node_path.get_subname(0));
|
|
||||||
if (bone_idx != -1) {
|
|
||||||
position_track_value->bone_idx = bone_idx;
|
|
||||||
animation->try_position_track_interpolate(i, node_time_info.position, &position_track_value->position);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// TODO
|
|
||||||
assert(false && !"Not yet implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
track_value = position_track_value;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Animation::TYPE_ROTATION_3D: {
|
|
||||||
AnimationData::RotationTrackValue *rotation_track_value = memnew(AnimationData::RotationTrackValue);
|
|
||||||
|
|
||||||
if (track_node_path.get_subname_count() == 1) {
|
|
||||||
int bone_idx = context.skeleton_3d->find_bone(track_node_path.get_subname(0));
|
|
||||||
if (bone_idx != -1) {
|
|
||||||
rotation_track_value->bone_idx = bone_idx;
|
|
||||||
animation->try_rotation_track_interpolate(i, node_time_info.position, &rotation_track_value->rotation);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// TODO
|
|
||||||
assert(false && !"Not yet implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
track_value = rotation_track_value;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
// TODO
|
|
||||||
assert(false && !"Not yet implemented");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
output.set_value(animation_track->thash, track_value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,7 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "scene/animation/animation_player.h"
|
#include "scene/animation/animation_player.h"
|
||||||
#include "scene/animation/animation_tree.h"
|
#include "synced_animation_node.h"
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
@ -14,14 +14,15 @@ private:
|
|||||||
NodePath animation_player_path;
|
NodePath animation_player_path;
|
||||||
NodePath skeleton_path;
|
NodePath skeleton_path;
|
||||||
|
|
||||||
|
GraphEvaluationContext graph_context = {};
|
||||||
|
SyncedAnimationNode* root_node = nullptr;
|
||||||
|
|
||||||
void set_animation_player(const NodePath &p_path);
|
void set_animation_player(const NodePath &p_path);
|
||||||
NodePath get_animation_player() const;
|
NodePath get_animation_player() const;
|
||||||
|
|
||||||
void set_skeleton(const NodePath &p_path);
|
void set_skeleton(const NodePath &p_path);
|
||||||
NodePath get_skeleton() const;
|
NodePath get_skeleton() const;
|
||||||
|
|
||||||
// AnimationMixer::TrackCache
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void _notification(int p_what);
|
void _notification(int p_what);
|
||||||
static void _bind_methods();
|
static void _bind_methods();
|
||||||
@ -36,6 +37,7 @@ protected:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
void _process_graph(double p_delta, bool p_update_only = false);
|
void _process_graph(double p_delta, bool p_update_only = false);
|
||||||
|
void _apply_animation_data(AnimationData output_data) const;
|
||||||
|
|
||||||
void set_active(bool p_active);
|
void set_active(bool p_active);
|
||||||
bool is_active() const;
|
bool is_active() const;
|
||||||
@ -53,153 +55,10 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void _set_process(bool p_process, bool p_force = false);
|
void _set_process(bool p_process, bool p_force = false);
|
||||||
|
|
||||||
|
void _setup_evaluation_context();
|
||||||
|
void _cleanup_evaluation_context();
|
||||||
|
|
||||||
|
void _setup_graph();
|
||||||
|
void _cleanup_graph();
|
||||||
};
|
};
|
||||||
|
|
||||||
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;
|
|
||||||
};
|
|
||||||
|
|
||||||
AnimationData() = default;
|
|
||||||
~AnimationData() {
|
|
||||||
_clear_values();
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct GraphEvaluationContext {
|
|
||||||
AnimationTree *animation_tree = nullptr;
|
|
||||||
AnimationPlayer *animation_player = nullptr;
|
|
||||||
Skeleton3D *skeleton_3d = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SyncTrack {
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
class SyncedAnimationNode {
|
|
||||||
friend class SyncedAnimationGraph;
|
|
||||||
|
|
||||||
public:
|
|
||||||
struct NodeTimeInfo {
|
|
||||||
double length = 0.0;
|
|
||||||
double position = 0.0;
|
|
||||||
double sync_position = 0.0;
|
|
||||||
double delta = 0.0;
|
|
||||||
double sync_delta = 0.0;
|
|
||||||
|
|
||||||
Animation::LoopMode loop_mode = Animation::LOOP_NONE;
|
|
||||||
SyncTrack sync_track;
|
|
||||||
};
|
|
||||||
NodeTimeInfo node_time_info;
|
|
||||||
|
|
||||||
struct InputSocket {
|
|
||||||
StringName name;
|
|
||||||
SyncedAnimationNode *node;
|
|
||||||
};
|
|
||||||
|
|
||||||
Vector<InputSocket> input_sockets;
|
|
||||||
|
|
||||||
virtual ~SyncedAnimationNode() = default;
|
|
||||||
virtual void initialize(GraphEvaluationContext &context) {}
|
|
||||||
virtual void activate_inputs(GraphEvaluationContext &context, Vector<StringName> input_names) {}
|
|
||||||
virtual void calculate_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;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Animation::LOOP_PINGPONG: {
|
|
||||||
assert(false && !"Not yet implemented.");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
virtual void evaluate(GraphEvaluationContext &context, AnimationData &output) {}
|
|
||||||
|
|
||||||
bool is_active() const { return active; }
|
|
||||||
bool set_input_node(const StringName &socket_name, SyncedAnimationNode *node);
|
|
||||||
void get_input_names(Vector<StringName> &inputs);
|
|
||||||
|
|
||||||
private:
|
|
||||||
AnimationData *output = nullptr;
|
|
||||||
bool active = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
class AnimationSamplerNode : public SyncedAnimationNode {
|
|
||||||
StringName animation_name;
|
|
||||||
Ref<Animation> animation;
|
|
||||||
|
|
||||||
void initialize(GraphEvaluationContext &context) override;
|
|
||||||
void evaluate(GraphEvaluationContext &context, AnimationData &output) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class BlendTree : public SyncedAnimationNode {
|
|
||||||
struct Connection {
|
|
||||||
const SyncedAnimationNode* source_node = nullptr;
|
|
||||||
const SyncedAnimationNode* target_node = nullptr;
|
|
||||||
const StringName target_socket_name = "";
|
|
||||||
};
|
|
||||||
|
|
||||||
Vector<SyncedAnimationNode> nodes;
|
|
||||||
Vector<int> node_parent;
|
|
||||||
Vector<Connection> connections;
|
|
||||||
|
|
||||||
public:
|
|
||||||
void connect_nodes(const SyncedAnimationNode* source_node, const SyncedAnimationNode* target_node, StringName target_socket_name) {
|
|
||||||
// TODO
|
|
||||||
// connections.append(Connection{source_node, target_node, target_socket_name});
|
|
||||||
// sort_nodes_by_evaluation_order();
|
|
||||||
}
|
|
||||||
|
|
||||||
void sort_nodes_by_evaluation_order() {
|
|
||||||
// TODO: sort nodes and node_parent s.t. for node i all children have index > i.
|
|
||||||
}
|
|
||||||
};
|
|
||||||
73
synced_animation_node.cpp
Normal file
73
synced_animation_node.cpp
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
//
|
||||||
|
// Created by martin on 03.12.25.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "synced_animation_node.h"
|
||||||
|
|
||||||
|
void AnimationSamplerNode::initialize(GraphEvaluationContext &context) {
|
||||||
|
animation = context.animation_player->get_animation(animation_name);
|
||||||
|
node_time_info.length = animation->get_length();
|
||||||
|
node_time_info.loop_mode = Animation::LOOP_LINEAR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AnimationSamplerNode::evaluate(GraphEvaluationContext &context, AnimationData &output) {
|
||||||
|
const Vector<Animation::Track *> tracks = animation->get_tracks();
|
||||||
|
Animation::Track *const *tracks_ptr = tracks.ptr();
|
||||||
|
// real_t a_length = animation->get_length();
|
||||||
|
int count = tracks.size();
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
AnimationData::TrackValue *track_value = nullptr;
|
||||||
|
const Animation::Track *animation_track = tracks_ptr[i];
|
||||||
|
const NodePath& track_node_path = animation_track->path;
|
||||||
|
if (!animation_track->enabled) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Animation::TrackType ttype = animation_track->type;
|
||||||
|
switch (ttype) {
|
||||||
|
case Animation::TYPE_POSITION_3D: {
|
||||||
|
AnimationData::PositionTrackValue *position_track_value = memnew(AnimationData::PositionTrackValue);
|
||||||
|
|
||||||
|
if (track_node_path.get_subname_count() == 1) {
|
||||||
|
int bone_idx = context.skeleton_3d->find_bone(track_node_path.get_subname(0));
|
||||||
|
if (bone_idx != -1) {
|
||||||
|
position_track_value->bone_idx = bone_idx;
|
||||||
|
}
|
||||||
|
animation->try_position_track_interpolate(i, node_time_info.position, &position_track_value->position);
|
||||||
|
} else {
|
||||||
|
// TODO
|
||||||
|
assert(false && !"Not yet implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
track_value = position_track_value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Animation::TYPE_ROTATION_3D: {
|
||||||
|
AnimationData::RotationTrackValue *rotation_track_value = memnew(AnimationData::RotationTrackValue);
|
||||||
|
|
||||||
|
if (track_node_path.get_subname_count() == 1) {
|
||||||
|
int bone_idx = context.skeleton_3d->find_bone(track_node_path.get_subname(0));
|
||||||
|
if (bone_idx != -1) {
|
||||||
|
rotation_track_value->bone_idx = bone_idx;
|
||||||
|
}
|
||||||
|
animation->try_rotation_track_interpolate(i, node_time_info.position, &rotation_track_value->rotation);
|
||||||
|
} else {
|
||||||
|
// TODO
|
||||||
|
assert(false && !"Not yet implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
track_value = rotation_track_value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
// TODO
|
||||||
|
assert(false && !"Not yet implemented");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
track_value->track = tracks_ptr[i];
|
||||||
|
output.set_value(animation_track->thash, track_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
175
synced_animation_node.h
Normal file
175
synced_animation_node.h
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "scene/animation/animation_player.h"
|
||||||
|
|
||||||
|
#include "scene/3d/skeleton_3d.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
struct GraphEvaluationContext {
|
||||||
|
AnimationPlayer *animation_player = nullptr;
|
||||||
|
Skeleton3D *skeleton_3d = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct AnimationData {
|
||||||
|
enum TrackType : uint8_t {
|
||||||
|
TYPE_VALUE, // Set a value in a property, can be interpolated.
|
||||||
|
TYPE_POSITION_3D, // Position 3D track, can be compressed.
|
||||||
|
TYPE_ROTATION_3D, // Rotation 3D track, can be compressed.
|
||||||
|
TYPE_SCALE_3D, // Scale 3D track, can be compressed.
|
||||||
|
TYPE_BLEND_SHAPE, // Blend Shape track, can be compressed.
|
||||||
|
TYPE_METHOD, // Call any method on a specific node.
|
||||||
|
TYPE_BEZIER, // Bezier curve.
|
||||||
|
TYPE_AUDIO,
|
||||||
|
TYPE_ANIMATION,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TrackValue {
|
||||||
|
Animation::Track *track = nullptr;
|
||||||
|
TrackType type = TYPE_ANIMATION;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PositionTrackValue : public TrackValue {
|
||||||
|
int bone_idx = -1;
|
||||||
|
Vector3 position = Vector3(0, 0, 0);
|
||||||
|
PositionTrackValue() { type = TYPE_POSITION_3D; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RotationTrackValue : public TrackValue {
|
||||||
|
int bone_idx = -1;
|
||||||
|
Quaternion rotation = Quaternion(0, 0, 0, 1);
|
||||||
|
RotationTrackValue() { type = TYPE_ROTATION_3D; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ScaleTrackValue : public TrackValue {
|
||||||
|
int bone_idx = -1;
|
||||||
|
Vector3 scale;
|
||||||
|
ScaleTrackValue() { type = TYPE_SCALE_3D; }
|
||||||
|
};
|
||||||
|
|
||||||
|
AnimationData() = default;
|
||||||
|
~AnimationData() {
|
||||||
|
_clear_values();
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SyncTrack {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class SyncedAnimationNode {
|
||||||
|
friend class SyncedAnimationGraph;
|
||||||
|
|
||||||
|
public:
|
||||||
|
struct NodeTimeInfo {
|
||||||
|
double length = 0.0;
|
||||||
|
double position = 0.0;
|
||||||
|
double sync_position = 0.0;
|
||||||
|
double delta = 0.0;
|
||||||
|
double sync_delta = 0.0;
|
||||||
|
|
||||||
|
Animation::LoopMode loop_mode = Animation::LOOP_NONE;
|
||||||
|
SyncTrack sync_track;
|
||||||
|
};
|
||||||
|
NodeTimeInfo node_time_info;
|
||||||
|
|
||||||
|
struct InputSocket {
|
||||||
|
StringName name;
|
||||||
|
SyncedAnimationNode *node;
|
||||||
|
};
|
||||||
|
|
||||||
|
Vector<InputSocket> input_sockets;
|
||||||
|
|
||||||
|
virtual ~SyncedAnimationNode() = default;
|
||||||
|
virtual void initialize(GraphEvaluationContext &context) {}
|
||||||
|
virtual void activate_inputs() {}
|
||||||
|
virtual void calculate_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;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Animation::LOOP_PINGPONG: {
|
||||||
|
assert(false && !"Not yet implemented.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
virtual void evaluate(GraphEvaluationContext &context, AnimationData &output) {}
|
||||||
|
|
||||||
|
bool is_active() const { return active; }
|
||||||
|
bool set_input_node(const StringName &socket_name, SyncedAnimationNode *node);
|
||||||
|
void get_input_names(Vector<StringName> &inputs);
|
||||||
|
|
||||||
|
private:
|
||||||
|
AnimationData *output = nullptr;
|
||||||
|
bool active = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AnimationSamplerNode : public SyncedAnimationNode {
|
||||||
|
public:
|
||||||
|
StringName animation_name;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ref<Animation> animation;
|
||||||
|
|
||||||
|
void initialize(GraphEvaluationContext &context) override;
|
||||||
|
void evaluate(GraphEvaluationContext &context, AnimationData &output) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BlendTree : public SyncedAnimationNode {
|
||||||
|
struct Connection {
|
||||||
|
const SyncedAnimationNode* source_node = nullptr;
|
||||||
|
const SyncedAnimationNode* target_node = nullptr;
|
||||||
|
const StringName target_socket_name = "";
|
||||||
|
};
|
||||||
|
|
||||||
|
Vector<SyncedAnimationNode> nodes;
|
||||||
|
Vector<int> node_parent;
|
||||||
|
Vector<Connection> connections;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void connect_nodes(const SyncedAnimationNode* source_node, const SyncedAnimationNode* target_node, StringName target_socket_name) {
|
||||||
|
// TODO
|
||||||
|
// connections.append(Connection{source_node, target_node, target_socket_name});
|
||||||
|
// sort_nodes_by_evaluation_order();
|
||||||
|
}
|
||||||
|
|
||||||
|
void sort_nodes_by_evaluation_order() {
|
||||||
|
// TODO: sort nodes and node_parent s.t. for node i all children have index > i.
|
||||||
|
}
|
||||||
|
};
|
||||||
Loading…
x
Reference in New Issue
Block a user