• 3D
  • How to achieve inverse kinematics for DAZ Studio models?

I have exported a model as Collada from DAZ Studio, and Godot imports it with a skeleton. But programmatically moving the hand bone does not result in the whole arm moving.

Godot 3.1 api docs mention a SkeletonIK class that isn't present in 3.0 docs. https://docs.godotengine.org/en/3.1/classes/class_skeletonik.html But that API reference is all there is - there's no documentation on how to use it.

There are two pages for skeletons and inverse kinematics in the 3.0 docs: https://docs.godotengine.org/en/3.0/tutorials/3d/working_with_3d_skeletons.html https://docs.godotengine.org/en/3.0/tutorials/3d/inverse_kinematics.html But the links to the "latest version" of those fail to load (browser redirect loop).

Any ideas?

I didn't actually know about the SkeletonIK node until reading this, so thanks for that. 3D Skeleton IK has been a bit of a source of frustration with me too in Godot. I spent some time experimenting with the node and so far I've found that it isn't super great, but it could be that I just don't know how to implement it well. However, I'll share what I was able to produce below. If anyone has any insight into how to make this work better, I'd also like to know about it. 3D Skeleton IK is something I'll probably want to be using in the future for 2 bone character arms and legs. I figured I'd have to make my own implementation because I haven't found anything in Godot or from others that works well yet, but please let me know if such a thing exists. Otherwise, when it comes to the time that I need a solution, I'll do my best make one that works well and will share it.

SkeletonIK Script

extends SkeletonIK

func _ready():
	self.start()

Target_Origin Script

extends Spatial

export(float) var rotation_speed = 0.5
export(float) var translation_speed = 0.5

func _process(delta):
	
	if Input.is_key_pressed(KEY_D):
		self.rotate_y(self.rotation_speed * delta)
	if Input.is_key_pressed(KEY_A):
		self.rotate_y(-self.rotation_speed * delta)
	if Input.is_key_pressed(KEY_W):
		self.translation.y += self.translation_speed * delta 
	if Input.is_key_pressed(KEY_S):
		self.translation.y += -self.translation_speed * delta

As luck would have it, I just ran into an IK demo in the godot-demo-projects repository in the "/3d/ik/" folder. Thanks @TwistedTwigleg for putting that together. _

I'm glad the IK demo is of some help! Hopefully it shows how the plugin can be used through the examples. :smile:

I wrote the plugin based on this FABRIK IK solver from, surprisingly, the Roblox wiki. The article explains how the solver works and the code samples, with a little work, were relatively easy to convert to GDScript. Unfortunately, it seems the wiki page has since been taken down.

The only thing that the IK demo does not have that can be a deal breaker is no joint constraints. I kinda worked around it by making a middle joint target, but it is not ideal. Without joint constraints, sometimes joints, like elbows, can bend backwards. This makes the IK solver not quite as useful for simulating organic joints.

The SkeletonIK node also uses a FABRIK solver, though I think the implementation is slightly different. Last I knew, the SkeletonIK node also has no joint constraints, making it not quite as useful as I hoped when I first read about it. It should be a tad faster though, since it is written in C++ instead of GDScript.


Currently I'm playing around with taking the CCDIK solver in this Unity repository and converting it to Godot. Right now I have the base solver finished, but I cannot seem to clamp the rotation to the min/max angles without Godot going crazy. Without constraints, it reaches the target as expected, so I'm probably just missing something. I have some ideas on why it is not working and some potential solutions I can try to implement.

Assuming I can get it working, I'll make a post about it here on the forums in the future :smile:

Thanks both.

I found a video about using SkeletonIK: https://godotengine.org/article/skeleton-inverse-kinematic

Had to watch it a couple of times to figure stuff out, and I still missed the .start() call to make it actually work, but got there in the end.

As TwistedTwigleg points out, the lack of joint constraints causes unnatural positions - that will be an issue, but one I can probably defer to later.

Anyway, here's the test script I currently have, attached to the inherited scene created when opening the .dae file

extends Spatial

var Limbs = {}
onready var Skel = get_node("RootNode/Skeleton")
var Targets = Spatial.new()


func _ready():
	Limbs["Left Arm"]  = _createIkLimb("Left Arm" ,"lShldrBend","lHand")
	Limbs["Right Arm"] = _createIkLimb("Right Arm","rShldrBend","rHand")
	Limbs["Left Leg"]  = _createIkLimb("Left Leg" ,"lThighBend","lFoot")
	Limbs["Right Leg"] = _createIkLimb("Right Leg","rThighBend","rFoot")

	for LimbName in Limbs:
		Skel.add_child(Limbs[LimbName])

	self.add_child(Targets)

	for i in range(10):
		Targets.add_child(_createRandomTarget())


func _createIkLimb(Name,From,To):
	var Limb = SkeletonIK.new()
	Limb.set_meta("name",Name)
	Limb.set_root_bone(From)
	Limb.set_tip_bone(To)
	return Limb


func _createRandomTarget():
	var Target = CSGSphere.new()
	Target.set_radius(0.05)
	var x = rand_range(-1,0.5)*2 # ???
	var y = rand_range(0,1)*2
	var z = rand_range(0,0.2)
	Target.set_translation(Vector3(x,y,z))
	return Target


func _selectTarget():
	return Targets.get_child( rand_range(0,Targets.get_child_count()) )


func _process(delta):

	if Input.is_action_just_pressed("ui_left"):
		pose("Left Arm",_selectTarget())

	if Input.is_action_just_pressed("ui_right"):
		pose("Right Arm",_selectTarget())

	if Input.is_action_just_pressed("ui_down"):
		pose("Left Leg",_selectTarget())
		pose("Right Leg",_selectTarget())
	

func pose(LimbName,Target):
	Limbs[LimbName].set_target_node(Target.get_path())
	Limbs[LimbName].start(true)

I'm currently working on a FABRIK plugin with constraints based on the original Fabrik paper linked here Constraints will be added as custom resources in the inspector. The constraints I'll add first are, pole target, lookat target, ball joint constraint. You'll be able to add the constraints to individual bones and link them to target any Spatially derived nodes. I'll share when its finished enough.

5 months later

So I'm back to trying to solve this problem again.

@TwistedTwigleg said: Currently I'm playing around with taking the CCDIK solver in this Unity repository and converting it to Godot. ...

@"Dave The Dev" said: I'm currently working on a FABRIK plugin with constraints based on the original Fabrik paper linked here ...

I don't suppose either of you has any progress worth sharing?

I've made a decent amount of progress since I last posted. I've posted the progress on my Twitter feed, but here are some of the highlights:

First YouTube video of CCDIK solver:

Second YouTube video of CCDIK solver:

Tweet showing 2-joint IK progress:

https://twitter.com/TwistedTwigleg/status/1185691929327144963

Tweet showing IK animation support:

https://twitter.com/TwistedTwigleg/status/1190733571344019456

Unfortunately, I've been really busy with school work, so I haven't had an opportunity to work on it as of late. It is fairly usable right now, but I really want to keep working on it and polishing it, and then decide what to do with it. I have some ideas, but again, time is the biggest constraint right now.

@cybereality said: Wow! That looks really quite nice.

Thanks! :smile:

2 years later

not impressive at all ! can you add weights for neck and spines, not only the head shoukd look at, how can you achieve that like unity ? you can't.