/*******************************************************************************
* Copyright 2011 See AUTHORS file.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.badlogic.gdx.tests;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.tests.utils.GdxTest;
public class InverseKinematicsTest extends GdxTest {
static class Bone {
final float len;
final Vector3 position = new Vector3();
final Vector3 inertia = new Vector3();
public String name;
public Bone (String name, float x, float y, float len) {
this.name = name;
this.position.set(x, y, 0);
this.len = len;
}
public String toString () {
return "bone " + name + ": " + position + ", " + len;
}
}
static final float GRAVITY = 0;
OrthographicCamera camera;
ShapeRenderer renderer;
Bone[] bones;
Vector3 globalCoords = new Vector3();
Vector3 endPoint = new Vector3();
Vector2 diff = new Vector2();
@Override
public void create () {
float aspect = Gdx.graphics.getWidth() / (float)Gdx.graphics.getHeight();
camera = new OrthographicCamera(15 * aspect, 15);
camera.update();
renderer = new ShapeRenderer();
renderer.setProjectionMatrix(camera.combined);
bones = new Bone[] {new Bone("bone0", 0, 0, 0), new Bone("bone1", 0, 2, 2), new Bone("bone2", 0, 4, 2),
new Bone("bone3", 0, 6, 2), new Bone("end", 0, 8, 2)};
globalCoords.set(bones[0].position);
}
@Override
public void dispose () {
renderer.dispose();
}
@Override
public void render () {
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
camera.update();
renderer.setProjectionMatrix(camera.combined);
if (Gdx.input.isTouched()) camera.unproject(globalCoords.set(Gdx.input.getX(), Gdx.input.getY(), 0));
solveFakeIK(globalCoords);
renderBones();
}
private void renderBones () {
renderer.begin(ShapeType.Line);
renderer.setColor(0, 1, 0, 1);
for (int i = 0; i < bones.length - 1; i++) {
renderer.line(bones[i].position.x, bones[i].position.y, bones[i + 1].position.x, bones[i + 1].position.y);
}
renderer.end();
renderer.begin(ShapeType.Point);
renderer.setColor(1, 0, 0, 1);
for (int i = 0; i < bones.length; i++) {
renderer.point(bones[i].position.x, bones[i].position.y, 0);
}
renderer.end();
}
public void solveFakeIK (Vector3 target) {
float gravity = Gdx.graphics.getDeltaTime() * GRAVITY;
endPoint.set(target);
bones[0].position.set(endPoint);
for (int i = 0; i < bones.length - 1; i++) {
Bone bone = bones[i];
endPoint.set(bone.position);
diff.set(endPoint.x, endPoint.y).sub(bones[i + 1].position.x, bones[i + 1].position.y);
diff.add(0, gravity);
diff.add(bones[i + 1].inertia.x, bones[i + 1].inertia.y);
diff.nor().scl(bones[i + 1].len);
float x = endPoint.x - diff.x;
float y = endPoint.y - diff.y;
float delta = Gdx.graphics.getDeltaTime();
bones[i + 1].inertia.add((bones[i + 1].position.x - x) * delta, (bones[i + 1].position.y - y) * delta, 0).scl(0.99f);
bones[i + 1].position.set(x, y, 0);
}
}
}