Browse Source

revise TestBoneRagdoll & TestRagdollCharacter to use DynamicAnimControl

Stephen Gold 6 năm trước cách đây
mục cha
commit
d55776d081

+ 154 - 260
jme3-examples/src/main/java/jme3test/bullet/TestBoneRagdoll.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2019 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -31,43 +31,51 @@
  */
 package jme3test.bullet;
 
-import com.jme3.animation.*;
+import com.jme3.anim.AnimComposer;
+import com.jme3.anim.tween.Tweens;
+import com.jme3.anim.tween.action.Action;
 import com.jme3.app.SimpleApplication;
 import com.jme3.asset.TextureKey;
 import com.jme3.bullet.BulletAppState;
 import com.jme3.bullet.PhysicsSpace;
-import com.jme3.bullet.collision.*;
+import com.jme3.bullet.animation.DynamicAnimControl;
+import com.jme3.bullet.animation.PhysicsLink;
+import com.jme3.bullet.animation.RagdollCollisionListener;
+import com.jme3.bullet.collision.PhysicsCollisionEvent;
+import com.jme3.bullet.collision.PhysicsCollisionObject;
 import com.jme3.bullet.collision.shapes.SphereCollisionShape;
-import com.jme3.bullet.control.KinematicRagdollControl;
 import com.jme3.bullet.control.RigidBodyControl;
 import com.jme3.font.BitmapText;
 import com.jme3.input.KeyInput;
 import com.jme3.input.MouseInput;
-import com.jme3.input.controls.*;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.input.controls.MouseButtonTrigger;
 import com.jme3.light.DirectionalLight;
 import com.jme3.material.Material;
-import com.jme3.math.*;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Transform;
+import com.jme3.math.Vector3f;
 import com.jme3.scene.Geometry;
 import com.jme3.scene.Node;
-import com.jme3.scene.debug.SkeletonDebugger;
 import com.jme3.scene.shape.Sphere;
 import com.jme3.scene.shape.Sphere.TextureMode;
 import com.jme3.texture.Texture;
 
 /**
- * PHYSICS RAGDOLLS ARE NOT WORKING PROPERLY YET!
  * @author normenhansen
  */
