WIP: Blend2 node and blending of AnimationData.
This commit is contained in:
parent
f4eea6d2d4
commit
56fde580c3
@ -4,15 +4,7 @@
|
||||
|
||||
#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, const Vector<AnimationData*>& inputs, AnimationData &output) {
|
||||
assert(inputs.size() == 0);
|
||||
|
||||
void AnimationData::sample_from_animation(const Ref<Animation> &animation, const Skeleton3D *skeleton_3d, double p_time) {
|
||||
const Vector<Animation::Track *> tracks = animation->get_tracks();
|
||||
Animation::Track *const *tracks_ptr = tracks.ptr();
|
||||
|
||||
@ -20,7 +12,7 @@ void AnimationSamplerNode::evaluate(GraphEvaluationContext &context, const Vecto
|
||||
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;
|
||||
const NodePath &track_node_path = animation_track->path;
|
||||
if (!animation_track->enabled) {
|
||||
continue;
|
||||
}
|
||||
@ -31,11 +23,11 @@ void AnimationSamplerNode::evaluate(GraphEvaluationContext &context, const Vecto
|
||||
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));
|
||||
int bone_idx = 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);
|
||||
animation->try_position_track_interpolate(i, p_time, &position_track_value->position);
|
||||
} else {
|
||||
// TODO
|
||||
assert(false && !"Not yet implemented");
|
||||
@ -48,11 +40,11 @@ void AnimationSamplerNode::evaluate(GraphEvaluationContext &context, const Vecto
|
||||
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));
|
||||
int bone_idx = 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);
|
||||
animation->try_rotation_track_interpolate(i, p_time, &rotation_track_value->rotation);
|
||||
} else {
|
||||
// TODO
|
||||
assert(false && !"Not yet implemented");
|
||||
@ -69,6 +61,22 @@ void AnimationSamplerNode::evaluate(GraphEvaluationContext &context, const Vecto
|
||||
}
|
||||
|
||||
track_value->track = tracks_ptr[i];
|
||||
output.set_value(animation_track->thash, track_value);
|
||||
set_value(animation_track->thash, track_value);
|
||||
}
|
||||
}
|
||||
|
||||
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, const Vector<AnimationData *> &inputs, AnimationData &output) {
|
||||
assert(inputs.size() == 0);
|
||||
|
||||
output.clear();
|
||||
output.sample_from_animation(animation, context.skeleton_3d, node_time_info.position);
|
||||
}
|
||||
|
||||
void AnimationBlend2Node::evaluate(GraphEvaluationContext &context, const Vector<AnimationData *> &inputs, AnimationData &output) {
|
||||
}
|
||||
@ -29,18 +29,81 @@ struct AnimationData {
|
||||
struct TrackValue {
|
||||
Animation::Track *track = nullptr;
|
||||
TrackType type = TYPE_ANIMATION;
|
||||
|
||||
virtual ~TrackValue() = default;
|
||||
|
||||
virtual void blend(const TrackValue &to_value, const float lambda) {
|
||||
print_error(vformat("Blending of TrackValue of type %d with TrackValue of type %d not yet implemented.", type, to_value.type));
|
||||
}
|
||||
|
||||
virtual bool operator==(const TrackValue &other_value) const {
|
||||
print_error(vformat("Comparing TrackValue of type %d with TrackValue of type %d not yet implemented.", type, other_value.type));
|
||||
return false;
|
||||
}
|
||||
bool operator!=(const TrackValue &other_value) const {
|
||||
return !(*this == other_value);
|
||||
}
|
||||
|
||||
virtual TrackValue *clone() const {
|
||||
print_error(vformat("Cannot clone TrackValue of type %d: not yet implemented.", type));
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
struct PositionTrackValue : public TrackValue {
|
||||
int bone_idx = -1;
|
||||
Vector3 position = Vector3(0, 0, 0);
|
||||
PositionTrackValue() { type = TYPE_POSITION_3D; }
|
||||
|
||||
void blend(const TrackValue &to_value, const float lambda) override {
|
||||
const PositionTrackValue *to_value_casted = &static_cast<const PositionTrackValue &>(to_value);
|
||||
assert(bone_idx == to_value_casted->bone_idx);
|
||||
position = (1. - lambda) * position + lambda * to_value_casted->position;
|
||||
}
|
||||
|
||||
bool operator==(const TrackValue &other_value) const override {
|
||||
if (type != other_value.type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const PositionTrackValue *other_value_casted = &static_cast<const PositionTrackValue &>(other_value);
|
||||
return bone_idx == other_value_casted->bone_idx && position == other_value_casted->position;
|
||||
}
|
||||
|
||||
TrackValue *clone() const override {
|
||||
PositionTrackValue *result = memnew(PositionTrackValue);
|
||||
result->bone_idx = bone_idx;
|
||||
result->position = position;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
struct RotationTrackValue : public TrackValue {
|
||||
int bone_idx = -1;
|
||||
Quaternion rotation = Quaternion(0, 0, 0, 1);
|
||||
RotationTrackValue() { type = TYPE_ROTATION_3D; }
|
||||
|
||||
void blend(const TrackValue &to_value, const float lambda) override {
|
||||
const RotationTrackValue *to_value_casted = &static_cast<const RotationTrackValue &>(to_value);
|
||||
assert(bone_idx == to_value_casted->bone_idx);
|
||||
rotation = rotation.slerp(to_value_casted->rotation, lambda);
|
||||
}
|
||||
|
||||
bool operator==(const TrackValue &other_value) const override {
|
||||
if (type != other_value.type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const RotationTrackValue *other_value_casted = &static_cast<const RotationTrackValue &>(other_value);
|
||||
return bone_idx == other_value_casted->bone_idx && rotation == other_value_casted->rotation;
|
||||
}
|
||||
|
||||
TrackValue *clone() const override {
|
||||
RotationTrackValue *result = memnew(RotationTrackValue);
|
||||
result->bone_idx = bone_idx;
|
||||
result->rotation = rotation;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
struct ScaleTrackValue : public TrackValue {
|
||||
@ -53,8 +116,26 @@ struct AnimationData {
|
||||
~AnimationData() {
|
||||
_clear_values();
|
||||
}
|
||||
AnimationData(const AnimationData &other) {
|
||||
for (const KeyValue<Animation::TypeHash, TrackValue *> &K : other.track_values) {
|
||||
track_values.insert(K.key, K.value->clone());
|
||||
}
|
||||
}
|
||||
AnimationData(AnimationData &&other) noexcept :
|
||||
track_values(std::exchange(other.track_values, AHashMap<Animation::TypeHash, TrackValue *, HashHasher>())) {
|
||||
}
|
||||
AnimationData &operator=(const AnimationData &other) {
|
||||
AnimationData temp(other);
|
||||
std::swap(track_values, temp.track_values);
|
||||
return *this;
|
||||
}
|
||||
AnimationData &operator=(AnimationData &&other) noexcept {
|
||||
std::swap(track_values, other.track_values);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void set_value(Animation::TypeHash thash, TrackValue *value) {
|
||||
void
|
||||
set_value(const Animation::TypeHash& thash, TrackValue *value) {
|
||||
if (!track_values.has(thash)) {
|
||||
track_values.insert(thash, value);
|
||||
} else {
|
||||
@ -66,6 +147,39 @@ struct AnimationData {
|
||||
_clear_values();
|
||||
}
|
||||
|
||||
bool has_same_tracks(const AnimationData &other) const {
|
||||
HashSet<Animation::TypeHash> valid_track_hashes;
|
||||
for (const KeyValue<Animation::TypeHash, TrackValue *> &K : track_values) {
|
||||
valid_track_hashes.insert(K.key);
|
||||
}
|
||||
|
||||
for (const KeyValue<Animation::TypeHash, TrackValue *> &K : other.track_values) {
|
||||
if (HashSet<Animation::TypeHash>::Iterator entry = valid_track_hashes.find(K.key)) {
|
||||
valid_track_hashes.remove(entry);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return valid_track_hashes.size() == 0;
|
||||
}
|
||||
|
||||
void blend(const AnimationData &to_data, const float lambda) {
|
||||
if (!has_same_tracks(to_data)) {
|
||||
print_error("Cannot blend AnimationData: tracks do not match.");
|
||||
return;
|
||||
}
|
||||
|
||||
for (const KeyValue<Animation::TypeHash, TrackValue *> &K : track_values) {
|
||||
TrackValue *track_value = K.value;
|
||||
TrackValue *other_track_value = to_data.track_values[K.key];
|
||||
|
||||
track_value->blend(*other_track_value, lambda);
|
||||
}
|
||||
}
|
||||
|
||||
void sample_from_animation(const Ref<Animation> &animation, const Skeleton3D *skeleton_3d, double p_time);
|
||||
|
||||
AHashMap<Animation::TypeHash, TrackValue *, HashHasher> track_values; // Animation::Track to TrackValue
|
||||
|
||||
protected:
|
||||
@ -192,10 +306,14 @@ public:
|
||||
|
||||
class AnimationBlend2Node : public SyncedAnimationNode {
|
||||
public:
|
||||
float blend_weight = 0.0f;
|
||||
|
||||
void get_input_names(Vector<StringName> &inputs) const override {
|
||||
inputs.push_back("Input0");
|
||||
inputs.push_back("Input1");
|
||||
}
|
||||
|
||||
void evaluate(GraphEvaluationContext &context, const Vector<AnimationData *> &inputs, AnimationData &output) override;
|
||||
};
|
||||
|
||||
struct BlendTreeConnection {
|
||||
|
||||
@ -54,7 +54,7 @@ struct SyncedAnimationGraphFixture {
|
||||
|
||||
namespace TestSyncedAnimationGraph {
|
||||
|
||||
TEST_CASE("[SyncedAnimationGraph] TestBlendTreeConstruction") {
|
||||
TEST_CASE("[SyncedAnimationGraph] Test BlendTree construction") {
|
||||
BlendTreeBuilder tree_constructor;
|
||||
|
||||
Ref<AnimationSamplerNode> animation_sampler_node0;
|
||||
@ -133,6 +133,34 @@ TEST_CASE("[SyncedAnimationGraph] TestBlendTreeConstruction") {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(SyncedAnimationGraphFixture, "[SceneTree][SyncedAnimationGraph] Test AnimationData blending") {
|
||||
AnimationData data_t0;
|
||||
data_t0.sample_from_animation(test_animation, skeleton_node, 0.0);
|
||||
|
||||
AnimationData data_t1;
|
||||
data_t1.sample_from_animation(test_animation, skeleton_node, 1.0);
|
||||
|
||||
AnimationData data_t0_5;
|
||||
data_t0_5.sample_from_animation(test_animation, skeleton_node, 0.5);
|
||||
|
||||
AnimationData data_blended = data_t0;
|
||||
data_blended.blend(data_t1, 0.5);
|
||||
|
||||
REQUIRE(data_blended.has_same_tracks(data_t0_5));
|
||||
for (const KeyValue<Animation::TypeHash, AnimationData::TrackValue *> &K : data_blended.track_values) {
|
||||
CHECK(K.value->operator==(*data_t0_5.track_values.find(K.key)->value));
|
||||
}
|
||||
|
||||
// And also check that values do not match
|
||||
data_blended = data_t0;
|
||||
data_blended.blend(data_t1, 0.3);
|
||||
|
||||
REQUIRE(data_blended.has_same_tracks(data_t0_5));
|
||||
for (const KeyValue<Animation::TypeHash, AnimationData::TrackValue *> &K : data_blended.track_values) {
|
||||
CHECK(K.value->operator!=(*data_t0_5.track_values.find(K.key)->value));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(SyncedAnimationGraphFixture, "[SceneTree][SyncedAnimationGraph] SyncedAnimationGraph with an AnimationSampler as root node") {
|
||||
Ref<AnimationSamplerNode> animation_sampler_node;
|
||||
animation_sampler_node.instantiate();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user