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

Cannot override bone Transform in code when using AnimationTree, even Deterministic = false #87428

Closed
nagasawamaki opened this issue Jan 21, 2024 · 8 comments

Comments

@nagasawamaki
Copy link

Tested versions

v4.2.1.stable.official [b09f793]

System information

Godot v4.2.stable - Windows 10.0.19045 - Vulkan (Forward+)

Issue description

When using AnimationTree, I cannot override the bone transform in code.
Everytime it seems the AnimationTree will reset my overrided bone transform,
even I have checked Deterministic = false option, as stated in #87400 .

Steps to reproduce

Import a animated model and set up AnimationTree node and active it, also check Deterministic = false option .

write a script to override bone transform, like below:

func _physics_process(delta):
	test.get_node("Armature/Skeleton3D").set_bone_global_pose_override(b_idx, Transform3D(), 1)

No any effect.

Minimal reproduction project (MRP)

bone_transform_override.zip

@TokageItLab
Copy link
Member

TokageItLab commented Jan 21, 2024

You should use set_bone_pose_rotation(). Also, as a specification, it cannot be overridden while the animation is playing although Deterministic = false. We are planning something like Bone Modification that can be blended with animation by reworking IK, but it is still in the planning stage.

@TokageItLab TokageItLab closed this as not planned Won't fix, can't repro, duplicate, stale Jan 21, 2024
@nagasawamaki
Copy link
Author

You should use set_bone_pose_rotation(). Also, as a specification, it cannot be overridden while the animation is playing although Deterministic = false. We are planning something like Bone Modification that can be blended with animation by reworking IK, but it is still in the planning stage.

I have changed the function from test.get_node("Armature/Skeleton3D").set_bone_global_pose_override(b_idx, Transform3D(), 1) to test.get_node("Armature/Skeleton3D").set_bone_pose_rotation(b_idx, Quaternion()) but still no any effect.

Do you mean currently I cannot change the bone rotation by code when using with animationTree?

@TokageItLab
Copy link
Member

TokageItLab commented Jan 21, 2024

See above. Animation values are applied after gdscript process().

Also, as a specification, it cannot be overridden while the animation is playing although Deterministic = false. We are planning something like Bone Modification that can be blended with animation by reworking IK, but it is still in the planning stage.

@KMouratidis
Copy link

KMouratidis commented Jan 22, 2024

@nagasawamaki does it work if you do the override in _process instead? E.g. this works for me:

extends Node3D

@onready var test : Node3D = $test2

func _process(delta):
	var b_idx = test.get_node("Armature/Skeleton3D").find_bone("Bone.001")
	test.get_node("Armature/Skeleton3D").set_bone_global_pose_override(b_idx, Quaternion())

Edit: Oh, it also works without process_priority too. But this does need the process priority:

extends Node3D

@onready var test = $test2

func _ready():
	process_priority = 1

func _process(delta):
	var b_idx = test.get_node("Armature/Skeleton3D").find_bone("Bone.001")
	test.get_node("Armature/Skeleton3D").set_bone_pose_rotation(b_idx, Quaternion())

@TokageItLab
Copy link
Member

TokageItLab commented Jan 22, 2024

Oh, indeed, the main problem is that the CallbackModeProcess of the AnimationMixer does not match the process of the script.

However, even if they do match, it is not safe because the order in which the animation values are applied depends on the node order in SceneTree or process_priority as @KMouratidis said.

@KMouratidis
Copy link

KMouratidis commented Jan 22, 2024

See above. Animation values are applied after gdscript process().

That doesn't sound right to me.

All internal code should be called before GDScript, as seen here:

if (p_physics) {
if (n->is_physics_processing_internal()) {
n->notification(Node::NOTIFICATION_INTERNAL_PHYSICS_PROCESS);
}
if (n->is_physics_processing()) {
n->notification(Node::NOTIFICATION_PHYSICS_PROCESS);
}
} else {
if (n->is_processing_internal()) {
n->notification(Node::NOTIFICATION_INTERNAL_PROCESS);
}
if (n->is_processing()) {
n->notification(Node::NOTIFICATION_PROCESS);
}
}

And if I'm not mistaken, the actual updates DO happen in process/physics process, as seen here:

case NOTIFICATION_INTERNAL_PROCESS: {
if (process_callback == ANIMATION_PROCESS_PHYSICS) {
break;
}
if (processing) {
_animation_process(get_process_delta_time());
}
} break;
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
if (process_callback == ANIMATION_PROCESS_IDLE) {
break;
}
if (processing) {
_animation_process(get_physics_process_delta_time());
}
} break;

So maybe the reason why it works in _process but not in _physics_process is because the order is:

  1. Physics process internal: AnimationPlayer / AnimationTree sets bone data
  2. Physics process gdscript: player overrides bone data
  3. Process internal: AnimationPlayer / AnimationTree sets bone data, overriding the user's override
  4. Process gdscript: overrides in here work

This explains why set_bone_global_pose_override in _physics_process doesn't work but _process works.

As for the reason this doesn't apply to set_bone_pose_rotation unless you set process_priority, I'm still unable to understand. :facepalm: I think this is because of the tree order processing, as mentioned in the comment above:

  1. Player updates something at top node
  2. Grandchild node goes over the above 4 steps

Which with process_priority=1 becomes:

  1. Grandchild node goes over the above 4 steps
  2. Player updates something at top node

And maybe overrides just overshadow all the other modifications?

@TokageItLab
Copy link
Member

TokageItLab commented Jan 22, 2024

set_bone_global_pose_override - bone_pose_override was implemented to ignore process order for BoneModifications that need to be applied after Animation like IK or PhysicsBone, as hack, but the problem here would be that even if it did, it would not ignore the order for rendering server with skinning (and also maybe multi-threading will mess them up). I'm writing a proposal now to deprecate bone_pose_override in the future and instead have a cleaner control over process order for Animation.

@nagasawamaki
Copy link
Author

@KMouratidis Thanks! It works when I put the code inside _process. And thanks for your detailed explanation about the running order inside Godot.

@TokageItLab Got it. Hope your proposal will be implemented soon. Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants