1 /* 2 * Copyright (c) 2009-2010 jMonkeyEngine 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * * Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 package jme3test.bullet; 33 34 import com.jme3.animation.*; 35 import com.jme3.app.SimpleApplication; 36 import com.jme3.asset.TextureKey; 37 import com.jme3.bullet.BulletAppState; 38 import com.jme3.bullet.PhysicsSpace; 39 import com.jme3.bullet.collision.PhysicsCollisionEvent; 40 import com.jme3.bullet.collision.PhysicsCollisionObject; 41 import com.jme3.bullet.collision.RagdollCollisionListener; 42 import com.jme3.bullet.collision.shapes.SphereCollisionShape; 43 import com.jme3.bullet.control.KinematicRagdollControl; 44 import com.jme3.bullet.control.RigidBodyControl; 45 import com.jme3.font.BitmapText; 46 import com.jme3.input.KeyInput; 47 import com.jme3.input.MouseInput; 48 import com.jme3.input.controls.ActionListener; 49 import com.jme3.input.controls.KeyTrigger; 50 import com.jme3.input.controls.MouseButtonTrigger; 51 import com.jme3.light.DirectionalLight; 52 import com.jme3.material.Material; 53 import com.jme3.math.ColorRGBA; 54 import com.jme3.math.FastMath; 55 import com.jme3.math.Quaternion; 56 import com.jme3.math.Vector3f; 57 import com.jme3.scene.Geometry; 58 import com.jme3.scene.Node; 59 import com.jme3.scene.debug.SkeletonDebugger; 60 import com.jme3.scene.shape.Sphere; 61 import com.jme3.scene.shape.Sphere.TextureMode; 62 import com.jme3.texture.Texture; 63 64 /** 65 * PHYSICS RAGDOLLS ARE NOT WORKING PROPERLY YET! 66 * @author normenhansen 67 */ 68 public class TestBoneRagdoll extends SimpleApplication implements RagdollCollisionListener, AnimEventListener { 69 70 private BulletAppState bulletAppState; 71 Material matBullet; 72 Node model; 73 KinematicRagdollControl ragdoll; 74 float bulletSize = 1f; 75 Material mat; 76 Material mat3; 77 private Sphere bullet; 78 private SphereCollisionShape bulletCollisionShape; 79 main(String[] args)80 public static void main(String[] args) { 81 TestBoneRagdoll app = new TestBoneRagdoll(); 82 app.start(); 83 } 84 simpleInitApp()85 public void simpleInitApp() { 86 initCrossHairs(); 87 initMaterial(); 88 89 cam.setLocation(new Vector3f(0.26924422f, 6.646658f, 22.265987f)); 90 cam.setRotation(new Quaternion(-2.302544E-4f, 0.99302495f, -0.117888905f, -0.0019395084f)); 91 92 93 bulletAppState = new BulletAppState(); 94 bulletAppState.setEnabled(true); 95 stateManager.attach(bulletAppState); 96 bullet = new Sphere(32, 32, 1.0f, true, false); 97 bullet.setTextureMode(TextureMode.Projected); 98 bulletCollisionShape = new SphereCollisionShape(1.0f); 99 100 // bulletAppState.getPhysicsSpace().enableDebug(assetManager); 101 PhysicsTestHelper.createPhysicsTestWorld(rootNode, assetManager, bulletAppState.getPhysicsSpace()); 102 setupLight(); 103 104 model = (Node) assetManager.loadModel("Models/Sinbad/Sinbad.mesh.xml"); 105 106 // model.setLocalRotation(new Quaternion().fromAngleAxis(FastMath.HALF_PI, Vector3f.UNIT_X)); 107 108 //debug view 109 AnimControl control = model.getControl(AnimControl.class); 110 SkeletonDebugger skeletonDebug = new SkeletonDebugger("skeleton", control.getSkeleton()); 111 Material mat2 = new Material(getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); 112 mat2.getAdditionalRenderState().setWireframe(true); 113 mat2.setColor("Color", ColorRGBA.Green); 114 mat2.getAdditionalRenderState().setDepthTest(false); 115 skeletonDebug.setMaterial(mat2); 116 skeletonDebug.setLocalTranslation(model.getLocalTranslation()); 117 118 //Note: PhysicsRagdollControl is still TODO, constructor will change 119 ragdoll = new KinematicRagdollControl(0.5f); 120 setupSinbad(ragdoll); 121 ragdoll.addCollisionListener(this); 122 model.addControl(ragdoll); 123 124 float eighth_pi = FastMath.PI * 0.125f; 125 ragdoll.setJointLimit("Waist", eighth_pi, eighth_pi, eighth_pi, eighth_pi, eighth_pi, eighth_pi); 126 ragdoll.setJointLimit("Chest", eighth_pi, eighth_pi, 0, 0, eighth_pi, eighth_pi); 127 128 129 //Oto's head is almost rigid 130 // ragdoll.setJointLimit("head", 0, 0, eighth_pi, -eighth_pi, 0, 0); 131 132 getPhysicsSpace().add(ragdoll); 133 speed = 1.3f; 134 135 rootNode.attachChild(model); 136 // rootNode.attachChild(skeletonDebug); 137 flyCam.setMoveSpeed(50); 138 139 140 animChannel = control.createChannel(); 141 animChannel.setAnim("Dance"); 142 control.addListener(this); 143 144 inputManager.addListener(new ActionListener() { 145 146 public void onAction(String name, boolean isPressed, float tpf) { 147 if (name.equals("toggle") && isPressed) { 148 149 Vector3f v = new Vector3f(); 150 v.set(model.getLocalTranslation()); 151 v.y = 0; 152 model.setLocalTranslation(v); 153 Quaternion q = new Quaternion(); 154 float[] angles = new float[3]; 155 model.getLocalRotation().toAngles(angles); 156 q.fromAngleAxis(angles[1], Vector3f.UNIT_Y); 157 model.setLocalRotation(q); 158 if (angles[0] < 0) { 159 animChannel.setAnim("StandUpBack"); 160 ragdoll.blendToKinematicMode(0.5f); 161 } else { 162 animChannel.setAnim("StandUpFront"); 163 ragdoll.blendToKinematicMode(0.5f); 164 } 165 166 } 167 if (name.equals("bullet+") && isPressed) { 168 bulletSize += 0.1f; 169 170 } 171 if (name.equals("bullet-") && isPressed) { 172 bulletSize -= 0.1f; 173 174 } 175 176 if (name.equals("stop") && isPressed) { 177 ragdoll.setEnabled(!ragdoll.isEnabled()); 178 ragdoll.setRagdollMode(); 179 } 180 181 if (name.equals("shoot") && !isPressed) { 182 Geometry bulletg = new Geometry("bullet", bullet); 183 bulletg.setMaterial(matBullet); 184 bulletg.setLocalTranslation(cam.getLocation()); 185 bulletg.setLocalScale(bulletSize); 186 bulletCollisionShape = new SphereCollisionShape(bulletSize); 187 RigidBodyControl bulletNode = new RigidBodyControl(bulletCollisionShape, bulletSize * 10); 188 bulletNode.setCcdMotionThreshold(0.001f); 189 bulletNode.setLinearVelocity(cam.getDirection().mult(80)); 190 bulletg.addControl(bulletNode); 191 rootNode.attachChild(bulletg); 192 getPhysicsSpace().add(bulletNode); 193 } 194 if (name.equals("boom") && !isPressed) { 195 Geometry bulletg = new Geometry("bullet", bullet); 196 bulletg.setMaterial(matBullet); 197 bulletg.setLocalTranslation(cam.getLocation()); 198 bulletg.setLocalScale(bulletSize); 199 bulletCollisionShape = new SphereCollisionShape(bulletSize); 200 BombControl bulletNode = new BombControl(assetManager, bulletCollisionShape, 1); 201 bulletNode.setForceFactor(8); 202 bulletNode.setExplosionRadius(20); 203 bulletNode.setCcdMotionThreshold(0.001f); 204 bulletNode.setLinearVelocity(cam.getDirection().mult(180)); 205 bulletg.addControl(bulletNode); 206 rootNode.attachChild(bulletg); 207 getPhysicsSpace().add(bulletNode); 208 } 209 } 210 }, "toggle", "shoot", "stop", "bullet+", "bullet-", "boom"); 211 inputManager.addMapping("toggle", new KeyTrigger(KeyInput.KEY_SPACE)); 212 inputManager.addMapping("shoot", new MouseButtonTrigger(MouseInput.BUTTON_LEFT)); 213 inputManager.addMapping("boom", new MouseButtonTrigger(MouseInput.BUTTON_RIGHT)); 214 inputManager.addMapping("stop", new KeyTrigger(KeyInput.KEY_H)); 215 inputManager.addMapping("bullet-", new KeyTrigger(KeyInput.KEY_COMMA)); 216 inputManager.addMapping("bullet+", new KeyTrigger(KeyInput.KEY_PERIOD)); 217 218 219 } 220 setupLight()221 private void setupLight() { 222 // AmbientLight al = new AmbientLight(); 223 // al.setColor(ColorRGBA.White.mult(1)); 224 // rootNode.addLight(al); 225 226 DirectionalLight dl = new DirectionalLight(); 227 dl.setDirection(new Vector3f(-0.1f, -0.7f, -1).normalizeLocal()); 228 dl.setColor(new ColorRGBA(1f, 1f, 1f, 1.0f)); 229 rootNode.addLight(dl); 230 } 231 getPhysicsSpace()232 private PhysicsSpace getPhysicsSpace() { 233 return bulletAppState.getPhysicsSpace(); 234 } 235 initMaterial()236 public void initMaterial() { 237 238 matBullet = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); 239 TextureKey key2 = new TextureKey("Textures/Terrain/Rock/Rock.PNG"); 240 key2.setGenerateMips(true); 241 Texture tex2 = assetManager.loadTexture(key2); 242 matBullet.setTexture("ColorMap", tex2); 243 } 244 initCrossHairs()245 protected void initCrossHairs() { 246 guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt"); 247 BitmapText ch = new BitmapText(guiFont, false); 248 ch.setSize(guiFont.getCharSet().getRenderedSize() * 2); 249 ch.setText("+"); // crosshairs 250 ch.setLocalTranslation( // center 251 settings.getWidth() / 2 - guiFont.getCharSet().getRenderedSize() / 3 * 2, 252 settings.getHeight() / 2 + ch.getLineHeight() / 2, 0); 253 guiNode.attachChild(ch); 254 } 255 collide(Bone bone, PhysicsCollisionObject object, PhysicsCollisionEvent event)256 public void collide(Bone bone, PhysicsCollisionObject object, PhysicsCollisionEvent event) { 257 258 if (object.getUserObject() != null && object.getUserObject() instanceof Geometry) { 259 Geometry geom = (Geometry) object.getUserObject(); 260 if ("Floor".equals(geom.getName())) { 261 return; 262 } 263 } 264 265 ragdoll.setRagdollMode(); 266 267 } 268 setupSinbad(KinematicRagdollControl ragdoll)269 private void setupSinbad(KinematicRagdollControl ragdoll) { 270 ragdoll.addBoneName("Ulna.L"); 271 ragdoll.addBoneName("Ulna.R"); 272 ragdoll.addBoneName("Chest"); 273 ragdoll.addBoneName("Foot.L"); 274 ragdoll.addBoneName("Foot.R"); 275 ragdoll.addBoneName("Hand.R"); 276 ragdoll.addBoneName("Hand.L"); 277 ragdoll.addBoneName("Neck"); 278 ragdoll.addBoneName("Root"); 279 ragdoll.addBoneName("Stomach"); 280 ragdoll.addBoneName("Waist"); 281 ragdoll.addBoneName("Humerus.L"); 282 ragdoll.addBoneName("Humerus.R"); 283 ragdoll.addBoneName("Thigh.L"); 284 ragdoll.addBoneName("Thigh.R"); 285 ragdoll.addBoneName("Calf.L"); 286 ragdoll.addBoneName("Calf.R"); 287 ragdoll.addBoneName("Clavicle.L"); 288 ragdoll.addBoneName("Clavicle.R"); 289 290 } 291 float elTime = 0; 292 boolean forward = true; 293 AnimControl animControl; 294 AnimChannel animChannel; 295 Vector3f direction = new Vector3f(0, 0, 1); 296 Quaternion rotate = new Quaternion().fromAngleAxis(FastMath.PI / 8, Vector3f.UNIT_Y); 297 boolean dance = true; 298 299 @Override simpleUpdate(float tpf)300 public void simpleUpdate(float tpf) { 301 // System.out.println(((BoundingBox) model.getWorldBound()).getYExtent()); 302 // elTime += tpf; 303 // if (elTime > 3) { 304 // elTime = 0; 305 // if (dance) { 306 // rotate.multLocal(direction); 307 // } 308 // if (Math.random() > 0.80) { 309 // dance = true; 310 // animChannel.setAnim("Dance"); 311 // } else { 312 // dance = false; 313 // animChannel.setAnim("RunBase"); 314 // rotate.fromAngleAxis(FastMath.QUARTER_PI * ((float) Math.random() - 0.5f), Vector3f.UNIT_Y); 315 // rotate.multLocal(direction); 316 // } 317 // } 318 // if (!ragdoll.hasControl() && !dance) { 319 // if (model.getLocalTranslation().getZ() < -10) { 320 // direction.z = 1; 321 // direction.normalizeLocal(); 322 // } else if (model.getLocalTranslation().getZ() > 10) { 323 // direction.z = -1; 324 // direction.normalizeLocal(); 325 // } 326 // if (model.getLocalTranslation().getX() < -10) { 327 // direction.x = 1; 328 // direction.normalizeLocal(); 329 // } else if (model.getLocalTranslation().getX() > 10) { 330 // direction.x = -1; 331 // direction.normalizeLocal(); 332 // } 333 // model.move(direction.multLocal(tpf * 8)); 334 // direction.normalizeLocal(); 335 // model.lookAt(model.getLocalTranslation().add(direction), Vector3f.UNIT_Y); 336 // } 337 } 338 onAnimCycleDone(AnimControl control, AnimChannel channel, String animName)339 public void onAnimCycleDone(AnimControl control, AnimChannel channel, String animName) { 340 // if(channel.getAnimationName().equals("StandUpFront")){ 341 // channel.setAnim("Dance"); 342 // } 343 344 if (channel.getAnimationName().equals("StandUpBack") || channel.getAnimationName().equals("StandUpFront")) { 345 channel.setLoopMode(LoopMode.DontLoop); 346 channel.setAnim("IdleTop", 5); 347 channel.setLoopMode(LoopMode.Loop); 348 } 349 // if(channel.getAnimationName().equals("IdleTop")){ 350 // channel.setAnim("StandUpFront"); 351 // } 352 353 } 354 onAnimChange(AnimControl control, AnimChannel channel, String animName)355 public void onAnimChange(AnimControl control, AnimChannel channel, String animName) { 356 } 357 } 358