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

Updated to Godot 4.X #1

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
18 changes: 9 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Godot Jigglebones

This is an addon that adds jigglebones to Godot 3.x. Jigglebones are bones that jiggle when the skeleton moves. They are used for procedural animation, so you can move only the important parts of the skeleton and the little bits will automatically jiggle with it.
This is an addon that adds jigglebones to Godot 4.x. Jigglebones are bones that jiggle when the skeleton moves. They are used for procedural animation, so you can move only the important parts of the skeleton and the little bits will automatically jiggle with it.

![Jiggle](images/jiggle.gif)

Expand All @@ -23,7 +23,7 @@ It can be used for:

[Here's a video of it in action!](https://youtu.be/hJtRCyUwJLY)

# How to use it
# How to use it

1. Download the repository as zip and extract it.
2. Copy the `addons` folder into the root folder of your project, writing into it if it already exists.
Expand Down Expand Up @@ -59,22 +59,22 @@ It can be used for:

**Forward Axis**: By default, the -Z axis is used as the forward direction of the bone, which works well with models exported from Blender. However, if you use another 3D modelling tool, the bone might be pointing into the wrong direction. In that case you can try some other axes here to see which is the right one.

**Collision Shape**: Optionally, select the desired `CollisionShape` node, which can be anywhere in your scene tree. The only supported shape is `SphereShape`. See below for more details.
**Collision Shape**: Optionally, select the desired `CollisionShape3D` node, which can be anywhere in your scene tree. The only supported shape is `SphereShape3D`. See below for more details.


# Collision

If you wish to use collision, create a `CollisionShape`, and within it, add a `SphereShape`. Set your desired location and radius.
If you wish to use collision, create a `CollisionShape3D`, and within it, add a `SphereShape3D`. Set your desired location and radius.

The `CollisionShape` can be placed anywhere in your tree, and can be one that is used for other purposes as well, as long as it is a `SphereShape`. If it is used exclusively for JiggleBones, make sure to disable the `CollisionShape`.
The `CollisionShape3D` can be placed anywhere in your tree, and can be one that is used for other purposes as well, as long as it is a `SphereShape3D`. If it is used exclusively for JiggleBones, make sure to disable the `CollisionShape3D`.

JiggleBones does not use the physics system. Instead it calculates its own physics. This `CollisionShape` is used only as a visual indicator to communicate where you want the JiggleBone to collide. Disabling it prevents the physics system from processing it. You may ignore the warning that will appear on the `CollisionShape` about needing a `PhysicsBody`.
JiggleBones does not use the physics system. Instead it calculates its own physics. This `CollisionShape3D` is used only as a visual indicator to communicate where you want the JiggleBone to collide. Disabling it prevents the physics system from processing it. You may ignore the warning that will appear on the `CollisionShape3D` about needing a `PhysicsBody`.

If you have an object with multiple JiggleBones, say a ponytail with 3 bones, you may set them all to the same `CollisionShape`.
If you have an object with multiple JiggleBones, say a ponytail with 3 bones, you may set them all to the same `CollisionShape3D`.

In the example below, the cape has 13 bones split up into 3 chains of 4 bones down the left, right and middle, plus a root bone. Only the top 3 of each chain are using JiggleBones, so 9 in total. The ponytail has 4 bones, but only the bottom 3 are using JiggleBones.
In the example below, the cape has 13 bones split up into 3 chains of 4 bones down the left, right and middle, plus a root bone. Only the top 3 of each chain are using JiggleBones, so 9 in total. The ponytail has 4 bones, but only the bottom 3 are using JiggleBones.

There are two `CollisionShapes`. The upper one keeps the ponytail off the back and cape. The lower one keeps the body from poking through the cape.
There are two `CollisionShape3Ds`. The upper one keeps the ponytail off the back and cape. The lower one keeps the body from poking through the cape.

This scene runs at 600-750fps @ 1920x1080 on a GTX 1060.

Expand Down
14 changes: 7 additions & 7 deletions addons/jigglebones/custom_node.gd
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
tool
@tool
extends EditorPlugin

func _enter_tree():
# Initialization of the plugin goes here
# Add the new type with a name, a parent type, a script and an icon
add_custom_type("Jigglebone", "Spatial", preload("jigglebone.gd"), preload("icon.svg"))
# Initialization of the plugin goes here
# Add the new type with a name, a parent type, a script and an icon
add_custom_type("Jigglebone", "Node3D", preload("jigglebone.gd"), preload("icon.svg"))

func _exit_tree():
# Clean-up of the plugin goes here
# Always remember to remove it from the engine when deactivated
remove_custom_type("Jigglebone")
# Clean-up of the plugin goes here
# Always remember to remove it from the engine when deactivated
remove_custom_type("Jigglebone")
52 changes: 26 additions & 26 deletions addons/jigglebones/jigglebone.gd
Original file line number Diff line number Diff line change
@@ -1,30 +1,30 @@
extends Spatial
extends Node3D

enum Axis {
X_Plus, Y_Plus, Z_Plus, X_Minus, Y_Minus, Z_Minus
}

export var enabled: bool = true
export (String) var bone_name: String
export (float, 0.1, 100, 0.1) var stiffness: float = 1
export (float, 0, 100, 0.1) var damping: float = 0
export var use_gravity: bool = false
export var gravity := Vector3(0, -9.81, 0)
export (Axis) var forward_axis: int = Axis.Z_Minus
export (NodePath) var collision_shape: NodePath setget set_collision_shape
@export var enabled: bool = true
@export var bone_name: String
@export_range(0.1, 100.0) var stiffness: float = 1.0
@export_range(0.0, 100.0) var damping: float = 0.0
@export var use_gravity: bool = false
@export var gravity := Vector3(0, -9.81, 0)
@export var forward_axis: Axis = Axis.Z_Minus
@export var collision_shape: NodePath : set = set_collision_shape

var skeleton: Skeleton
var skeleton: Skeleton3D
var bone_id: int
var bone_id_parent: int
var collision_sphere: CollisionShape
var collision_sphere: CollisionShape3D
var prev_pos: Vector3


func set_collision_shape(path:NodePath) -> void:
func set_collision_shape(path: NodePath) -> void:
collision_shape = path
collision_sphere = get_node_or_null(path)
if collision_sphere:
assert(collision_sphere is CollisionShape and collision_sphere.shape is SphereShape,
assert(collision_sphere is CollisionShape3D and collision_sphere.shape is SphereShape3D,
"%s: Only SphereShapes are supported for CollisionShapes" % [ name ])


Expand All @@ -33,14 +33,14 @@ func _ready() -> void:
set_physics_process(false)
return

set_as_toplevel(true) # Ignore parent transformation
set_as_top_level(true) # Ignore parent transformation
skeleton = get_parent() # Parent must be a Skeleton node
skeleton.clear_bones_global_pose_override()
prev_pos = global_transform.origin
set_collision_shape(collision_shape)


assert(! (is_nan(translation.x) or is_inf(translation.x)), "%s: Bone translation corrupted" % [ name ])
assert(! (is_nan(position.x) or is_inf(position.x)), "%s: Bone translation corrupted" % [ name ])
assert(bone_name, "%s: Please enter a bone name" % [ name ])
bone_id = skeleton.find_bone(bone_name)
assert(bone_id != -1, "%s: Unknown bone %s - Please enter a valid bone name" % [ name, bone_name ])
Expand All @@ -57,24 +57,24 @@ func _physics_process(delta) -> void:

# See https://godotengine.org/qa/7631/armature-differences-between-bones-custom_pose-transform

var bone_transf_obj: Transform = skeleton.get_bone_global_pose(bone_id) # Object space bone pose
var bone_transf_world: Transform = skeleton.global_transform * bone_transf_obj
var bone_transf_obj: Transform3D = skeleton.get_bone_global_pose(bone_id) # Object space bone pose
var bone_transf_world: Transform3D = skeleton.global_transform * bone_transf_obj

var bone_transf_rest_local: Transform = skeleton.get_bone_rest(bone_id)
var bone_transf_rest_obj: Transform = skeleton.get_bone_global_pose(bone_id_parent) * bone_transf_rest_local
var bone_transf_rest_world: Transform = skeleton.global_transform * bone_transf_rest_obj
var bone_transf_rest_local: Transform3D = skeleton.get_bone_rest(bone_id)
var bone_transf_rest_obj: Transform3D = skeleton.get_bone_global_pose(bone_id_parent) * bone_transf_rest_local
var bone_transf_rest_world: Transform3D = skeleton.global_transform * bone_transf_rest_obj

############### Integrate velocity (Verlet integration) ##############
############### Integrate velocity (Verlet integration) ##############

# If not using gravity, apply force in the direction of the bone (so it always wants to point "forward")
var grav: Vector3 = bone_transf_rest_world.basis.xform(Vector3(0, 0, -1)).normalized() * 9.81
var grav: Vector3 = (bone_transf_rest_world.basis * Vector3(0, 0, -1)).normalized() * 9.81
var vel: Vector3 = (global_transform.origin - prev_pos) / delta

if use_gravity:
grav = gravity

grav *= stiffness
vel += grav
vel += grav
vel -= vel * damping * delta # Damping

prev_pos = global_transform.origin
Expand All @@ -94,7 +94,7 @@ func _physics_process(delta) -> void:

############## Rotate the bone to point to this object #############

var diff_vec_local: Vector3 = bone_transf_world.affine_inverse().xform(global_transform.origin).normalized()
var diff_vec_local: Vector3 = (bone_transf_world.affine_inverse() * global_transform.origin).normalized()

var bone_forward_local: Vector3 = get_bone_forward_local()

Expand All @@ -108,8 +108,8 @@ func _physics_process(delta) -> void:
bone_rotate_axis = bone_rotate_axis.normalized()

# Bring the axis to object space, WITHOUT translation (so only the BASIS is used) since vectors shouldn't be translated
var bone_rotate_axis_obj: Vector3 = bone_transf_obj.basis.xform(bone_rotate_axis).normalized()
var bone_new_transf_obj: Transform = Transform(bone_transf_obj.basis.rotated(bone_rotate_axis_obj, bone_rotate_angle), bone_transf_obj.origin)
var bone_rotate_axis_obj: Vector3 = (bone_transf_obj.basis * bone_rotate_axis).normalized()
var bone_new_transf_obj: Transform3D = Transform3D(bone_transf_obj.basis.rotated(bone_rotate_axis_obj, bone_rotate_angle), bone_transf_obj.origin)

skeleton.set_bone_global_pose_override(bone_id, bone_new_transf_obj, 0.5, true)

Expand Down