-//TODO rework this Test when the new animation system is done.
-public class TestBoneRagdoll extends SimpleApplication implements RagdollCollisionListener, AnimEventListener {
-
-    private BulletAppState bulletAppState;
-    Material matBullet;
-    Node model;
-    KinematicRagdollControl ragdoll;
-    float bulletSize = 1f;
-    Material mat;
-    Material mat3;
+public class TestBoneRagdoll
+        extends SimpleApplication
+        implements ActionListener, RagdollCollisionListener {
+
+    private AnimComposer composer;
+    private DynamicAnimControl ragdoll;
+    private float bulletSize = 1f;
+    private Material matBullet;
+    private Node model;
+    private PhysicsSpace physicsSpace;
     private Sphere bullet;
     private SphereCollisionShape bulletCollisionShape;
 
@@ -76,276 +84,162 @@ public class TestBoneRagdoll extends SimpleApplication implements RagdollCollisi
         app.start();
     }
 
-    public void simpleInitApp() {
-        initCrossHairs();
-        initMaterial();
-
-        cam.setLocation(new Vector3f(0.26924422f, 6.646658f, 22.265987f));
-        cam.setRotation(new Quaternion(-2.302544E-4f, 0.99302495f, -0.117888905f, -0.0019395084f));
+    public void onStandDone() {
+        composer.setCurrentAction("IdleTop");
+    }
 
+    @Override
+    public void onAction(String name, boolean isPressed, float tpf) {
+        if (name.equals("boom") && !isPressed) {
+            Geometry bulletg = new Geometry("bullet", bullet);
+            bulletg.setMaterial(matBullet);
+            bulletg.setLocalTranslation(cam.getLocation());
+            bulletg.setLocalScale(bulletSize);
+            bulletCollisionShape = new SphereCollisionShape(bulletSize);
+            BombControl bulletNode = new BombControl(assetManager, bulletCollisionShape, 1f);
+            bulletNode.setForceFactor(8f);
+            bulletNode.setExplosionRadius(20f);
+            bulletNode.setCcdMotionThreshold(0.001f);
+            bulletNode.setLinearVelocity(cam.getDirection().mult(180f));
+            bulletg.addControl(bulletNode);
+            rootNode.attachChild(bulletg);
+            physicsSpace.add(bulletNode);
+        }
+        if (name.equals("bullet+") && isPressed) {
+            bulletSize += 0.1f;
+        }
+        if (name.equals("bullet-") && isPressed) {
+            bulletSize -= 0.1f;
+        }
+        if (name.equals("shoot") && !isPressed) {
+            Geometry bulletg = new Geometry("bullet", bullet);
+            bulletg.setMaterial(matBullet);
+            bulletg.setLocalTranslation(cam.getLocation());
+            bulletg.setLocalScale(bulletSize);
+            bulletCollisionShape = new SphereCollisionShape(bulletSize);
+            RigidBodyControl bulletNode = new RigidBodyControl(bulletCollisionShape, bulletSize * 10f);
+            bulletNode.setCcdMotionThreshold(0.001f);
+            bulletNode.setLinearVelocity(cam.getDirection().mult(80f));
+            bulletg.addControl(bulletNode);
+            rootNode.attachChild(bulletg);
+            physicsSpace.add(bulletNode);
+        }
+        if (name.equals("stop") && isPressed) {
+            ragdoll.setEnabled(!ragdoll.isEnabled());
+            ragdoll.setRagdollMode();
+        }
+        if (name.equals("toggle") && isPressed) {
+            Vector3f v = new Vector3f(model.getLocalTranslation());
+            v.y = 0f;
+            model.setLocalTranslation(v);
+            Quaternion q = new Quaternion();
+            float[] angles = new float[3];
+            model.getLocalRotation().toAngles(angles);
+            q.fromAngleAxis(angles[1], Vector3f.UNIT_Y);
+            Transform endModelTransform
+                    = new Transform(v, q, new Vector3f(1f, 1f, 1f));
+            if (angles[0] < 0f) {
+                composer.setCurrentAction("BackOnce");
+                ragdoll.blendToKinematicMode(0.5f, endModelTransform);
+            } else {
+                composer.setCurrentAction("FrontOnce");
+                ragdoll.blendToKinematicMode(0.5f, endModelTransform);
+            }
+        }
+    }
 
-        bulletAppState = new BulletAppState();
-        bulletAppState.setEnabled(true);
-        stateManager.attach(bulletAppState);
-        bullet = new Sphere(32, 32, 1.0f, true, false);
-        bullet.setTextureMode(TextureMode.Projected);
-        bulletCollisionShape = new SphereCollisionShape(1.0f);
+    @Override
+    public void simpleInitApp() {
+        flyCam.setMoveSpeed(50f);
+        cam.setLocation(new Vector3f(0.3f, 6.7f, 22.3f));
+        cam.setRotation(new Quaternion(-2E-4f, 0.993025f, -0.1179f, -0.0019f));
 
-//        bulletAppState.getPhysicsSpace().enableDebug(assetManager);
-        PhysicsTestHelper.createPhysicsTestWorld(rootNode, assetManager, bulletAppState.getPhysicsSpace());
+        initCrossHairs();
+        initMaterial();
+        setupKeys();
         setupLight();
 
-        model = (Node) assetManager.loadModel("Models/Sinbad/SinbadOldAnim.j3o");
-
-        //  model.setLocalRotation(new Quaternion().fromAngleAxis(FastMath.HALF_PI, Vector3f.UNIT_X));
-
-        //debug view
-        AnimControl control = model.getControl(AnimControl.class);
-        SkeletonDebugger skeletonDebug = new SkeletonDebugger("skeleton", control.getSkeleton());
-        Material mat2 = new Material(getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
-        mat2.getAdditionalRenderState().setWireframe(true);
-        mat2.setColor("Color", ColorRGBA.Green);
-        mat2.getAdditionalRenderState().setDepthTest(false);
-        skeletonDebug.setMaterial(mat2);
-        skeletonDebug.setLocalTranslation(model.getLocalTranslation());
-
-        //Note: PhysicsRagdollControl is still TODO, constructor will change
-        ragdoll = new KinematicRagdollControl(0.5f);
-        setupSinbad(ragdoll);
-        ragdoll.addCollisionListener(this);
-        model.addControl(ragdoll);
-
-        float eighth_pi = FastMath.PI * 0.125f;
-        ragdoll.setJointLimit("Waist", eighth_pi, eighth_pi, eighth_pi, eighth_pi, eighth_pi, eighth_pi);
-        ragdoll.setJointLimit("Chest", eighth_pi, eighth_pi, 0, 0, eighth_pi, eighth_pi);
-
+        BulletAppState bulletAppState = new BulletAppState();
+        stateManager.attach(bulletAppState);
+        //bulletAppState.setDebugEnabled(true);
+        physicsSpace = bulletAppState.getPhysicsSpace();
 
-        //Oto's head is almost rigid
-        //    ragdoll.setJointLimit("head", 0, 0, eighth_pi, -eighth_pi, 0, 0);
+        bullet = new Sphere(32, 32, 1f, true, false);
+        bullet.setTextureMode(TextureMode.Projected);
+        bulletCollisionShape = new SphereCollisionShape(1f);
 
-        getPhysicsSpace().add(ragdoll);
-        speed = 1.3f;
+        PhysicsTestHelper.createPhysicsTestWorld(rootNode, assetManager,
+                physicsSpace);
 
+        model = (Node) assetManager.loadModel("Models/Sinbad/Sinbad.mesh.xml");
         rootNode.attachChild(model);
-        // rootNode.attachChild(skeletonDebug);
-        flyCam.setMoveSpeed(50);
-
-
-        animChannel = control.createChannel();
-        animChannel.setAnim("Dance");
-        control.addListener(this);
-
-        inputManager.addListener(new ActionListener() {
-
-            public void onAction(String name, boolean isPressed, float tpf) {
-                if (name.equals("toggle") && isPressed) {
-
-                    Vector3f v = new Vector3f();
-                    v.set(model.getLocalTranslation());
-                    v.y = 0;
-                    model.setLocalTranslation(v);
-                    Quaternion q = new Quaternion();
-                    float[] angles = new float[3];
-                    model.getLocalRotation().toAngles(angles);
-                    q.fromAngleAxis(angles[1], Vector3f.UNIT_Y);
-                    model.setLocalRotation(q);
-                    if (angles[0] < 0) {
-                        animChannel.setAnim("StandUpBack");
-                        ragdoll.blendToKinematicMode(0.5f);
-                    } else {
-                        animChannel.setAnim("StandUpFront");
-                        ragdoll.blendToKinematicMode(0.5f);
-                    }
 
-                }
-                if (name.equals("bullet+") && isPressed) {
-                    bulletSize += 0.1f;
-
-                }
-                if (name.equals("bullet-") && isPressed) {
-                    bulletSize -= 0.1f;
-
-                }
-
-                if (name.equals("stop") && isPressed) {
-                    ragdoll.setEnabled(!ragdoll.isEnabled());
-                    ragdoll.setRagdollMode();
-                }
-
-                if (name.equals("shoot") && !isPressed) {
-                    Geometry bulletg = new Geometry("bullet", bullet);
-                    bulletg.setMaterial(matBullet);
-                    bulletg.setLocalTranslation(cam.getLocation());
-                    bulletg.setLocalScale(bulletSize);
-                    bulletCollisionShape = new SphereCollisionShape(bulletSize);
-                    RigidBodyControl bulletNode = new RigidBodyControl(bulletCollisionShape, bulletSize * 10);
-                    bulletNode.setCcdMotionThreshold(0.001f);
-                    bulletNode.setLinearVelocity(cam.getDirection().mult(80));
-                    bulletg.addControl(bulletNode);
-                    rootNode.attachChild(bulletg);
-                    getPhysicsSpace().add(bulletNode);
-                }
-                if (name.equals("boom") && !isPressed) {
-                    Geometry bulletg = new Geometry("bullet", bullet);
-                    bulletg.setMaterial(matBullet);
-                    bulletg.setLocalTranslation(cam.getLocation());
-                    bulletg.setLocalScale(bulletSize);
-                    bulletCollisionShape = new SphereCollisionShape(bulletSize);
-                    BombControl bulletNode = new BombControl(assetManager, bulletCollisionShape, 1);
-                    bulletNode.setForceFactor(8);
-                    bulletNode.setExplosionRadius(20);
-                    bulletNode.setCcdMotionThreshold(0.001f);
-                    bulletNode.setLinearVelocity(cam.getDirection().mult(180));
-                    bulletg.addControl(bulletNode);
-                    rootNode.attachChild(bulletg);
-                    getPhysicsSpace().add(bulletNode);
-                }
-            }
-        }, "toggle", "shoot", "stop", "bullet+", "bullet-", "boom");
-        inputManager.addMapping("toggle", new KeyTrigger(KeyInput.KEY_SPACE));
-        inputManager.addMapping("shoot", new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
-        inputManager.addMapping("boom", new MouseButtonTrigger(MouseInput.BUTTON_RIGHT));
-        inputManager.addMapping("stop", new KeyTrigger(KeyInput.KEY_H));
-        inputManager.addMapping("bullet-", new KeyTrigger(KeyInput.KEY_COMMA));
-        inputManager.addMapping("bullet+", new KeyTrigger(KeyInput.KEY_PERIOD));
+        composer = model.getControl(AnimComposer.class);
+        composer.setCurrentAction("Dance");
 
+        Action standUpFront = composer.action("StandUpFront");
+        composer.actionSequence("FrontOnce",
+                standUpFront, Tweens.callMethod(this, "onStandDone"));
+        Action standUpBack = composer.action("StandUpBack");
+        composer.actionSequence("BackOnce",
+                standUpBack, Tweens.callMethod(this, "onStandDone"));
 
+        ragdoll = new DynamicAnimControl();
+        TestRagdollCharacter.setupSinbad(ragdoll);
+        model.addControl(ragdoll);
+        physicsSpace.add(ragdoll);
+        ragdoll.addCollisionListener(this);
     }
 
-    private void setupLight() {
-        // AmbientLight al = new AmbientLight();
-        //  al.setColor(ColorRGBA.White.mult(1));
-        //   rootNode.addLight(al);
-
-        DirectionalLight dl = new DirectionalLight();
-        dl.setDirection(new Vector3f(-0.1f, -0.7f, -1).normalizeLocal());
-        dl.setColor(new ColorRGBA(1f, 1f, 1f, 1.0f));
-        rootNode.addLight(dl);
-    }
-
-    private PhysicsSpace getPhysicsSpace() {
-        return bulletAppState.getPhysicsSpace();
-    }
-
-    public void initMaterial() {
-
-        matBullet = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
-        TextureKey key2 = new TextureKey("Textures/Terrain/Rock/Rock.PNG");
-        key2.setGenerateMips(true);
-        Texture tex2 = assetManager.loadTexture(key2);
-        matBullet.setTexture("ColorMap", tex2);
+    @Override
+    public void collide(PhysicsLink bone, PhysicsCollisionObject object,
+            PhysicsCollisionEvent event) {
+        if (object.getUserObject() != null
+                && object.getUserObject() instanceof Geometry) {
+            Geometry geom = (Geometry) object.getUserObject();
+            if ("bullet".equals(geom.getName())) {
+                ragdoll.setRagdollMode();
+            }
+        }
     }
 
-    protected void initCrossHairs() {
+    private void initCrossHairs() {
         guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
         BitmapText ch = new BitmapText(guiFont, false);
-        ch.setSize(guiFont.getCharSet().getRenderedSize() * 2);
+        ch.setSize(guiFont.getCharSet().getRenderedSize() * 2f);
         ch.setText("+"); // crosshairs
         ch.setLocalTranslation( // center
-                settings.getWidth() / 2 - guiFont.getCharSet().getRenderedSize() / 3 * 2,
-                settings.getHeight() / 2 + ch.getLineHeight() / 2, 0);
+                settings.getWidth() / 2f - guiFont.getCharSet().getRenderedSize() / 3f * 2f,
+                settings.getHeight() / 2f + ch.getLineHeight() / 2f, 0f);
         guiNode.attachChild(ch);
     }
 
-    public void collide(Bone bone, PhysicsCollisionObject object, PhysicsCollisionEvent event) {
-
-        if (object.getUserObject() != null && object.getUserObject() instanceof Geometry) {
-            Geometry geom = (Geometry) object.getUserObject();
-            if ("Floor".equals(geom.getName())) {
-                return;
-            }
-        }
-
-        ragdoll.setRagdollMode();
-
-    }
-
-    private void setupSinbad(KinematicRagdollControl ragdoll) {
-        ragdoll.addBoneName("Ulna.L");
-        ragdoll.addBoneName("Ulna.R");
-        ragdoll.addBoneName("Chest");
-        ragdoll.addBoneName("Foot.L");
-        ragdoll.addBoneName("Foot.R");
-        ragdoll.addBoneName("Hand.R");
-        ragdoll.addBoneName("Hand.L");
-        ragdoll.addBoneName("Neck");
-        ragdoll.addBoneName("Root");
-        ragdoll.addBoneName("Stomach");
-        ragdoll.addBoneName("Waist");
-        ragdoll.addBoneName("Humerus.L");
-        ragdoll.addBoneName("Humerus.R");
-        ragdoll.addBoneName("Thigh.L");
-        ragdoll.addBoneName("Thigh.R");
-        ragdoll.addBoneName("Calf.L");
-        ragdoll.addBoneName("Calf.R");
-        ragdoll.addBoneName("Clavicle.L");
-        ragdoll.addBoneName("Clavicle.R");
-
-    }
-    float elTime = 0;
-    boolean forward = true;
-    AnimControl animControl;
-    AnimChannel animChannel;
-    Vector3f direction = new Vector3f(0, 0, 1);
-    Quaternion rotate = new Quaternion().fromAngleAxis(FastMath.PI / 8, Vector3f.UNIT_Y);
-    boolean dance = true;
-
-    @Override
-    public void simpleUpdate(float tpf) {
-        // System.out.println(((BoundingBox) model.getWorldBound()).getYExtent());
-//        elTime += tpf;
-//        if (elTime > 3) {
-//            elTime = 0;
-//            if (dance) {
-//                rotate.multLocal(direction);
-//            }
-//            if (Math.random() > 0.80) {
-//                dance = true;
-//                animChannel.setAnim("Dance");
-//            } else {
-//                dance = false;
-//                animChannel.setAnim("RunBase");
-//                rotate.fromAngleAxis(FastMath.QUARTER_PI * ((float) Math.random() - 0.5f), Vector3f.UNIT_Y);
-//                rotate.multLocal(direction);
-//            }
-//        }
-//        if (!ragdoll.hasControl() && !dance) {
-//            if (model.getLocalTranslation().getZ() < -10) {
-//                direction.z = 1;
-//                direction.normalizeLocal();
-//            } else if (model.getLocalTranslation().getZ() > 10) {
-//                direction.z = -1;
-//                direction.normalizeLocal();
-//            }
-//            if (model.getLocalTranslation().getX() < -10) {
-//                direction.x = 1;
-//                direction.normalizeLocal();
-//            } else if (model.getLocalTranslation().getX() > 10) {
-//                direction.x = -1;
-//                direction.normalizeLocal();
-//            }
-//            model.move(direction.multLocal(tpf * 8));
-//            direction.normalizeLocal();
-//            model.lookAt(model.getLocalTranslation().add(direction), Vector3f.UNIT_Y);
-//        }
+    private void initMaterial() {
+        matBullet = new Material(assetManager,
+                "Common/MatDefs/Misc/Unshaded.j3md");
+        TextureKey key2 = new TextureKey("Textures/Terrain/Rock/Rock.PNG");
+        key2.setGenerateMips(true);
+        Texture tex2 = assetManager.loadTexture(key2);
+        matBullet.setTexture("ColorMap", tex2);
     }
 
-    public void onAnimCycleDone(AnimControl control, AnimChannel channel, String animName) {
-//        if(channel.getAnimationName().equals("StandUpFront")){
-//            channel.setAnim("Dance");
-//        }
+    private void setupKeys() {
+        inputManager.addMapping("boom", new MouseButtonTrigger(MouseInput.BUTTON_RIGHT));
+        inputManager.addMapping("bullet+", new KeyTrigger(KeyInput.KEY_PERIOD));
+        inputManager.addMapping("bullet-", new KeyTrigger(KeyInput.KEY_COMMA));
+        inputManager.addMapping("shoot", new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
+        inputManager.addMapping("stop", new KeyTrigger(KeyInput.KEY_H));
+        inputManager.addMapping("toggle", new KeyTrigger(KeyInput.KEY_SPACE));
 
-        if (channel.getAnimationName().equals("StandUpBack") || channel.getAnimationName().equals("StandUpFront")) {
-            channel.setLoopMode(LoopMode.DontLoop);
-            channel.setAnim("IdleTop", 5);
-            channel.setLoopMode(LoopMode.Loop);
-        }
-//        if(channel.getAnimationName().equals("IdleTop")){
-//            channel.setAnim("StandUpFront");
-//        }
+        inputManager.addListener(this,
+                "boom", "bullet-", "bullet+", "shoot", "stop", "toggle");
 
     }
 
-    public void onAnimChange(AnimControl control, AnimChannel channel, String animName) {
+    private void setupLight() {
+        DirectionalLight dl = new DirectionalLight();
+        dl.setDirection(new Vector3f(-0.1f, -0.7f, -1f).normalizeLocal());
+        dl.setColor(new ColorRGBA(1f, 1f, 1f, 1f));
+        rootNode.addLight(dl);
     }
 }

+ 147 - 126
jme3-examples/src/main/java/jme3test/bullet/TestRagdollCharacter.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2019 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -31,19 +31,25 @@
  */
 package jme3test.bullet;
 
-import com.jme3.animation.*;
+import com.jme3.anim.AnimComposer;
+import com.jme3.anim.tween.Tweens;
+import com.jme3.anim.tween.action.Action;
 import com.jme3.app.SimpleApplication;
 import com.jme3.asset.TextureKey;
 import com.jme3.bullet.BulletAppState;
 import com.jme3.bullet.PhysicsSpace;
-import com.jme3.bullet.control.KinematicRagdollControl;
+import com.jme3.bullet.animation.DynamicAnimControl;
+import com.jme3.bullet.animation.RangeOfMotion;
 import com.jme3.bullet.control.RigidBodyControl;
 import com.jme3.input.KeyInput;
 import com.jme3.input.controls.ActionListener;
 import com.jme3.input.controls.KeyTrigger;
 import com.jme3.light.DirectionalLight;
 import com.jme3.material.Material;
-import com.jme3.math.*;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
 import com.jme3.renderer.queue.RenderQueue.ShadowMode;
 import com.jme3.scene.Geometry;
 import com.jme3.scene.Node;
@@ -53,101 +59,153 @@ import com.jme3.texture.Texture;
 /**
  * @author normenhansen
  */
-//TODO rework this Test when the new animation system is done.
-public class TestRagdollCharacter extends SimpleApplication implements AnimEventListener, ActionListener {
+public class TestRagdollCharacter
+        extends SimpleApplication
+        implements ActionListener {
 
-    BulletAppState bulletAppState;
-    Node model;
-    KinematicRagdollControl ragdoll;
-    boolean leftStrafe = false, rightStrafe = false, forward = false, backward = false,
+    private AnimComposer composer;
+    private boolean forward = false, backward = false,
             leftRotate = false, rightRotate = false;
-    AnimControl animControl;
-    AnimChannel animChannel;
+    private DynamicAnimControl ragdoll;
+    private Node model;
+    private PhysicsSpace physicsSpace;
 
     public static void main(String[] args) {
         TestRagdollCharacter app = new TestRagdollCharacter();
         app.start();
     }
 
-    public void simpleInitApp() {
-        setupKeys();
-
-        bulletAppState = new BulletAppState();
-        bulletAppState.setEnabled(true);
-        stateManager.attach(bulletAppState);
+    public void onSliceDone() {
+        composer.setCurrentAction("IdleTop");
+    }
 
+    static void setupSinbad(DynamicAnimControl ragdoll) {
+        ragdoll.link("Waist", 1f,
+                new RangeOfMotion(1f, -0.4f, 0.8f, -0.8f, 0.4f, -0.4f));
+        ragdoll.link("Chest", 1f, new RangeOfMotion(0.4f, 0f, 0.4f));
+        ragdoll.link("Neck", 1f, new RangeOfMotion(0.5f, 1f, 0.7f));
+
+        ragdoll.link("Clavicle.R", 1f,
+                new RangeOfMotion(0.3f, -0.6f, 0f, 0f, 0.4f, -0.4f));
+        ragdoll.link("Humerus.R", 1f,
+                new RangeOfMotion(1.6f, -0.8f, 1f, -1f, 1.6f, -1f));
+        ragdoll.link("Ulna.R", 1f, new RangeOfMotion(0f, 0f, 1f, -1f, 0f, -2f));
+        ragdoll.link("Hand.R", 1f, new RangeOfMotion(0.8f, 0f, 0.2f));
+
+        ragdoll.link("Clavicle.L", 1f,
+                new RangeOfMotion(0.6f, -0.3f, 0f, 0f, 0.4f, -0.4f));
+        ragdoll.link("Humerus.L",
+                1f, new RangeOfMotion(0.8f, -1.6f, 1f, -1f, 1f, -1.6f));
+        ragdoll.link("Ulna.L", 1f, new RangeOfMotion(0f, 0f, 1f, -1f, 2f, 0f));
+        ragdoll.link("Hand.L", 1f, new RangeOfMotion(0.8f, 0f, 0.2f));
+
+        ragdoll.link("Thigh.R", 1f,
+                new RangeOfMotion(0.4f, -1f, 0.4f, -0.4f, 1f, -0.5f));
+        ragdoll.link("Calf.R", 1f, new RangeOfMotion(2f, 0f, 0f, 0f, 0f, 0f));
+        ragdoll.link("Foot.R", 1f, new RangeOfMotion(0.3f, 0.5f, 0f));
+
+        ragdoll.link("Thigh.L", 1f,
+                new RangeOfMotion(0.4f, -1f, 0.4f, -0.4f, 0.5f, -1f));
+        ragdoll.link("Calf.L", 1f, new RangeOfMotion(2f, 0f, 0f, 0f, 0f, 0f));
+        ragdoll.link("Foot.L", 1f, new RangeOfMotion(0.3f, 0.5f, 0f));
+    }
 
-//        bulletAppState.getPhysicsSpace().enableDebug(assetManager);
-        PhysicsTestHelper.createPhysicsTestWorld(rootNode, assetManager, bulletAppState.getPhysicsSpace());
-        initWall(2,1,1);
-        setupLight();
+    @Override
+    public void onAction(String binding, boolean isPressed, float tpf) {
+        if (binding.equals("Rotate Left")) {
+            if (isPressed) {
+                leftRotate = true;
+            } else {
+                leftRotate = false;
+            }
+        } else if (binding.equals("Rotate Right")) {
+            if (isPressed) {
+                rightRotate = true;
+            } else {
+                rightRotate = false;
+            }
+        } else if (binding.equals("Slice")) {
+            if (isPressed) {
+                composer.setCurrentAction("SliceOnce");
+            }
+        } else if (binding.equals("Walk Forward")) {
+            if (isPressed) {
+                forward = true;
+            } else {
+                forward = false;
+            }
+        } else if (binding.equals("Walk Backward")) {
+            if (isPressed) {
+                backward = true;
+            } else {
+                backward = false;
+            }
+        }
+    }
 
-        cam.setLocation(new Vector3f(-8,0,-4));
-        cam.lookAt(new Vector3f(4,0,-7), Vector3f.UNIT_Y);
+    @Override
+    public void simpleInitApp() {
+        flyCam.setMoveSpeed(50f);
+        cam.setLocation(new Vector3f(-16f, 4.7f, -1.6f));
+        cam.setRotation(new Quaternion(0.0484f, 0.804337f, -0.066f, 0.5885f));
 
-        model = (Node) assetManager.loadModel("Models/Sinbad/SinbadOldAnim.j3o");
-        model.lookAt(new Vector3f(0,0,-1), Vector3f.UNIT_Y);
-        model.setLocalTranslation(4, 0, -7f);
+        setupKeys();
+        setupLight();
 
-        ragdoll = new KinematicRagdollControl(0.5f);
-        model.addControl(ragdoll);
+        BulletAppState bulletAppState = new BulletAppState();
+        stateManager.attach(bulletAppState);
+        //bulletAppState.setDebugEnabled(true);
+        physicsSpace = bulletAppState.getPhysicsSpace();
 
-        getPhysicsSpace().add(ragdoll);
-        speed = 1.3f;
+        PhysicsTestHelper.createPhysicsTestWorld(rootNode, assetManager,
+                physicsSpace);
+        initWall(2f, 1f, 1f);
 
+        model = (Node) assetManager.loadModel("Models/Sinbad/Sinbad.mesh.xml");
         rootNode.attachChild(model);
+        model.lookAt(new Vector3f(0f, 0f, -1f), Vector3f.UNIT_Y);
+        model.setLocalTranslation(4f, 0f, -7f);
 
+        composer = model.getControl(AnimComposer.class);
+        composer.setCurrentAction("IdleTop");
 
-        AnimControl control = model.getControl(AnimControl.class);
-        animChannel = control.createChannel();
-        animChannel.setAnim("IdleTop");
-        control.addListener(this);
+        Action slice = composer.action("SliceHorizontal");
+        composer.actionSequence("SliceOnce",
+                slice, Tweens.callMethod(this, "onSliceDone"));
 
+        ragdoll = new DynamicAnimControl();
+        setupSinbad(ragdoll);
+        model.addControl(ragdoll);
+        physicsSpace.add(ragdoll);
     }
 
-    private void setupLight() {
-        DirectionalLight dl = new DirectionalLight();
-        dl.setDirection(new Vector3f(-0.1f, -0.7f, -1).normalizeLocal());
-        dl.setColor(new ColorRGBA(1f, 1f, 1f, 1.0f));
-        rootNode.addLight(dl);
-    }
-
-    private PhysicsSpace getPhysicsSpace() {
-        return bulletAppState.getPhysicsSpace();
-    }
-
-    private void setupKeys() {
-        inputManager.addMapping("Rotate Left",
-                new KeyTrigger(KeyInput.KEY_H));
-        inputManager.addMapping("Rotate Right",
-                new KeyTrigger(KeyInput.KEY_K));
-        inputManager.addMapping("Walk Forward",
-                new KeyTrigger(KeyInput.KEY_U));
-        inputManager.addMapping("Walk Backward",
-                new KeyTrigger(KeyInput.KEY_J));
-        inputManager.addMapping("Slice",
-                new KeyTrigger(KeyInput.KEY_SPACE),
-                new KeyTrigger(KeyInput.KEY_RETURN));
-        inputManager.addListener(this, "Strafe Left", "Strafe Right");
-        inputManager.addListener(this, "Rotate Left", "Rotate Right");
-        inputManager.addListener(this, "Walk Forward", "Walk Backward");
-        inputManager.addListener(this, "Slice");
+    @Override
+    public void simpleUpdate(float tpf) {
+        if (forward) {
+            model.move(model.getLocalRotation().multLocal(new Vector3f(0f, 0f, tpf)));
+        } else if (backward) {
+            model.move(model.getLocalRotation().multLocal(new Vector3f(0f, 0f, -tpf)));
+        } else if (leftRotate) {
+            model.rotate(0f, tpf, 0f);
+        } else if (rightRotate) {
+            model.rotate(0f, -tpf, 0f);
+        }
     }
 
-    public void initWall(float bLength, float bWidth, float bHeight) {
+    private void initWall(float bLength, float bWidth, float bHeight) {
         Box brick = new Box(bLength, bHeight, bWidth);
-        brick.scaleTextureCoordinates(new Vector2f(1f, .5f));
+        brick.scaleTextureCoordinates(new Vector2f(1f, 0.5f));
         Material mat2 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
         TextureKey key = new TextureKey("Textures/Terrain/BrickWall/BrickWall.jpg");
         key.setGenerateMips(true);
         Texture tex = assetManager.loadTexture(key);
         mat2.setTexture("ColorMap", tex);
-        
-        float startpt = bLength / 4;
-        float height = -5;
+
+        float startpt = bLength / 4f;
+        float height = -5f;
         for (int j = 0; j < 15; j++) {
             for (int i = 0; i < 4; i++) {
-                Vector3f ori = new Vector3f(i * bLength * 2 + startpt, bHeight + height, -10);
+                Vector3f ori = new Vector3f(i * bLength * 2f + startpt, bHeight + height, -10f);
                 Geometry reBoxg = new Geometry("brick", brick);
                 reBoxg.setMaterial(mat2);
                 reBoxg.setLocalTranslation(ori);
@@ -156,71 +214,34 @@ public class TestRagdollCharacter extends SimpleApplication implements AnimEvent
                 reBoxg.setShadowMode(ShadowMode.CastAndReceive);
                 reBoxg.getControl(RigidBodyControl.class).setFriction(0.6f);
                 this.rootNode.attachChild(reBoxg);
-                this.getPhysicsSpace().add(reBoxg);
+                physicsSpace.add(reBoxg);
             }
             startpt = -startpt;
-            height += 2 * bHeight;
+            height += 2f * bHeight;
         }
     }
 
-    public void onAnimCycleDone(AnimControl control, AnimChannel channel, String animName) {
-
-        if (channel.getAnimationName().equals("SliceHorizontal")) {
-            channel.setLoopMode(LoopMode.DontLoop);
-            channel.setAnim("IdleTop", 5);
-            channel.setLoopMode(LoopMode.Loop);
-        }
-
-    }
+    private void setupKeys() {
+        inputManager.addMapping("Rotate Left",
+                new KeyTrigger(KeyInput.KEY_H));
+        inputManager.addMapping("Rotate Right",
+                new KeyTrigger(KeyInput.KEY_K));
+        inputManager.addMapping("Walk Backward",
+                new KeyTrigger(KeyInput.KEY_J));
+        inputManager.addMapping("Walk Forward",
+                new KeyTrigger(KeyInput.KEY_U));
+        inputManager.addMapping("Slice",
+                new KeyTrigger(KeyInput.KEY_SPACE),
+                new KeyTrigger(KeyInput.KEY_RETURN));
 
-    public void onAnimChange(AnimControl control, AnimChannel channel, String animName) {
-    }
-    
-    public void onAction(String binding, boolean value, float tpf) {
-        if (binding.equals("Rotate Left")) {
-            if (value) {
-                leftRotate = true;
-            } else {
-                leftRotate = false;
-            }
-        } else if (binding.equals("Rotate Right")) {
-            if (value) {
-                rightRotate = true;
-            } else {
-                rightRotate = false;
-            }
-        } else if (binding.equals("Walk Forward")) {
-            if (value) {
-                forward = true;
-            } else {
-                forward = false;
-            }
-        } else if (binding.equals("Walk Backward")) {
-            if (value) {
-                backward = true;
-            } else {
-                backward = false;
-            }
-        } else if (binding.equals("Slice")) {
-            if (value) {
-                animChannel.setAnim("SliceHorizontal");
-                animChannel.setSpeed(0.3f);
-            }
-        }
+        inputManager.addListener(this, "Rotate Left", "Rotate Right", "Slice",
+                "Walk Backward", "Walk Forward");
     }
 
-    @Override
-    public void simpleUpdate(float tpf) {
-        if(forward){
-            model.move(model.getLocalRotation().multLocal(new Vector3f(0,0,1)).multLocal(tpf));
-        }else if(backward){
-            model.move(model.getLocalRotation().multLocal(new Vector3f(0,0,1)).multLocal(-tpf));
-        }else if(leftRotate){
-            model.rotate(0, tpf, 0);
-        }else if(rightRotate){
-            model.rotate(0, -tpf, 0);
-        }
-        fpsText.setText(cam.getLocation() + "/" + cam.getRotation());
+    private void setupLight() {
+        DirectionalLight dl = new DirectionalLight();
+        dl.setDirection(new Vector3f(-0.1f, -0.7f, -1f).normalizeLocal());
+        dl.setColor(new ColorRGBA(1f, 1f, 1f, 1f));
+        rootNode.addLight(dl);
     }
-
 }