diff --git a/engine/src/test/jme3test/bullet/TestRagdollCharacter.java b/engine/src/test/jme3test/bullet/TestRagdollCharacter.java new file mode 100644 index 000000000..57030009b --- /dev/null +++ b/engine/src/test/jme3test/bullet/TestRagdollCharacter.java @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2009-2010 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package jme3test.bullet; + +import com.jme3.animation.AnimChannel; +import com.jme3.animation.AnimControl; +import com.jme3.animation.AnimEventListener; +import com.jme3.animation.Bone; +import com.jme3.animation.LoopMode; +import com.jme3.bullet.BulletAppState; +import com.jme3.app.SimpleApplication; +import com.jme3.asset.TextureKey; +import com.jme3.bullet.PhysicsSpace; +import com.jme3.bullet.collision.PhysicsCollisionEvent; +import com.jme3.bullet.collision.PhysicsCollisionObject; +import com.jme3.bullet.collision.RagdollCollisionListener; +import com.jme3.bullet.control.KinematicRagdollControl; +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.ColorRGBA; +import com.jme3.math.FastMath; +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; +import com.jme3.scene.shape.Box; +import com.jme3.texture.Texture; + +/** + * @author normenhansen + */ +public class TestRagdollCharacter extends SimpleApplication implements RagdollCollisionListener, AnimEventListener, ActionListener { + + BulletAppState bulletAppState; + Material matBullet; + Node model; + KinematicRagdollControl ragdoll; + float bulletSize = 1f; + Material mat; + Material mat3; + boolean leftStrafe = false, rightStrafe = false, forward = false, backward = false, + leftRotate = false, rightRotate = false; + AnimControl animControl; + AnimChannel animChannel; + + public static void main(String[] args) { + TestRagdollCharacter app = new TestRagdollCharacter(); + app.start(); + } + + public void simpleInitApp() { + bulletAppState = new BulletAppState(); + bulletAppState.setEnabled(true); + stateManager.attach(bulletAppState); + initMaterial(); + setupKeys(); + initWall(2,1,1); + + cam.setLocation(new Vector3f(-8,0,-4)); + cam.lookAt(new Vector3f(4,0,-7), Vector3f.UNIT_Y); + + + +// bulletAppState.getPhysicsSpace().enableDebug(assetManager); + PhysicsTestHelper.createPhysicsTestWorld(rootNode, assetManager, bulletAppState.getPhysicsSpace()); + setupLight(); + + model = (Node) assetManager.loadModel("Models/Sinbad/Sinbad.mesh.xml"); + model.lookAt(new Vector3f(0,0,-1), Vector3f.UNIT_Y); + model.setLocalTranslation(4, 0, -7f); + + // model.setLocalRotation(new Quaternion().fromAngleAxis(FastMath.HALF_PI, Vector3f.UNIT_X)); + + //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); + + + //Oto's head is almost rigid + // ragdoll.setJointLimit("head", 0, 0, eighth_pi, -eighth_pi, 0, 0); + + getPhysicsSpace().add(ragdoll); + speed = 1.3f; + + rootNode.attachChild(model); + + + AnimControl control = model.getControl(AnimControl.class); + animChannel = control.createChannel(); + animChannel.setAnim("IdleTop"); + control.addListener(this); + + } + + 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(); + } + + 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); + } + + 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 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"); + } + + 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.setSpeed(0.3f); + animChannel.setAnim("SliceHorizontal"); + animChannel.setSpeed(0.3f); + } + } + } + + 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"); + + } + + @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()); + } + + 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); + } + + } + + public void onAnimChange(AnimControl control, AnimChannel channel, String animName) { + } + + public void initWall(float bLength, float bWidth, float bHeight) { + Box brick = new Box(Vector3f.ZERO, bLength, bHeight, bWidth); + brick.scaleTextureCoordinates(new Vector2f(1f, .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; + 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); + Geometry reBoxg = new Geometry("brick", brick); + reBoxg.setMaterial(mat2); + reBoxg.setLocalTranslation(ori); + //for geometry with sphere mesh the physics system automatically uses a sphere collision shape + reBoxg.addControl(new RigidBodyControl(1.5f)); + reBoxg.setShadowMode(ShadowMode.CastAndReceive); + reBoxg.getControl(RigidBodyControl.class).setFriction(0.6f); + this.rootNode.attachChild(reBoxg); + this.getPhysicsSpace().add(reBoxg); + } + startpt = -startpt; + height += 2 * bHeight; + } + } + +}