[WIP] Inverse Kinematics

I spent some of today looking into Inverse Kinematics as I got a bit side tracked from my main project. It soon became apparent that this would be a large project, as there’s so many different kinds of algorithms you can use, constraints to apply, integrate with the jME animation system, and blend with current animations etc. And I couldn’t find much info on it, and most of the stuff I found was analytic stuff for robotics, so I sort of had to make it up as I went along. Instead of tackling it all at once, I was going to do it piece meal when I had time. Also didn’t want to spend a lot of time on it, if someone is already working on an implementation.

Heres what I came up with so far:

[video]http://www.youtube.com/watch?v=XxqttU555_c[/video]

12 Likes

hey that’s pretty good!
I know @rickard once worked on an IK control based no the ragdoll control http://hub.jmonkeyengine.org/forum/topic/inverse-kinematics-control-wip/
I also know @Kaelthas need a n IK solver to compute Ik contrainst from blender animation to properly load bone animation using IK constraints.

1 Like

Wow, that really looks cool :smiley:
Love it love it love it love it :smiley:

If you plan to add it as some kind of control that would really make things easier for me to load IK constraint from blender.
I was also looking into the subject of IK but my implementation did not even work :stuck_out_tongue:

1 Like

Looking good 8)

1 Like
<cite>@Kaelthas said:</cite> Wow, that really looks cool :D Love it love it love it love it :D

If you plan to add it as some kind of control that would really make things easier for me to load IK constraint from blender.
I was also looking into the subject of IK but my implementation did not even work :stuck_out_tongue:

hehe, yeh it took me a few attempts to get something that didn’t result in it flying everywhere all over the screen! ^^ What implementation did you try?

Im using Cyclic Coordinate Descent (CCD), and the best explanation I found, was here:

His example is for 2D, but that’s better as it gives you a clear picture on how it works (the next page has even better diagrams!)

I wouldn’t rely on me too much for this, at least not for a while :stuck_out_tongue: but I will post any further developments here and will @mention you, if I have anything useable

1 Like
I know @rickard once worked on an IK control based no the ragdoll control http://hub.jmonkeyengine.org/forum/topic/inverse-kinematics-control-wip/

Yes. That is in a working state. The version there has a limitation in that you can’t limit the number of joints that it affects (it goes all the way to the root bone).

1 Like

cool :slight_smile:

I implemented a limit on the number of joints it affects, and made a turret ^^

[video]http://www.youtube.com/watch?v=Nn-34l7Q-6I[/video]

You will notice that over time there is “roll” with the joint, this is because it is all done in 1 basis. I already know how to fix that in the regular sense, but constraints on joints (when implemented) will eliminate it

4 Likes

@wezrule how about your IK solver ?? :slight_smile:

Are you going to put the code somewhere ?
It would be lovely to see it as a Control in the core since lots of animated models use the inverse kinematics.
And I would then use such Control in blender importer directly.

Hey, sorry I was busy the last few weeks moving, and also didn’t have internet, so I was enjoying that for a while :P. I haven’t looked at this since. I will clean up what I have now and make it a control (sometime on the weekend). I don’t have much time to extend this atm, but it will at least give you basic functionality.

OK that would be cool :slight_smile:

@Kaelthas here you go. Sorry I didn’t have time to make it as generic as you wanted (and very tired atm), but hopefully how I did it can at least help.

Once again:

Is what I modeled it against

TestIKControl.java
[java]package mygame;

import com.jme3.app.FlyCamAppState;
import com.jme3.app.SimpleApplication;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;

//
// @author wezrule
//
public class TestIKControl extends SimpleApplication {

private final int NUM_BONES = 4;     // How many bones in the chain?
private final int NUM_AFFECTED = 4;  // How many bones (starting from the end) are affected

public static void main(String[] args) {
    TestIKControl app = new TestIKControl();
    app.start();
}

public TestIKControl() {
    super(new FlyCamAppState());
}

@Override
public void simpleInitApp() {
    setupCamera();

    // Create rootBone placeholder node
    Node rootBone = new Node("RootBone");
    rootNode.attachChild(rootBone);

    Node lastBone = createBoneChain(rootBone);

    // Add IK control
    rootBone.addControl(new IKControl(lastBone, NUM_AFFECTED, this));
}

private void setupCamera() {
    flyCam.setMoveSpeed(10);
    setPauseOnLostFocus(false);
    flyCam.setDragToRotate(true);
    cam.setLocation(new Vector3f(0, 0, 20));
}

private Node createBoneChain(Node rootBone) {

    Node previousBone = rootBone;
    for (int i = 0; i &lt; NUM_BONES; ++i) {
        previousBone = addBone(previousBone);

        if (i &gt; 0) {
            previousBone.move(0, 1, 0);     // This should probably use the extents, but I know they are 1 unit tall
        }
    }

    return previousBone;
}

private Node addBone(Node previousBone) {
    Node bone = new Node("bone");
    previousBone.attachChild(bone);

    // Add the graphical bone
    Spatial boneSpatial = assetManager.loadModel("Models/bone.j3o");
    Material m = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
    m.setColor("Color", ColorRGBA.randomColor());
    boneSpatial.setMaterial(m);
    bone.attachChild(boneSpatial);

    return bone;
}

}[/java]

IKControl.java
[java]
package mygame;

import com.jme3.app.SimpleApplication;
import com.jme3.input.InputManager;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Node;
import com.jme3.scene.control.AbstractControl;

//
// @author wezrule
//
public class IKControl extends AbstractControl {

private Node lastBone;
private SimpleApplication myApp;
private int numBonesAffected;

public IKControl(Node lastBone, int numBonesAffected, SimpleApplication app) {
    this.lastBone = lastBone;
    this.myApp = app;
    this.numBonesAffected = numBonesAffected;
}

@Override
protected void controlUpdate(float tpf) {

    Camera cam = myApp.getCamera();
    InputManager inputManager = myApp.getInputManager();

    float viewToProjectionZ = cam.getViewToProjectionZ(20); // Project 20 units in front of the camera
    Vector3f mousePos = cam.getWorldCoordinates(inputManager.getCursorPosition(), viewToProjectionZ);

    int iterations = 0;
    Node parent = lastBone;
    while (parent != spatial &amp;&amp; iterations &lt; numBonesAffected) {    // spatial is the root bone
        Vector3f e = getEffectorPosition();                // effector
        Vector3f j = parent.getWorldTranslation().clone(); // current join position
        Vector3f t = mousePos; //target

        Vector3f currentDir = e.subtract(j).normalizeLocal();
        Vector3f target = t.subtract(j).normalizeLocal();

        float angle = currentDir.angleBetween(target);
        Vector3f cross = currentDir.cross(target);

        Quaternion q = new Quaternion().fromAngleAxis(angle, cross);
        Quaternion targetWorld = q.mult(parent.getParent().getWorldRotation());
        Quaternion diff = parent.getParent().getWorldRotation().inverse().mult(targetWorld);
        parent.setLocalRotation(diff.mult(parent.getLocalRotation()));

        parent = parent.getParent();
        ++iterations;
    }
}

private Vector3f getEffectorPosition() {
    // Get the last one
    return lastBone.getWorldTranslation().add(lastBone.getWorldRotation().mult(Vector3f.UNIT_Y));
}

@Override
protected void controlRender(RenderManager rm, ViewPort vp) {
}

}
[/java]

// Bone blend file
https://dl.dropboxusercontent.com/u/53339759/bone.blend

2 Likes

Lovely :slight_smile:
I will take a look into it and try to use it in the importer.

Thanks a lot :wink: