Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize animation blend tree process #68593

Merged
merged 1 commit into from
Nov 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions scene/animation/animation_blend_space_1d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -309,8 +309,8 @@ double AnimationNodeBlendSpace1D::process(double p_time, bool p_seek, bool p_see
if (i == point_lower || i == point_higher) {
double remaining = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_seek_root, weights[i], FILTER_IGNORE, true);
max_time_remaining = MAX(max_time_remaining, remaining);
} else {
blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_seek_root, 0, FILTER_IGNORE, sync);
} else if (sync) {
blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_seek_root, 0, FILTER_IGNORE, true);
}
}

Expand Down
12 changes: 7 additions & 5 deletions scene/animation/animation_blend_space_2d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -512,8 +512,8 @@ double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek, bool p_see
}
}

if (!found) {
blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_seek_root, 0, FILTER_IGNORE, sync);
if (sync && !found) {
blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_seek_root, 0, FILTER_IGNORE, true);
}
}
} else {
Expand Down Expand Up @@ -550,9 +550,11 @@ double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek, bool p_see
mind = blend_node(blend_points[cur_closest].name, blend_points[cur_closest].node, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, true);
}

for (int i = 0; i < blend_points_used; i++) {
if (i != cur_closest) {
blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_seek_root, 0, FILTER_IGNORE, sync);
if (sync) {
for (int i = 0; i < blend_points_used; i++) {
if (i != cur_closest) {
blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_seek_root, 0, FILTER_IGNORE, true);
}
}
}
}
Expand Down
8 changes: 5 additions & 3 deletions scene/animation/animation_blend_tree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -726,9 +726,11 @@ double AnimationNodeTransition::process(double p_time, bool p_seek, bool p_seek_

double rem = 0.0;

for (int i = 0; i < enabled_inputs; i++) {
if (i != cur_current && i != cur_prev) {
blend_input(i, p_time, p_seek, p_seek_root, 0, FILTER_IGNORE, sync);
if (sync) {
for (int i = 0; i < enabled_inputs; i++) {
if (i != cur_current && i != cur_prev) {
blend_input(i, p_time, p_seek, p_seek_root, 0, FILTER_IGNORE, true);
}
}
}

Expand Down
143 changes: 55 additions & 88 deletions scene/animation/animation_tree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -289,11 +289,8 @@ double AnimationNode::_blend_node(const StringName &p_subpath, const Vector<Stri
new_path = String(parent->base_path) + String(p_subpath) + "/";
}

// If tracks for blending don't exist for one of the animations, Rest or RESET animation is blended as init animation instead.
// Then blend weight is 0 means that the init animation blend weight is 1.
// In that case, processing only the animation with the lacking track will not process the lacking track, and will not properly apply the Reset value.
// This means that all tracks which the animations in the branch that may be blended have must be processed.
// Therefore, the blending process must be executed even if the blend weight is 0.
// This process, which depends on p_sync is needed to process sync correctly in the case of
// that a synced AnimationNodeSync exists under the un-synced AnimationNodeSync.
if (!p_seek && !p_sync && !any_valid) {
return p_node->_pre_process(new_path, new_parent, state, 0, p_seek, p_seek_root, p_connections);
}
Expand Down Expand Up @@ -596,6 +593,13 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {

track = track_value;

// If a value track without a key is cached first, the initial value cannot be determined.
// It is a corner case, but which may cause problems with blending.
ERR_CONTINUE_MSG(anim->track_get_key_count(i) == 0, "AnimationTree: '" + String(E) + "', value track: '" + String(path) + "' must have at least one key to cache for blending.");
track_value->init_value = anim->track_get_key_value(i, 0);
track_value->init_value.zero();

// If there is a Reset Animation, it takes precedence by overwriting.
if (has_reset_anim) {
int rt = reset_anim->find_track(path, track_type);
if (rt >= 0 && reset_anim->track_get_key_count(rt) > 0) {
Expand Down Expand Up @@ -955,8 +959,44 @@ void AnimationTree::_process_graph(double p_delta) {
if (!state.valid) {
return; //state is not valid. do nothing.
}
//apply value/transform/bezier blends to track caches and execute method/audio/animation tracks

// Init all value/transform/blend/bezier tracks that track_cache has.
{
for (const KeyValue<NodePath, TrackCache *> &K : track_cache) {
TrackCache *track = K.value;

switch (track->type) {
case Animation::TYPE_POSITION_3D: {
TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
if (track->root_motion) {
t->loc = Vector3(0, 0, 0);
t->rot = Quaternion(0, 0, 0, 1);
t->scale = Vector3(0, 0, 0);
} else {
t->loc = t->init_loc;
t->rot = t->init_rot;
t->scale = t->init_scale;
}
} break;
case Animation::TYPE_BLEND_SHAPE: {
TrackCacheBlendShape *t = static_cast<TrackCacheBlendShape *>(track);
t->value = t->init_value;
} break;
case Animation::TYPE_VALUE: {
TrackCacheValue *t = static_cast<TrackCacheValue *>(track);
t->value = t->init_value;
} break;
case Animation::TYPE_BEZIER: {
TrackCacheBezier *t = static_cast<TrackCacheBezier *>(track);
t->value = t->init_value;
} break;
default: {
} break;
}
}
}

// Apply value/transform/blend/bezier blends to track caches and execute method/audio/animation tracks.
{
bool can_call = is_inside_tree() && !Engine::get_singleton()->is_editor_hint();

Expand All @@ -968,7 +1008,7 @@ void AnimationTree::_process_graph(double p_delta) {
bool seeked = as.seeked;
int pingponged = as.pingponged;
#ifndef _3D_DISABLED
bool backward = signbit(delta);
bool backward = signbit(delta); // This flag is required only for the root motion since it calculates the difference between the previous and current frames.
bool calc_root = !seeked || as.seek_root;
#endif // _3D_DISABLED

Expand All @@ -978,37 +1018,29 @@ void AnimationTree::_process_graph(double p_delta) {
}

NodePath path = a->track_get_path(i);

ERR_CONTINUE(!track_cache.has(path));

TrackCache *track = track_cache[path];

ERR_CONTINUE(!state.track_map.has(path));
int blend_idx = state.track_map[path];
ERR_CONTINUE(blend_idx < 0 || blend_idx >= state.track_count);
real_t blend = (*as.track_blends)[blend_idx] * weight;
if (blend < CMP_EPSILON) {
continue; // Nothing to blend.
}

Animation::TrackType ttype = a->track_get_type(i);
if (ttype != Animation::TYPE_POSITION_3D && ttype != Animation::TYPE_ROTATION_3D && ttype != Animation::TYPE_SCALE_3D && track->type != ttype) {
//broken animation, but avoid error spamming
continue;
}

track->root_motion = root_motion_track == path;

ERR_CONTINUE(!state.track_map.has(path));
int blend_idx = state.track_map[path];

ERR_CONTINUE(blend_idx < 0 || blend_idx >= state.track_count);

real_t blend = (*as.track_blends)[blend_idx] * weight;

switch (ttype) {
case Animation::TYPE_POSITION_3D: {
#ifndef _3D_DISABLED
TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
if (track->root_motion && calc_root) {
if (t->process_pass != process_pass) {
t->process_pass = process_pass;
t->loc = Vector3(0, 0, 0);
t->rot = Quaternion(0, 0, 0, 1);
t->scale = Vector3(0, 0, 0);
}
double prev_time = time - delta;
if (!backward) {
if (prev_time < 0) {
Expand Down Expand Up @@ -1084,12 +1116,6 @@ void AnimationTree::_process_graph(double p_delta) {
prev_time = !backward ? 0 : (double)a->get_length();

} else {
if (t->process_pass != process_pass) {
t->process_pass = process_pass;
t->loc = t->init_loc;
t->rot = t->init_rot;
t->scale = t->init_scale;
}
Vector3 loc;

Error err = a->position_track_interpolate(i, time, &loc);
Expand All @@ -1106,12 +1132,6 @@ void AnimationTree::_process_graph(double p_delta) {
#ifndef _3D_DISABLED
TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
if (track->root_motion && calc_root) {
if (t->process_pass != process_pass) {
t->process_pass = process_pass;
t->loc = Vector3(0, 0, 0);
t->rot = Quaternion(0, 0, 0, 1);
t->scale = Vector3(0, 0, 0);
}
double prev_time = time - delta;
if (!backward) {
if (prev_time < 0) {
Expand Down Expand Up @@ -1186,12 +1206,6 @@ void AnimationTree::_process_graph(double p_delta) {
prev_time = !backward ? 0 : (double)a->get_length();

} else {
if (t->process_pass != process_pass) {
t->process_pass = process_pass;
t->loc = t->init_loc;
t->rot = t->init_rot;
t->scale = t->init_scale;
}
Quaternion rot;

Error err = a->rotation_track_interpolate(i, time, &rot);
Expand All @@ -1208,12 +1222,6 @@ void AnimationTree::_process_graph(double p_delta) {
#ifndef _3D_DISABLED
TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
if (track->root_motion && calc_root) {
if (t->process_pass != process_pass) {
t->process_pass = process_pass;
t->loc = Vector3(0, 0, 0);
t->rot = Quaternion(0, 0, 0, 1);
t->scale = Vector3(0, 0, 0);
}
double prev_time = time - delta;
if (!backward) {
if (prev_time < 0) {
Expand Down Expand Up @@ -1289,12 +1297,6 @@ void AnimationTree::_process_graph(double p_delta) {
prev_time = !backward ? 0 : (double)a->get_length();

} else {
if (t->process_pass != process_pass) {
t->process_pass = process_pass;
t->loc = t->init_loc;
t->rot = t->init_rot;
t->scale = t->init_scale;
}
Vector3 scale;

Error err = a->scale_track_interpolate(i, time, &scale);
Expand All @@ -1311,11 +1313,6 @@ void AnimationTree::_process_graph(double p_delta) {
#ifndef _3D_DISABLED
TrackCacheBlendShape *t = static_cast<TrackCacheBlendShape *>(track);

if (t->process_pass != process_pass) {
t->process_pass = process_pass;
t->value = t->init_value;
}

float value;

Error err = a->blend_shape_track_interpolate(i, time, &value);
Expand All @@ -1342,15 +1339,6 @@ void AnimationTree::_process_graph(double p_delta) {
continue;
}

if (t->process_pass != process_pass) {
t->process_pass = process_pass;
if (!t->init_value) {
t->init_value = value;
t->init_value.zero();
}
t->value = t->init_value;
}

// Special case for angle interpolation.
if (t->is_using_angle) {
// For blending consistency, it prevents rotation of more than 180 degrees from init_value.
Expand Down Expand Up @@ -1379,10 +1367,6 @@ void AnimationTree::_process_graph(double p_delta) {
}
}
} else {
if (blend < CMP_EPSILON) {
continue; //nothing to blend
}

if (seeked) {
int idx = a->track_find_key(i, time);
if (idx < 0) {
Expand All @@ -1404,9 +1388,6 @@ void AnimationTree::_process_graph(double p_delta) {

} break;
case Animation::TYPE_METHOD: {
if (blend < CMP_EPSILON) {
continue; //nothing to blend
}
TrackCacheMethod *t = static_cast<TrackCacheMethod *>(track);

if (seeked) {
Expand Down Expand Up @@ -1437,17 +1418,9 @@ void AnimationTree::_process_graph(double p_delta) {
real_t bezier = a->bezier_track_interpolate(i, time);
bezier = _post_process_key_value(a, i, bezier, t->object);

if (t->process_pass != process_pass) {
t->process_pass = process_pass;
t->value = t->init_value;
}

t->value += (bezier - t->init_value) * blend;
} break;
case Animation::TYPE_AUDIO: {
if (blend < CMP_EPSILON) {
continue; //nothing to blend
}
TrackCacheAudio *t = static_cast<TrackCacheAudio *>(track);

if (seeked) {
Expand Down Expand Up @@ -1555,9 +1528,6 @@ void AnimationTree::_process_graph(double p_delta) {
t->object->call(SNAME("set_volume_db"), db);
} break;
case Animation::TYPE_ANIMATION: {
if (blend < CMP_EPSILON) {
continue; //nothing to blend
}
TrackCacheAnimation *t = static_cast<TrackCacheAnimation *>(track);

AnimationPlayer *player2 = Object::cast_to<AnimationPlayer>(t->object);
Expand Down Expand Up @@ -1639,9 +1609,6 @@ void AnimationTree::_process_graph(double p_delta) {
// finally, set the tracks
for (const KeyValue<NodePath, TrackCache *> &K : track_cache) {
TrackCache *track = K.value;
if (track->process_pass != process_pass) {
continue; //not processed, ignore
}

switch (track->type) {
case Animation::TYPE_POSITION_3D: {
Expand Down
1 change: 0 additions & 1 deletion scene/animation/animation_tree.h
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,6 @@ class AnimationTree : public Node {
struct TrackCache {
bool root_motion = false;
uint64_t setup_pass = 0;
uint64_t process_pass = 0;
Animation::TrackType type = Animation::TrackType::TYPE_ANIMATION;
Object *object = nullptr;
ObjectID object_id;
Expand Down