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.PhysicsCollisionObject; 39 import com.jme3.bullet.collision.shapes.BoxCollisionShape; 40 import com.jme3.bullet.collision.shapes.CollisionShape; 41 import com.jme3.bullet.control.RigidBodyControl; 42 import com.jme3.bullet.util.CollisionShapeFactory; 43 import com.jme3.font.BitmapText; 44 import com.jme3.input.ChaseCamera; 45 import com.jme3.input.KeyInput; 46 import com.jme3.input.controls.ActionListener; 47 import com.jme3.input.controls.AnalogListener; 48 import com.jme3.input.controls.KeyTrigger; 49 import com.jme3.light.DirectionalLight; 50 import com.jme3.light.PointLight; 51 import com.jme3.material.Material; 52 import com.jme3.math.*; 53 import com.jme3.renderer.Camera; 54 import com.jme3.renderer.queue.RenderQueue.ShadowMode; 55 import com.jme3.scene.Geometry; 56 import com.jme3.scene.Spatial; 57 import com.jme3.shadow.PssmShadowRenderer; 58 import com.jme3.shadow.PssmShadowRenderer.CompareMode; 59 import com.jme3.shadow.PssmShadowRenderer.FilterMode; 60 import com.jme3.terrain.geomipmap.TerrainLodControl; 61 import com.jme3.terrain.geomipmap.TerrainQuad; 62 import com.jme3.terrain.heightmap.AbstractHeightMap; 63 import com.jme3.terrain.heightmap.ImageBasedHeightMap; 64 import com.jme3.texture.Texture; 65 import com.jme3.texture.Texture.WrapMode; 66 import com.jme3.util.SkyFactory; 67 import java.util.ArrayList; 68 import java.util.List; 69 70 public class TestHoveringTank extends SimpleApplication implements AnalogListener, 71 ActionListener { 72 73 private BulletAppState bulletAppState; 74 private PhysicsHoverControl hoverControl; 75 private Spatial spaceCraft; 76 TerrainQuad terrain; 77 Material matRock; 78 boolean wireframe = false; 79 protected BitmapText hintText; 80 PointLight pl; 81 Geometry lightMdl; 82 Geometry collisionMarker; 83 84 public static void main(String[] args) { 85 TestHoveringTank app = new TestHoveringTank(); 86 app.start(); 87 } 88 89 private PhysicsSpace getPhysicsSpace() { 90 return bulletAppState.getPhysicsSpace(); 91 } 92 93 private void setupKeys() { 94 inputManager.addMapping("Lefts", new KeyTrigger(KeyInput.KEY_A)); 95 inputManager.addMapping("Rights", new KeyTrigger(KeyInput.KEY_D)); 96 inputManager.addMapping("Ups", new KeyTrigger(KeyInput.KEY_W)); 97 inputManager.addMapping("Downs", new KeyTrigger(KeyInput.KEY_S)); 98 inputManager.addMapping("Space", new KeyTrigger(KeyInput.KEY_SPACE)); 99 inputManager.addMapping("Reset", new KeyTrigger(KeyInput.KEY_RETURN)); 100 inputManager.addListener(this, "Lefts"); 101 inputManager.addListener(this, "Rights"); 102 inputManager.addListener(this, "Ups"); 103 inputManager.addListener(this, "Downs"); 104 inputManager.addListener(this, "Space"); 105 inputManager.addListener(this, "Reset"); 106 } 107 108 @Override 109 public void simpleInitApp() { 110 bulletAppState = new BulletAppState(); 111 bulletAppState.setThreadingType(BulletAppState.ThreadingType.PARALLEL); 112 stateManager.attach(bulletAppState); 113 // bulletAppState.getPhysicsSpace().enableDebug(assetManager); 114 bulletAppState.getPhysicsSpace().setAccuracy(1f/30f); 115 rootNode.attachChild(SkyFactory.createSky(assetManager, "Textures/Sky/Bright/BrightSky.dds", false)); 116 117 PssmShadowRenderer pssmr = new PssmShadowRenderer(assetManager, 2048, 3); 118 pssmr.setDirection(new Vector3f(-0.5f, -0.3f, -0.3f).normalizeLocal()); 119 pssmr.setLambda(0.55f); 120 pssmr.setShadowIntensity(0.6f); 121 pssmr.setCompareMode(CompareMode.Hardware); 122 pssmr.setFilterMode(FilterMode.Bilinear); 123 viewPort.addProcessor(pssmr); 124 125 setupKeys(); 126 createTerrain(); 127 buildPlayer(); 128 129 DirectionalLight dl = new DirectionalLight(); 130 dl.setColor(new ColorRGBA(1.0f, 0.94f, 0.8f, 1f).multLocal(1.3f)); 131 dl.setDirection(new Vector3f(-0.5f, -0.3f, -0.3f).normalizeLocal()); 132 rootNode.addLight(dl); 133 134 Vector3f lightDir2 = new Vector3f(0.70518064f, 0.5902297f, -0.39287305f); 135 DirectionalLight dl2 = new DirectionalLight(); 136 dl2.setColor(new ColorRGBA(0.7f, 0.85f, 1.0f, 1f)); 137 dl2.setDirection(lightDir2); 138 rootNode.addLight(dl2); 139 } 140 141 private void buildPlayer() { 142 spaceCraft = assetManager.loadModel("Models/HoverTank/Tank2.mesh.xml"); 143 CollisionShape colShape = CollisionShapeFactory.createDynamicMeshShape(spaceCraft); 144 spaceCraft.setShadowMode(ShadowMode.CastAndReceive); 145 spaceCraft.setLocalTranslation(new Vector3f(-140, 14, -23)); 146 spaceCraft.setLocalRotation(new Quaternion(new float[]{0, 0.01f, 0})); 147 148 hoverControl = new PhysicsHoverControl(colShape, 500); 149 hoverControl.setCollisionGroup(PhysicsCollisionObject.COLLISION_GROUP_02); 150 151 spaceCraft.addControl(hoverControl); 152 153 154 rootNode.attachChild(spaceCraft); 155 getPhysicsSpace().add(hoverControl); 156 157 ChaseCamera chaseCam = new ChaseCamera(cam, inputManager); 158 spaceCraft.addControl(chaseCam); 159 160 flyCam.setEnabled(false); 161 } 162 163 public void makeMissile() { 164 Vector3f pos = spaceCraft.getWorldTranslation().clone(); 165 Quaternion rot = spaceCraft.getWorldRotation(); 166 Vector3f dir = rot.getRotationColumn(2); 167 168 Spatial missile = assetManager.loadModel("Models/SpaceCraft/Rocket.mesh.xml"); 169 missile.scale(0.5f); 170 missile.rotate(0, FastMath.PI, 0); 171 missile.updateGeometricState(); 172 173 BoundingBox box = (BoundingBox) missile.getWorldBound(); 174 final Vector3f extent = box.getExtent(null); 175 176 BoxCollisionShape boxShape = new BoxCollisionShape(extent); 177 178 missile.setName("Missile"); 179 missile.rotate(rot); 180 missile.setLocalTranslation(pos.addLocal(0, extent.y * 4.5f, 0)); 181 missile.setLocalRotation(hoverControl.getPhysicsRotation()); 182 missile.setShadowMode(ShadowMode.Cast); 183 RigidBodyControl control = new BombControl(assetManager, boxShape, 20); 184 control.setLinearVelocity(dir.mult(100)); 185 control.setCollisionGroup(PhysicsCollisionObject.COLLISION_GROUP_03); 186 missile.addControl(control); 187 188 189 rootNode.attachChild(missile); 190 getPhysicsSpace().add(missile); 191 } 192 193 public void onAnalog(String binding, float value, float tpf) { 194 } 195 196 public void onAction(String binding, boolean value, float tpf) { 197 if (binding.equals("Lefts")) { 198 hoverControl.steer(value ? 50f : 0); 199 } else if (binding.equals("Rights")) { 200 hoverControl.steer(value ? -50f : 0); 201 } else if (binding.equals("Ups")) { 202 hoverControl.accelerate(value ? 100f : 0); 203 } else if (binding.equals("Downs")) { 204 hoverControl.accelerate(value ? -100f : 0); 205 } else if (binding.equals("Reset")) { 206 if (value) { 207 System.out.println("Reset"); 208 hoverControl.setPhysicsLocation(new Vector3f(-140, 14, -23)); 209 hoverControl.setPhysicsRotation(new Matrix3f()); 210 hoverControl.clearForces(); 211 } else { 212 } 213 } else if (binding.equals("Space") && value) { 214 makeMissile(); 215 } 216 } 217 218 public void updateCamera() { 219 rootNode.updateGeometricState(); 220 221 Vector3f pos = spaceCraft.getWorldTranslation().clone(); 222 Quaternion rot = spaceCraft.getWorldRotation(); 223 Vector3f dir = rot.getRotationColumn(2); 224 225 // make it XZ only 226 Vector3f camPos = new Vector3f(dir); 227 camPos.setY(0); 228 camPos.normalizeLocal(); 229 230 // negate and multiply by distance from object 231 camPos.negateLocal(); 232 camPos.multLocal(15); 233 234 // add Y distance 235 camPos.setY(2); 236 camPos.addLocal(pos); 237 cam.setLocation(camPos); 238 239 Vector3f lookAt = new Vector3f(dir); 240 lookAt.multLocal(7); // look at dist 241 lookAt.addLocal(pos); 242 cam.lookAt(lookAt, Vector3f.UNIT_Y); 243 } 244 245 @Override 246 public void simpleUpdate(float tpf) { 247 } 248 249 private void createTerrain() { 250 matRock = new Material(assetManager, "Common/MatDefs/Terrain/TerrainLighting.j3md"); 251 matRock.setBoolean("useTriPlanarMapping", false); 252 matRock.setBoolean("WardIso", true); 253 matRock.setTexture("AlphaMap", assetManager.loadTexture("Textures/Terrain/splat/alphamap.png")); 254 Texture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png"); 255 Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); 256 grass.setWrap(WrapMode.Repeat); 257 matRock.setTexture("DiffuseMap", grass); 258 matRock.setFloat("DiffuseMap_0_scale", 64); 259 Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg"); 260 dirt.setWrap(WrapMode.Repeat); 261 matRock.setTexture("DiffuseMap_1", dirt); 262 matRock.setFloat("DiffuseMap_1_scale", 16); 263 Texture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg"); 264 rock.setWrap(WrapMode.Repeat); 265 matRock.setTexture("DiffuseMap_2", rock); 266 matRock.setFloat("DiffuseMap_2_scale", 128); 267 Texture normalMap0 = assetManager.loadTexture("Textures/Terrain/splat/grass_normal.jpg"); 268 normalMap0.setWrap(WrapMode.Repeat); 269 Texture normalMap1 = assetManager.loadTexture("Textures/Terrain/splat/dirt_normal.png"); 270 normalMap1.setWrap(WrapMode.Repeat); 271 Texture normalMap2 = assetManager.loadTexture("Textures/Terrain/splat/road_normal.png"); 272 normalMap2.setWrap(WrapMode.Repeat); 273 matRock.setTexture("NormalMap", normalMap0); 274 matRock.setTexture("NormalMap_1", normalMap2); 275 matRock.setTexture("NormalMap_2", normalMap2); 276 277 AbstractHeightMap heightmap = null; 278 try { 279 heightmap = new ImageBasedHeightMap(heightMapImage.getImage(), 0.25f); 280 heightmap.load(); 281 } catch (Exception e) { 282 e.printStackTrace(); 283 } 284 terrain = new TerrainQuad("terrain", 65, 513, heightmap.getHeightMap()); 285 List<Camera> cameras = new ArrayList<Camera>(); 286 cameras.add(getCamera()); 287 TerrainLodControl control = new TerrainLodControl(terrain, cameras); 288 terrain.addControl(control); 289 terrain.setMaterial(matRock); 290 terrain.setLocalScale(new Vector3f(2, 2, 2)); 291 terrain.setLocked(false); // unlock it so we can edit the height 292 293 terrain.setShadowMode(ShadowMode.CastAndReceive); 294 terrain.addControl(new RigidBodyControl(0)); 295 rootNode.attachChild(terrain); 296 getPhysicsSpace().addAll(terrain); 297 298 } 299 } 300