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.app.SimpleApplication; 35 import com.jme3.bounding.BoundingBox; 36 import com.jme3.bullet.BulletAppState; 37 import com.jme3.bullet.PhysicsSpace; 38 import com.jme3.bullet.collision.shapes.CollisionShape; 39 import com.jme3.bullet.control.VehicleControl; 40 import com.jme3.bullet.objects.VehicleWheel; 41 import com.jme3.bullet.util.CollisionShapeFactory; 42 import com.jme3.input.KeyInput; 43 import com.jme3.input.controls.ActionListener; 44 import com.jme3.input.controls.KeyTrigger; 45 import com.jme3.light.DirectionalLight; 46 import com.jme3.math.FastMath; 47 import com.jme3.math.Matrix3f; 48 import com.jme3.math.Vector3f; 49 import com.jme3.renderer.queue.RenderQueue.ShadowMode; 50 import com.jme3.scene.Geometry; 51 import com.jme3.scene.Node; 52 import com.jme3.scene.Spatial; 53 import com.jme3.shadow.BasicShadowRenderer; 54 55 public class TestFancyCar extends SimpleApplication implements ActionListener { 56 57 private BulletAppState bulletAppState; 58 private VehicleControl player; 59 private VehicleWheel fr, fl, br, bl; 60 private Node node_fr, node_fl, node_br, node_bl; 61 private float wheelRadius; 62 private float steeringValue = 0; 63 private float accelerationValue = 0; 64 private Node carNode; 65 66 public static void main(String[] args) { 67 TestFancyCar app = new TestFancyCar(); 68 app.start(); 69 } 70 71 private void setupKeys() { 72 inputManager.addMapping("Lefts", new KeyTrigger(KeyInput.KEY_H)); 73 inputManager.addMapping("Rights", new KeyTrigger(KeyInput.KEY_K)); 74 inputManager.addMapping("Ups", new KeyTrigger(KeyInput.KEY_U)); 75 inputManager.addMapping("Downs", new KeyTrigger(KeyInput.KEY_J)); 76 inputManager.addMapping("Space", new KeyTrigger(KeyInput.KEY_SPACE)); 77 inputManager.addMapping("Reset", new KeyTrigger(KeyInput.KEY_RETURN)); 78 inputManager.addListener(this, "Lefts"); 79 inputManager.addListener(this, "Rights"); 80 inputManager.addListener(this, "Ups"); 81 inputManager.addListener(this, "Downs"); 82 inputManager.addListener(this, "Space"); 83 inputManager.addListener(this, "Reset"); 84 } 85 86 @Override 87 public void simpleInitApp() { 88 bulletAppState = new BulletAppState(); 89 stateManager.attach(bulletAppState); 90 // bulletAppState.getPhysicsSpace().enableDebug(assetManager); 91 if (settings.getRenderer().startsWith("LWJGL")) { 92 BasicShadowRenderer bsr = new BasicShadowRenderer(assetManager, 512); 93 bsr.setDirection(new Vector3f(-0.5f, -0.3f, -0.3f).normalizeLocal()); 94 viewPort.addProcessor(bsr); 95 } 96 cam.setFrustumFar(150f); 97 flyCam.setMoveSpeed(10); 98 99 setupKeys(); 100 PhysicsTestHelper.createPhysicsTestWorld(rootNode, assetManager, bulletAppState.getPhysicsSpace()); 101 // setupFloor(); 102 buildPlayer(); 103 104 DirectionalLight dl = new DirectionalLight(); 105 dl.setDirection(new Vector3f(-0.5f, -1f, -0.3f).normalizeLocal()); 106 rootNode.addLight(dl); 107 108 dl = new DirectionalLight(); 109 dl.setDirection(new Vector3f(0.5f, -0.1f, 0.3f).normalizeLocal()); 110 rootNode.addLight(dl); 111 } 112 113 private PhysicsSpace getPhysicsSpace() { 114 return bulletAppState.getPhysicsSpace(); 115 } 116 117 // public void setupFloor() { 118 // Material mat = assetManager.loadMaterial("Textures/Terrain/BrickWall/BrickWall.j3m"); 119 // mat.getTextureParam("DiffuseMap").getTextureValue().setWrap(WrapMode.Repeat); 120 //// mat.getTextureParam("NormalMap").getTextureValue().setWrap(WrapMode.Repeat); 121 //// mat.getTextureParam("ParallaxMap").getTextureValue().setWrap(WrapMode.Repeat); 122 // 123 // Box floor = new Box(Vector3f.ZERO, 140, 1f, 140); 124 // floor.scaleTextureCoordinates(new Vector2f(112.0f, 112.0f)); 125 // Geometry floorGeom = new Geometry("Floor", floor); 126 // floorGeom.setShadowMode(ShadowMode.Receive); 127 // floorGeom.setMaterial(mat); 128 // 129 // PhysicsNode tb = new PhysicsNode(floorGeom, new MeshCollisionShape(floorGeom.getMesh()), 0); 130 // tb.setLocalTranslation(new Vector3f(0f, -6, 0f)); 131 //// tb.attachDebugShape(assetManager); 132 // rootNode.attachChild(tb); 133 // getPhysicsSpace().add(tb); 134 // } 135 136 private Geometry findGeom(Spatial spatial, String name) { 137 if (spatial instanceof Node) { 138 Node node = (Node) spatial; 139 for (int i = 0; i < node.getQuantity(); i++) { 140 Spatial child = node.getChild(i); 141 Geometry result = findGeom(child, name); 142 if (result != null) { 143 return result; 144 } 145 } 146 } else if (spatial instanceof Geometry) { 147 if (spatial.getName().startsWith(name)) { 148 return (Geometry) spatial; 149 } 150 } 151 return null; 152 } 153 154 private void buildPlayer() { 155 float stiffness = 120.0f;//200=f1 car 156 float compValue = 0.2f; //(lower than damp!) 157 float dampValue = 0.3f; 158 final float mass = 400; 159 160 //Load model and get chassis Geometry 161 carNode = (Node)assetManager.loadModel("Models/Ferrari/Car.scene"); 162 carNode.setShadowMode(ShadowMode.Cast); 163 Geometry chasis = findGeom(carNode, "Car"); 164 BoundingBox box = (BoundingBox) chasis.getModelBound(); 165 166 //Create a hull collision shape for the chassis 167 CollisionShape carHull = CollisionShapeFactory.createDynamicMeshShape(chasis); 168 169 //Create a vehicle control 170 player = new VehicleControl(carHull, mass); 171 carNode.addControl(player); 172 173 //Setting default values for wheels 174 player.setSuspensionCompression(compValue * 2.0f * FastMath.sqrt(stiffness)); 175 player.setSuspensionDamping(dampValue * 2.0f * FastMath.sqrt(stiffness)); 176 player.setSuspensionStiffness(stiffness); 177 player.setMaxSuspensionForce(10000); 178 179 //Create four wheels and add them at their locations 180 //note that our fancy car actually goes backwards.. 181 Vector3f wheelDirection = new Vector3f(0, -1, 0); 182 Vector3f wheelAxle = new Vector3f(-1, 0, 0); 183 184 Geometry wheel_fr = findGeom(carNode, "WheelFrontRight"); 185 wheel_fr.center(); 186 box = (BoundingBox) wheel_fr.getModelBound(); 187 wheelRadius = box.getYExtent(); 188 float back_wheel_h = (wheelRadius * 1.7f) - 1f; 189 float front_wheel_h = (wheelRadius * 1.9f) - 1f; 190 player.addWheel(wheel_fr.getParent(), box.getCenter().add(0, -front_wheel_h, 0), 191 wheelDirection, wheelAxle, 0.2f, wheelRadius, true); 192 193 Geometry wheel_fl = findGeom(carNode, "WheelFrontLeft"); 194 wheel_fl.center(); 195 box = (BoundingBox) wheel_fl.getModelBound(); 196 player.addWheel(wheel_fl.getParent(), box.getCenter().add(0, -front_wheel_h, 0), 197 wheelDirection, wheelAxle, 0.2f, wheelRadius, true); 198 199 Geometry wheel_br = findGeom(carNode, "WheelBackRight"); 200 wheel_br.center(); 201 box = (BoundingBox) wheel_br.getModelBound(); 202 player.addWheel(wheel_br.getParent(), box.getCenter().add(0, -back_wheel_h, 0), 203 wheelDirection, wheelAxle, 0.2f, wheelRadius, false); 204 205 Geometry wheel_bl = findGeom(carNode, "WheelBackLeft"); 206 wheel_bl.center(); 207 box = (BoundingBox) wheel_bl.getModelBound(); 208 player.addWheel(wheel_bl.getParent(), box.getCenter().add(0, -back_wheel_h, 0), 209 wheelDirection, wheelAxle, 0.2f, wheelRadius, false); 210 211 player.getWheel(2).setFrictionSlip(4); 212 player.getWheel(3).setFrictionSlip(4); 213 214 rootNode.attachChild(carNode); 215 getPhysicsSpace().add(player); 216 } 217 218 public void onAction(String binding, boolean value, float tpf) { 219 if (binding.equals("Lefts")) { 220 if (value) { 221 steeringValue += .5f; 222 } else { 223 steeringValue += -.5f; 224 } 225 player.steer(steeringValue); 226 } else if (binding.equals("Rights")) { 227 if (value) { 228 steeringValue += -.5f; 229 } else { 230 steeringValue += .5f; 231 } 232 player.steer(steeringValue); 233 } //note that our fancy car actually goes backwards.. 234 else if (binding.equals("Ups")) { 235 if (value) { 236 accelerationValue -= 800; 237 } else { 238 accelerationValue += 800; 239 } 240 player.accelerate(accelerationValue); 241 player.setCollisionShape(CollisionShapeFactory.createDynamicMeshShape(findGeom(carNode, "Car"))); 242 } else if (binding.equals("Downs")) { 243 if (value) { 244 player.brake(40f); 245 } else { 246 player.brake(0f); 247 } 248 } else if (binding.equals("Reset")) { 249 if (value) { 250 System.out.println("Reset"); 251 player.setPhysicsLocation(Vector3f.ZERO); 252 player.setPhysicsRotation(new Matrix3f()); 253 player.setLinearVelocity(Vector3f.ZERO); 254 player.setAngularVelocity(Vector3f.ZERO); 255 player.resetSuspension(); 256 } else { 257 } 258 } 259 } 260 261 @Override 262 public void simpleUpdate(float tpf) { 263 cam.lookAt(carNode.getWorldTranslation(), Vector3f.UNIT_Y); 264 } 265 } 266