@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 < NUM_BONES; ++i) {
previousBone = addBone(previousBone);
if (i > 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 && iterations < 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