diff --git a/collections/_article/design-of-the-skeleton-modifier-3d.md b/collections/_article/design-of-the-skeleton-modifier-3d.md new file mode 100644 index 0000000000..41867915fb --- /dev/null +++ b/collections/_article/design-of-the-skeleton-modifier-3d.md @@ -0,0 +1,102 @@ +--- +title: "Design of the Skeleton Modifier 3D" +excerpt: "Skeleton bone update process is reworked to add SkeletonModifier3D for modifying the Skeleton." +categories: ["progress-report"] +author: Silc Renew +image: /storage/blog/covers/design-of-the-skeleton-modifier-3d.webp +date: 2024-06-01 00:00:00 +--- + +A new node called `SkeletonModifier3D` will be added in Godot 4.3. It is to animate `Skeleton3D` outside of `AnimationMixer` and is the base class for several existing nodes. + +Thus, we add the deprecated flag on some old properties with "override" in `Skeleton3D`. + +- `set_bone_global_pose_override()` +- `get_bone_global_pose_override()` +- `get_bone_global_pose_no_override()` +- `clear_bones_global_pose_override()` + +# Did the pose override design have problems? + +![modification](/storage/blog/design-of-the-skeleton-modifier-3d/mod.webp) + +Previously, we recommended using the property `global_pose_override` when modifying the bones. This was useful because the original pose was kept separately, so blend values could be set, and bones could be modified without changing the property in `.tscn` file. However, the more complex people's demands for Godot 3D became, the less it covered the use cases and became outdated. + +The main problem is the fact that "the processing order between `Skeleton3D` and `AnimationMixer` is changed depending on the `SceneTree` structure`. + +**For example, it means that the following two scenes will have different results:** + +![different process orders](/storage/blog/design-of-the-skeleton-modifier-3d/different_process_orders.webp) + +If there is modifier such as IK or physical bone, in most of cases, it needs to be applied to the result of the played animation. So they need to be processed after the `AnimationMixer`. + +In old skeleton modifier design with bone pose override you must place those modifiers below the `AnimationMixer`. However as scene trees become more complex, it becomes difficult to keep track of the processing order. Also the scene might be imported from glTF and cannot edit without localization, so managing node order becomes tedious. + +Moreover, if multiple nodes use bone pose override, it breaks the modified result. + +**Let's imagine a case in which bone modification is performed in the following order:** + +``` +AnimationMixer -> ModifierA -> ModifierB +``` + +Keep in mind that both `ModifierA` and `ModifierB` need to get the bone pose that was processed immediately before. + +The `AnimationMixer` does not use `set_bone_global_pose_override()`, so it transforms the original pose as `set_bone_pose_rotation()`. This means that the input to `ModifierA` must be get from the original pose with `get_bone_global_pose_no_override()` and the output must be get from override with `get_bone_global_pose_override()`. In this case, if `ModiferB` wants to consider the output of `ModiferA`, both the input and output of `ModifierB` must be override with `get_bone_global_pose_override()`. + +Then, can the order of `ModifierA` and `ModifierB` be interchanged? + +--The answer is "NO". + +Because `ModifierB`'s input is now `get_bone_global_pose_override()` which is different from `get_bone_global_pose_no_override()`, so `ModifierB` cannot get the original pose set by the `AnimationMixer`. + +As I described above, the override design was very weak in terms of process ordering. + +# How the new skeleton design to work with SkeletonModifier3D? + +`SkeletonModifier3D` is designed to modify bones in the `_process_modification()` as virtual method. This means that if you want to develop a custom `SkeletonModifier3D`, you will need to modify the bones within that method. + +`SkeletonModifier3D` does not execute modifications as stand alone, but is executed by the parent of `Skeleton3D`. By placing `SkeletonModifier3D` as a child of `Skeleton3D`, they are registered in `Skeleton3D`, and the process is executed only once per frame in the `Skeleton3D` update process. Then, **the processing order between modifiers is guaranteed to be the same as the order of the `Skeleton3D`'s child list**. + +Since `AnimationMixer` is applied before the `Skeleton3D` update process, `SkeletonModifier3D` is guaranteed to run after `AnimationMixer`. Also, they do not require `bone_pose_global_override`; This removes any confusion as to whether we should use override or not. + +**Here is a SkeletonModifier3D sequence diagram:** + +![skeleton modifier process](/storage/blog/design-of-the-skeleton-modifier-3d/skeleton_modifier_process.webp) + +Dirty flag resolution may be performed several times per frame, but the update process is a deferred call and is performed only once per frame. + +At the beginning of the update process, it stores the pose before the modification process temporarily. When the modification process is complete and applied to the skin, the pose is rolled back to the temporarily stored pose. This performs the role of the past `bone_pose_global_override` which stored the override pose separate from the original pose. + +By the way, you may want to get the pose after the modification, or you may wonder why the modifier in the later part cannot enter the original pose when there are multiple modifiers. + +We have added some signals for cases where you need a pause at each point in time, so you can use them. + +- AnimationMixer: mixer_applied + - Notifies when the blending result related have been applied to the target objects +- SkeletonModifier3D: modification_processed + - Notifies when the modification have been finished +- Skeleton3D: skeleton_updated + - Emitted when the final pose has been calculated will be applied to the skin in the update process + +Also, note that this process depends on the `Skeleton3D.modifier_callback_mode_process` property. + +![modifier callback mode process property](/storage/blog/design-of-the-skeleton-modifier-3d/modifier_callback_mode_process.webp) + +For example, in a use case that the node uses the physics process outside of `Skeleton3D` and it affects `SkeletonModifier3D`, the property must be set to `Physics`. + +Finally, now we can say that `SkeletonModifier3D` does not make it impossible to do anything that was possible in the past. + +# Do I always need to develop a SkeletonModifier3D when modifying a Skeleton bone? + +As explained above, the modification provided by `SkeletonModifier3D` is temporary. So `SkeletonModifier3D` would be appropriate for effectors and controllers as **post FX**. + +If you want permanent modifications, i.e., if you want to develop something like a bone editor, then it makes sense that it is not a `SkeletonModifier3D`. Also, in simple cases where it is guaranteed that no other `SkeletonModifier3D` will be used in the scene, your judgment will prevail. + +# What kind of SkeletonModifier3D nodes are included in Godot 4.3? + +For now, Godot 4.3 will be containing only `SkeletonModifier3D` which is a migration of several existing nodes that have been in existence since 4.0. + +But, there is good news! We are planning to add some `SkeletonModifier3D` in Godot 4.4, such as new IK, constraint, and jiggle. + +If you are interested in developing your own `SkeletonModifier3D`, during 4.3 should be a great time to send a proposal to include it in core. diff --git a/storage/blog/covers/design-of-the-skeleton-modifier-3d.webp b/storage/blog/covers/design-of-the-skeleton-modifier-3d.webp new file mode 100644 index 0000000000..479f7b618e Binary files /dev/null and b/storage/blog/covers/design-of-the-skeleton-modifier-3d.webp differ diff --git a/storage/blog/design-of-the-skeleton-modifier-3d/different_process_orders.webp b/storage/blog/design-of-the-skeleton-modifier-3d/different_process_orders.webp new file mode 100644 index 0000000000..540ef37086 Binary files /dev/null and b/storage/blog/design-of-the-skeleton-modifier-3d/different_process_orders.webp differ diff --git a/storage/blog/design-of-the-skeleton-modifier-3d/mod.webp b/storage/blog/design-of-the-skeleton-modifier-3d/mod.webp new file mode 100644 index 0000000000..2952aaf100 Binary files /dev/null and b/storage/blog/design-of-the-skeleton-modifier-3d/mod.webp differ diff --git a/storage/blog/design-of-the-skeleton-modifier-3d/modifier_callback_mode_process.webp b/storage/blog/design-of-the-skeleton-modifier-3d/modifier_callback_mode_process.webp new file mode 100644 index 0000000000..ce9ded76fe Binary files /dev/null and b/storage/blog/design-of-the-skeleton-modifier-3d/modifier_callback_mode_process.webp differ diff --git a/storage/blog/design-of-the-skeleton-modifier-3d/skeleton_modifier_process.webp b/storage/blog/design-of-the-skeleton-modifier-3d/skeleton_modifier_process.webp new file mode 100644 index 0000000000..7c90b6f7c5 Binary files /dev/null and b/storage/blog/design-of-the-skeleton-modifier-3d/skeleton_modifier_process.webp differ