{
  "$type": "site.standard.document",
  "bskyPostRef": {
    "cid": "bafyreicwcribpohud26fgwzjctl56axrf3ifnbqy5qeziuj5vuvcfu63qi",
    "uri": "at://did:plc:dxjzgxe7cvirxkwfjr2tjspt/app.bsky.feed.post/3mnc2ujw7gzp2"
  },
  "path": "/t/mixing-animations-with-ragdoll-behaviour/49598#post_7",
  "publishedAt": "2026-06-02T05:36:18.000Z",
  "site": "https://hub.jmonkeyengine.org",
  "tags": [
    "@Override"
  ],
  "textContent": "Thanks. I got a little stuck with the first part of setting up the ragdoll. I’ve created a test to toggle between standing and dropping it in ragdoll mode. But when it stands up after ragdoll mode the model is orientated totally wrong, not with his head up facing towards the camera.\n\nI tried to isolate the relevant code in a single file:\npackage com.beer;\n\n\n    import com.jme3.anim.Armature;\n    import com.jme3.anim.SkinningControl;\n    import com.jme3.app.SimpleApplication;\n    import com.jme3.asset.plugins.FileLocator;\n    import com.jme3.bullet.BulletAppState;\n    import com.jme3.bullet.animation.DynamicAnimControl;\n    import com.jme3.bullet.animation.RangeOfMotion;\n    import com.jme3.bullet.collision.shapes.BoxCollisionShape;\n    import com.jme3.bullet.control.RigidBodyControl;\n    import com.jme3.input.KeyInput;\n    import com.jme3.input.controls.ActionListener;\n    import com.jme3.input.controls.KeyTrigger;\n    import com.jme3.light.AmbientLight;\n    import com.jme3.light.DirectionalLight;\n    import com.jme3.material.Material;\n    import com.jme3.math.ColorRGBA;\n    import com.jme3.math.Quaternion;\n    import com.jme3.math.Vector3f;\n    import com.jme3.scene.Geometry;\n    import com.jme3.scene.Node;\n    import com.jme3.scene.Spatial;\n    import com.jme3.scene.shape.Box;\n    import com.jme3.system.AppSettings;\n\n    import java.io.File;\n\n    public class RagdollStandingTest extends SimpleApplication {\n\n        public static void main(String[] args) {\n            RagdollStandingTest app = new RagdollStandingTest();\n            AppSettings settings = new AppSettings(true);\n            settings.setRenderer(AppSettings.LWJGL_OPENGL33);\n            settings.setResolution(1280, 720);\n            settings.setTitle(\"Ragdoll Standing Test\");\n            app.setSettings(settings);\n            app.setShowSettings(false);\n            app.start();\n        }\n\n        private BulletAppState bulletAppState;\n        private DynamicAnimControl ragdoll;\n        private boolean isRagdoll = false;\n        private Node model;\n        private Armature armature;\n\n        @Override\n        public void simpleInitApp() {\n            // IMPORTANT: Update this path to where your YBot.glb is located\n            assetManager.registerLocator(\n                    new File(\"assets\").getAbsolutePath() + \"/\", FileLocator.class);\n\n            // Setup physics\n            bulletAppState = new BulletAppState();\n            stateManager.attach(bulletAppState);\n\n            // Setup scene\n            setupLights();\n            setupFloor();\n            setupCamera();\n\n            // Load character (Mixamo YBot with 100% armature scale)\n            model = (Node) assetManager.loadModel(\"Models/YBot.glb\");\n            model.setLocalTranslation(0f, 0f, 0f);\n            rootNode.attachChild(model);\n\n            rootNode.updateGeometricState();\n\n            // Find skinning control and armature\n            Spatial spatial = findSpatialWithSkinning(model);\n            SkinningControl skinning = spatial.getControl(SkinningControl.class);\n            skinning.setHardwareSkinningPreferred(false);\n            armature = skinning.getArmature();\n            armature.update();\n\n            // Create DynamicAnimControl\n            ragdoll = new DynamicAnimControl();\n\n            // Basic RangeOfMotion for all joints\n            RangeOfMotion rom = new RangeOfMotion(1.0f, 0.01f, 0.8f);\n\n            // Link spine and limbs\n            ragdoll.link(\"mixamorig:Spine\", 1f, rom);\n            ragdoll.link(\"mixamorig:Spine1\", 1f, rom);\n            ragdoll.link(\"mixamorig:Spine2\", 1f, rom);\n            ragdoll.link(\"mixamorig:Neck\", 0.5f, rom);\n            ragdoll.link(\"mixamorig:Head\", 1f, rom);\n\n            ragdoll.link(\"mixamorig:LeftUpLeg\", 1.5f, rom);\n            ragdoll.link(\"mixamorig:LeftLeg\", 1.0f, rom);\n            ragdoll.link(\"mixamorig:RightUpLeg\", 1.5f, rom);\n            ragdoll.link(\"mixamorig:RightLeg\", 1.0f, rom);\n\n            ragdoll.link(\"mixamorig:LeftShoulder\", 0.5f, rom);\n            ragdoll.link(\"mixamorig:RightShoulder\", 0.5f, rom);\n            ragdoll.link(\"mixamorig:LeftArm\", 1f, rom);\n            ragdoll.link(\"mixamorig:LeftForeArm\", 1f, rom);\n            ragdoll.link(\"mixamorig:RightArm\", 1f, rom);\n            ragdoll.link(\"mixamorig:RightForeArm\", 1f, rom);\n\n            armature.update();\n\n            spatial.addControl(ragdoll);\n            bulletAppState.getPhysicsSpace().add(ragdoll);\n\n            // Start in kinematic mode (standing)\n            ragdoll.setKinematicMode();\n\n            // Setup keyboard controls\n            inputManager.addMapping(\"Toggle\", new KeyTrigger(KeyInput.KEY_SPACE));\n            inputManager.addListener(actionListener, \"Toggle\");\n\n            System.out.println(\"=== RAGDOLL STANDING TEST ===\");\n            System.out.println(\"Press SPACE to toggle between standing and ragdoll\");\n            System.out.println(\"Currently: STANDING (kinematic mode)\");\n        }\n\n        private final ActionListener actionListener = new ActionListener() {\n            @Override\n            public void onAction(String name, boolean isPressed, float tpf) {\n                if (name.equals(\"Toggle\") && !isPressed && ragdoll != null) {\n                    if (isRagdoll) {\n                        // Try to stand back up\n                        System.out.println(\"\\n--- Attempting to stand up ---\");\n\n                        // Reset skeleton to bind pose\n                        armature.applyBindPose();\n                        armature.update();\n\n                        // Switch to kinematic mode\n                        ragdoll.setKinematicMode();\n\n                        // Try to reset model position\n                        model.setLocalTranslation(Vector3f.ZERO);\n                        model.setLocalRotation(Quaternion.IDENTITY);\n\n                        // Try to reset torso physics\n                        var torso = ragdoll.getTorsoLink().getRigidBody();\n                        torso.setPhysicsLocation(new Vector3f(0, 1, 0));\n                        torso.setPhysicsRotation(Quaternion.IDENTITY);\n                        torso.setLinearVelocity(Vector3f.ZERO);\n                        torso.setAngularVelocity(Vector3f.ZERO);\n\n                        isRagdoll = false;\n                        System.out.println(\"PROBLEM: Character should be standing in T-pose but isn't!\");\n\n                    } else {\n                        // Go ragdoll\n                        System.out.println(\"\\n--- Going ragdoll ---\");\n                        ragdoll.setRagdollMode();\n\n                        // Apply damping\n                        for (var body : bulletAppState.getPhysicsSpace().getRigidBodyList()) {\n                            if (body.getMass() > 0) {\n                                body.setDamping(0.5f, 0.7f);\n                            }\n                        }\n\n                        isRagdoll = true;\n                        System.out.println(\"Currently: RAGDOLL (falls to ground)\");\n                    }\n                }\n            }\n        };\n\n        private void setupLights() {\n            DirectionalLight sun = new DirectionalLight();\n            sun.setDirection(new Vector3f(-0.5f, -1f, -0.3f).normalizeLocal());\n            sun.setColor(ColorRGBA.White);\n            rootNode.addLight(sun);\n\n            AmbientLight ambient = new AmbientLight();\n            ambient.setColor(new ColorRGBA(0.3f, 0.3f, 0.3f, 1f));\n            rootNode.addLight(ambient);\n        }\n\n        private void setupFloor() {\n            Box floorMesh = new Box(20f, 0.1f, 20f);\n            Geometry floorGeo = new Geometry(\"Floor\", floorMesh);\n            Material floorMat = new Material(assetManager, \"Common/MatDefs/Light/Lighting.j3md\");\n            floorMat.setBoolean(\"UseMaterialColors\", true);\n            floorMat.setColor(\"Diffuse\", ColorRGBA.Green);\n            floorGeo.setMaterial(floorMat);\n            floorGeo.setLocalTranslation(0f, -0.1f, 0f);\n            rootNode.attachChild(floorGeo);\n\n            RigidBodyControl floorPhysics = new RigidBodyControl(\n                    new BoxCollisionShape(new Vector3f(20f, 0.1f, 20f)), 0f);\n            floorGeo.addControl(floorPhysics);\n            bulletAppState.getPhysicsSpace().add(floorPhysics);\n            floorPhysics.setPhysicsLocation(new Vector3f(0f, -0.1f, 0f));\n        }\n\n        private void setupCamera() {\n            cam.setLocation(new Vector3f(0f, 2f, 6f));\n            cam.lookAt(new Vector3f(0f, 1f, 0f), Vector3f.UNIT_Y);\n            flyCam.setMoveSpeed(10f);\n        }\n\n        private static Spatial findSpatialWithSkinning(Spatial spatial) {\n            if (spatial.getControl(SkinningControl.class) != null) return spatial;\n            if (spatial instanceof Node) {\n                for (Spatial child : ((Node) spatial).getChildren()) {\n                    Spatial result = findSpatialWithSkinning(child);\n                    if (result != null) return result;\n                }\n            }\n            return null;\n        }\n    }\n",
  "title": "Mixing animations with Ragdoll behaviour"
}