mirror of
https://github.com/godotengine/godot.git
synced 2024-11-23 04:33:29 +00:00
Add root_motion_local option to AnimationMixer
This commit is contained in:
parent
5efd124ca1
commit
755bcf4737
@ -112,13 +112,13 @@
|
||||
The most basic example is applying position to [CharacterBody3D]:
|
||||
[codeblocks]
|
||||
[gdscript]
|
||||
var current_rotation: Quaternion
|
||||
var current_rotation
|
||||
|
||||
func _process(delta):
|
||||
if Input.is_action_just_pressed("animate"):
|
||||
current_rotation = get_quaternion()
|
||||
state_machine.travel("Animate")
|
||||
var velocity: Vector3 = current_rotation * animation_tree.get_root_motion_position() / delta
|
||||
var velocity = current_rotation * animation_tree.get_root_motion_position() / delta
|
||||
set_velocity(velocity)
|
||||
move_and_slide()
|
||||
[/gdscript]
|
||||
@ -130,7 +130,20 @@
|
||||
if Input.is_action_just_pressed("animate"):
|
||||
state_machine.travel("Animate")
|
||||
set_quaternion(get_quaternion() * animation_tree.get_root_motion_rotation())
|
||||
var velocity: Vector3 = (animation_tree.get_root_motion_rotation_accumulator().inverse() * get_quaternion()) * animation_tree.get_root_motion_position() / delta
|
||||
var velocity = (animation_tree.get_root_motion_rotation_accumulator().inverse() * get_quaternion()) * animation_tree.get_root_motion_position() / delta
|
||||
set_velocity(velocity)
|
||||
move_and_slide()
|
||||
[/gdscript]
|
||||
[/codeblocks]
|
||||
If [member root_motion_local] is [code]true[/code], return the pre-multiplied translation value with the inverted rotation.
|
||||
In this case, the code can be written as follows:
|
||||
[codeblocks]
|
||||
[gdscript]
|
||||
func _process(delta):
|
||||
if Input.is_action_just_pressed("animate"):
|
||||
state_machine.travel("Animate")
|
||||
set_quaternion(get_quaternion() * animation_tree.get_root_motion_rotation())
|
||||
var velocity = get_quaternion() * animation_tree.get_root_motion_position() / delta
|
||||
set_velocity(velocity)
|
||||
move_and_slide()
|
||||
[/gdscript]
|
||||
@ -145,13 +158,13 @@
|
||||
For example, if an animation with only one key [code]Vector3(0, 0, 0)[/code] is played in the previous frame and then an animation with only one key [code]Vector3(1, 0, 1)[/code] is played in the next frame, the difference can be calculated as follows:
|
||||
[codeblocks]
|
||||
[gdscript]
|
||||
var prev_root_motion_position_accumulator: Vector3
|
||||
var prev_root_motion_position_accumulator
|
||||
|
||||
func _process(delta):
|
||||
if Input.is_action_just_pressed("animate"):
|
||||
state_machine.travel("Animate")
|
||||
var current_root_motion_position_accumulator: Vector3 = animation_tree.get_root_motion_position_accumulator()
|
||||
var difference: Vector3 = current_root_motion_position_accumulator - prev_root_motion_position_accumulator
|
||||
var current_root_motion_position_accumulator = animation_tree.get_root_motion_position_accumulator()
|
||||
var difference = current_root_motion_position_accumulator - prev_root_motion_position_accumulator
|
||||
prev_root_motion_position_accumulator = current_root_motion_position_accumulator
|
||||
transform.origin += difference
|
||||
[/gdscript]
|
||||
@ -185,13 +198,13 @@
|
||||
For example, if an animation with only one key [code]Quaternion(0, 0, 0, 1)[/code] is played in the previous frame and then an animation with only one key [code]Quaternion(0, 0.707, 0, 0.707)[/code] is played in the next frame, the difference can be calculated as follows:
|
||||
[codeblocks]
|
||||
[gdscript]
|
||||
var prev_root_motion_rotation_accumulator: Quaternion
|
||||
var prev_root_motion_rotation_accumulator
|
||||
|
||||
func _process(delta):
|
||||
if Input.is_action_just_pressed("animate"):
|
||||
state_machine.travel("Animate")
|
||||
var current_root_motion_rotation_accumulator: Quaternion = animation_tree.get_root_motion_rotation_accumulator()
|
||||
var difference: Quaternion = prev_root_motion_rotation_accumulator.inverse() * current_root_motion_rotation_accumulator
|
||||
var current_root_motion_rotation_accumulator = animation_tree.get_root_motion_rotation_accumulator()
|
||||
var difference = prev_root_motion_rotation_accumulator.inverse() * current_root_motion_rotation_accumulator
|
||||
prev_root_motion_rotation_accumulator = current_root_motion_rotation_accumulator
|
||||
transform.basis *= Basis(difference)
|
||||
[/gdscript]
|
||||
@ -208,8 +221,8 @@
|
||||
The most basic example is applying scale to [CharacterBody3D]:
|
||||
[codeblocks]
|
||||
[gdscript]
|
||||
var current_scale: Vector3 = Vector3(1, 1, 1)
|
||||
var scale_accum: Vector3 = Vector3(1, 1, 1)
|
||||
var current_scale = Vector3(1, 1, 1)
|
||||
var scale_accum = Vector3(1, 1, 1)
|
||||
|
||||
func _process(delta):
|
||||
if Input.is_action_just_pressed("animate"):
|
||||
@ -229,13 +242,13 @@
|
||||
For example, if an animation with only one key [code]Vector3(1, 1, 1)[/code] is played in the previous frame and then an animation with only one key [code]Vector3(2, 2, 2)[/code] is played in the next frame, the difference can be calculated as follows:
|
||||
[codeblocks]
|
||||
[gdscript]
|
||||
var prev_root_motion_scale_accumulator: Vector3
|
||||
var prev_root_motion_scale_accumulator
|
||||
|
||||
func _process(delta):
|
||||
if Input.is_action_just_pressed("animate"):
|
||||
state_machine.travel("Animate")
|
||||
var current_root_motion_scale_accumulator: Vector3 = animation_tree.get_root_motion_scale_accumulator()
|
||||
var difference: Vector3 = current_root_motion_scale_accumulator - prev_root_motion_scale_accumulator
|
||||
var current_root_motion_scale_accumulator = animation_tree.get_root_motion_scale_accumulator()
|
||||
var difference = current_root_motion_scale_accumulator - prev_root_motion_scale_accumulator
|
||||
prev_root_motion_scale_accumulator = current_root_motion_scale_accumulator
|
||||
transform.basis = transform.basis.scaled(difference)
|
||||
[/gdscript]
|
||||
@ -304,6 +317,9 @@
|
||||
This is used by the editor. If set to [code]true[/code], the scene will be saved with the effects of the reset animation (the animation with the key [code]"RESET"[/code]) applied as if it had been seeked to time 0, with the editor keeping the values that the scene had before saving.
|
||||
This makes it more convenient to preview and edit animations in the editor, as changes to the scene will not be saved as long as they are set in the reset animation.
|
||||
</member>
|
||||
<member name="root_motion_local" type="bool" setter="set_root_motion_local" getter="is_root_motion_local">
|
||||
If [code]true[/code], [method get_root_motion_position] value is extracted as a local translation value before blending. In other words, it is treated like the translation is done after the rotation.
|
||||
</member>
|
||||
<member name="root_motion_track" type="NodePath" setter="set_root_motion_track" getter="get_root_motion_track" default="NodePath("")">
|
||||
The path to the Animation track used for root motion. Paths must be valid scene-tree paths to a node, and must be specified starting from the parent node of the node that will reproduce the animation. The [member root_motion_track] uses the same format as [method Animation.track_set_path], but note that a bone must be specified.
|
||||
If the track has type [constant Animation.TYPE_POSITION_3D], [constant Animation.TYPE_ROTATION_3D], or [constant Animation.TYPE_SCALE_3D] the transformation will be canceled visually, and the animation will appear to stay in place. See also [method get_root_motion_position], [method get_root_motion_rotation], [method get_root_motion_scale], and [RootMotionView].
|
||||
|
@ -127,6 +127,9 @@ void AnimationMixer::_validate_property(PropertyInfo &p_property) const {
|
||||
p_property.usage |= PROPERTY_USAGE_READ_ONLY;
|
||||
}
|
||||
#endif // TOOLS_ENABLED
|
||||
if (root_motion_track.is_empty() && p_property.name == "root_motion_local") {
|
||||
p_property.usage = PROPERTY_USAGE_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
@ -1200,6 +1203,10 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
|
||||
}
|
||||
TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
|
||||
if (track->root_motion && calc_root) {
|
||||
int rot_track = -1;
|
||||
if (root_motion_local) {
|
||||
rot_track = a->find_track(a->track_get_path(i), Animation::TYPE_ROTATION_3D);
|
||||
}
|
||||
double prev_time = time - delta;
|
||||
if (!backward) {
|
||||
if (Animation::is_less_approx(prev_time, start)) {
|
||||
@ -1234,41 +1241,92 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
|
||||
}
|
||||
}
|
||||
}
|
||||
Vector3 loc[2];
|
||||
if (!backward) {
|
||||
if (Animation::is_greater_approx(prev_time, time)) {
|
||||
Error err = a->try_position_track_interpolate(i, prev_time, &loc[0]);
|
||||
if (err != OK) {
|
||||
continue;
|
||||
if (rot_track >= 0) {
|
||||
Vector3 loc[2];
|
||||
Quaternion rot;
|
||||
if (!backward) {
|
||||
if (Animation::is_greater_approx(prev_time, time)) {
|
||||
Error err = a->try_position_track_interpolate(i, prev_time, &loc[0]);
|
||||
if (err != OK) {
|
||||
continue;
|
||||
}
|
||||
loc[0] = post_process_key_value(a, i, loc[0], t->object_id, t->bone_idx);
|
||||
a->try_position_track_interpolate(i, end, &loc[1]);
|
||||
loc[1] = post_process_key_value(a, i, loc[1], t->object_id, t->bone_idx);
|
||||
|
||||
a->try_rotation_track_interpolate(rot_track, end, &rot);
|
||||
rot = post_process_key_value(a, rot_track, rot, t->object_id, t->bone_idx);
|
||||
|
||||
root_motion_cache.loc += rot.xform_inv(loc[1] - loc[0]) * blend;
|
||||
prev_time = start;
|
||||
}
|
||||
} else {
|
||||
if (Animation::is_less_approx(prev_time, time)) {
|
||||
Error err = a->try_position_track_interpolate(i, prev_time, &loc[0]);
|
||||
if (err != OK) {
|
||||
continue;
|
||||
}
|
||||
loc[0] = post_process_key_value(a, i, loc[0], t->object_id, t->bone_idx);
|
||||
a->try_position_track_interpolate(i, start, &loc[1]);
|
||||
loc[1] = post_process_key_value(a, i, loc[1], t->object_id, t->bone_idx);
|
||||
|
||||
a->try_rotation_track_interpolate(rot_track, start, &rot);
|
||||
rot = post_process_key_value(a, rot_track, rot, t->object_id, t->bone_idx);
|
||||
|
||||
root_motion_cache.loc += rot.xform_inv(loc[1] - loc[0]) * blend;
|
||||
prev_time = end;
|
||||
}
|
||||
loc[0] = post_process_key_value(a, i, loc[0], t->object_id, t->bone_idx);
|
||||
a->try_position_track_interpolate(i, end, &loc[1]);
|
||||
loc[1] = post_process_key_value(a, i, loc[1], t->object_id, t->bone_idx);
|
||||
root_motion_cache.loc += (loc[1] - loc[0]) * blend;
|
||||
prev_time = start;
|
||||
}
|
||||
Error err = a->try_position_track_interpolate(i, prev_time, &loc[0]);
|
||||
if (err != OK) {
|
||||
continue;
|
||||
}
|
||||
loc[0] = post_process_key_value(a, i, loc[0], t->object_id, t->bone_idx);
|
||||
a->try_position_track_interpolate(i, time, &loc[1]);
|
||||
loc[1] = post_process_key_value(a, i, loc[1], t->object_id, t->bone_idx);
|
||||
|
||||
a->try_rotation_track_interpolate(rot_track, time, &rot);
|
||||
rot = post_process_key_value(a, rot_track, rot, t->object_id, t->bone_idx);
|
||||
|
||||
root_motion_cache.loc += rot.xform_inv(loc[1] - loc[0]) * blend;
|
||||
prev_time = !backward ? start : end;
|
||||
} else {
|
||||
if (Animation::is_less_approx(prev_time, time)) {
|
||||
Error err = a->try_position_track_interpolate(i, prev_time, &loc[0]);
|
||||
if (err != OK) {
|
||||
continue;
|
||||
Vector3 loc[2];
|
||||
if (!backward) {
|
||||
if (Animation::is_greater_approx(prev_time, time)) {
|
||||
Error err = a->try_position_track_interpolate(i, prev_time, &loc[0]);
|
||||
if (err != OK) {
|
||||
continue;
|
||||
}
|
||||
loc[0] = post_process_key_value(a, i, loc[0], t->object_id, t->bone_idx);
|
||||
a->try_position_track_interpolate(i, end, &loc[1]);
|
||||
loc[1] = post_process_key_value(a, i, loc[1], t->object_id, t->bone_idx);
|
||||
root_motion_cache.loc += (loc[1] - loc[0]) * blend;
|
||||
prev_time = start;
|
||||
}
|
||||
} else {
|
||||
if (Animation::is_less_approx(prev_time, time)) {
|
||||
Error err = a->try_position_track_interpolate(i, prev_time, &loc[0]);
|
||||
if (err != OK) {
|
||||
continue;
|
||||
}
|
||||
loc[0] = post_process_key_value(a, i, loc[0], t->object_id, t->bone_idx);
|
||||
a->try_position_track_interpolate(i, start, &loc[1]);
|
||||
loc[1] = post_process_key_value(a, i, loc[1], t->object_id, t->bone_idx);
|
||||
root_motion_cache.loc += (loc[1] - loc[0]) * blend;
|
||||
prev_time = end;
|
||||
}
|
||||
loc[0] = post_process_key_value(a, i, loc[0], t->object_id, t->bone_idx);
|
||||
a->try_position_track_interpolate(i, start, &loc[1]);
|
||||
loc[1] = post_process_key_value(a, i, loc[1], t->object_id, t->bone_idx);
|
||||
root_motion_cache.loc += (loc[1] - loc[0]) * blend;
|
||||
prev_time = end;
|
||||
}
|
||||
Error err = a->try_position_track_interpolate(i, prev_time, &loc[0]);
|
||||
if (err != OK) {
|
||||
continue;
|
||||
}
|
||||
loc[0] = post_process_key_value(a, i, loc[0], t->object_id, t->bone_idx);
|
||||
a->try_position_track_interpolate(i, time, &loc[1]);
|
||||
loc[1] = post_process_key_value(a, i, loc[1], t->object_id, t->bone_idx);
|
||||
root_motion_cache.loc += (loc[1] - loc[0]) * blend;
|
||||
prev_time = !backward ? start : end;
|
||||
}
|
||||
Error err = a->try_position_track_interpolate(i, prev_time, &loc[0]);
|
||||
if (err != OK) {
|
||||
continue;
|
||||
}
|
||||
loc[0] = post_process_key_value(a, i, loc[0], t->object_id, t->bone_idx);
|
||||
a->try_position_track_interpolate(i, time, &loc[1]);
|
||||
loc[1] = post_process_key_value(a, i, loc[1], t->object_id, t->bone_idx);
|
||||
root_motion_cache.loc += (loc[1] - loc[0]) * blend;
|
||||
prev_time = !backward ? start : end;
|
||||
}
|
||||
{
|
||||
Vector3 loc;
|
||||
@ -1343,6 +1401,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
|
||||
}
|
||||
rot[0] = post_process_key_value(a, i, rot[0], t->object_id, t->bone_idx);
|
||||
a->try_rotation_track_interpolate(i, start, &rot[1]);
|
||||
rot[1] = post_process_key_value(a, i, rot[1], t->object_id, t->bone_idx);
|
||||
root_motion_cache.rot = (root_motion_cache.rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized();
|
||||
prev_time = end;
|
||||
}
|
||||
@ -1418,8 +1477,8 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
|
||||
}
|
||||
scale[0] = post_process_key_value(a, i, scale[0], t->object_id, t->bone_idx);
|
||||
a->try_scale_track_interpolate(i, end, &scale[1]);
|
||||
root_motion_cache.scale += (scale[1] - scale[0]) * blend;
|
||||
scale[1] = post_process_key_value(a, i, scale[1], t->object_id, t->bone_idx);
|
||||
root_motion_cache.scale += (scale[1] - scale[0]) * blend;
|
||||
prev_time = start;
|
||||
}
|
||||
} else {
|
||||
@ -1990,12 +2049,21 @@ void AnimationMixer::clear_caches() {
|
||||
|
||||
void AnimationMixer::set_root_motion_track(const NodePath &p_track) {
|
||||
root_motion_track = p_track;
|
||||
notify_property_list_changed();
|
||||
}
|
||||
|
||||
NodePath AnimationMixer::get_root_motion_track() const {
|
||||
return root_motion_track;
|
||||
}
|
||||
|
||||
void AnimationMixer::set_root_motion_local(bool p_enabled) {
|
||||
root_motion_local = p_enabled;
|
||||
}
|
||||
|
||||
bool AnimationMixer::is_root_motion_local() const {
|
||||
return root_motion_local;
|
||||
}
|
||||
|
||||
Vector3 AnimationMixer::get_root_motion_position() const {
|
||||
return root_motion_position;
|
||||
}
|
||||
@ -2341,6 +2409,8 @@ void AnimationMixer::_bind_methods() {
|
||||
/* ---- Root motion accumulator for Skeleton3D ---- */
|
||||
ClassDB::bind_method(D_METHOD("set_root_motion_track", "path"), &AnimationMixer::set_root_motion_track);
|
||||
ClassDB::bind_method(D_METHOD("get_root_motion_track"), &AnimationMixer::get_root_motion_track);
|
||||
ClassDB::bind_method(D_METHOD("set_root_motion_local", "enabled"), &AnimationMixer::set_root_motion_local);
|
||||
ClassDB::bind_method(D_METHOD("is_root_motion_local"), &AnimationMixer::is_root_motion_local);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_root_motion_position"), &AnimationMixer::get_root_motion_position);
|
||||
ClassDB::bind_method(D_METHOD("get_root_motion_rotation"), &AnimationMixer::get_root_motion_rotation);
|
||||
@ -2368,6 +2438,7 @@ void AnimationMixer::_bind_methods() {
|
||||
|
||||
ADD_GROUP("Root Motion", "root_motion_");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "root_motion_track"), "set_root_motion_track", "get_root_motion_track");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "root_motion_local"), "set_root_motion_local", "is_root_motion_local");
|
||||
|
||||
ADD_GROUP("Audio", "audio_");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "audio_max_polyphony", PROPERTY_HINT_RANGE, "1,127,1"), "set_audio_max_polyphony", "get_audio_max_polyphony");
|
||||
|
@ -334,6 +334,7 @@ protected:
|
||||
|
||||
/* ---- Root motion accumulator for Skeleton3D ---- */
|
||||
NodePath root_motion_track;
|
||||
bool root_motion_local = false;
|
||||
Vector3 root_motion_position = Vector3(0, 0, 0);
|
||||
Quaternion root_motion_rotation = Quaternion(0, 0, 0, 1);
|
||||
Vector3 root_motion_scale = Vector3(0, 0, 0);
|
||||
@ -446,6 +447,9 @@ public:
|
||||
void set_root_motion_track(const NodePath &p_track);
|
||||
NodePath get_root_motion_track() const;
|
||||
|
||||
void set_root_motion_local(bool p_enabled);
|
||||
bool is_root_motion_local() const;
|
||||
|
||||
Vector3 get_root_motion_position() const;
|
||||
Quaternion get_root_motion_rotation() const;
|
||||
Vector3 get_root_motion_scale() const;
|
||||
|
@ -94,7 +94,6 @@ void RootMotionView::_notification(int p_what) {
|
||||
|
||||
if (has_node(path)) {
|
||||
Node *node = get_node(path);
|
||||
|
||||
AnimationMixer *mixer = Object::cast_to<AnimationMixer>(node);
|
||||
if (mixer && mixer->is_active() && mixer->get_root_motion_track() != NodePath()) {
|
||||
if (is_processing_internal() && mixer->get_callback_mode_process() == AnimationMixer::ANIMATION_CALLBACK_MODE_PROCESS_PHYSICS) {
|
||||
@ -106,12 +105,12 @@ void RootMotionView::_notification(int p_what) {
|
||||
set_process_internal(true);
|
||||
set_physics_process_internal(false);
|
||||
}
|
||||
|
||||
transform.origin = mixer->get_root_motion_position();
|
||||
transform.basis = mixer->get_root_motion_rotation(); // Scale is meaningless.
|
||||
diff = mixer->get_root_motion_rotation_accumulator();
|
||||
diff = mixer->is_root_motion_local() ? Quaternion() : mixer->get_root_motion_rotation_accumulator();
|
||||
}
|
||||
}
|
||||
|
||||
if (!first && transform == Transform3D()) {
|
||||
return;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user