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 33 package jme3test.helloworld; 34 35 import com.jme3.app.SimpleApplication; 36 import com.jme3.bullet.BulletAppState; 37 import com.jme3.bullet.collision.shapes.CapsuleCollisionShape; 38 import com.jme3.bullet.collision.shapes.CollisionShape; 39 import com.jme3.bullet.control.CharacterControl; 40 import com.jme3.bullet.control.RigidBodyControl; 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.material.Material; 46 import com.jme3.math.Vector3f; 47 import com.jme3.renderer.Camera; 48 import com.jme3.scene.Node; 49 import com.jme3.terrain.geomipmap.TerrainLodControl; 50 import com.jme3.terrain.geomipmap.TerrainQuad; 51 import com.jme3.terrain.heightmap.AbstractHeightMap; 52 import com.jme3.terrain.heightmap.ImageBasedHeightMap; 53 import com.jme3.texture.Texture; 54 import com.jme3.texture.Texture.WrapMode; 55 import java.util.ArrayList; 56 import java.util.List; 57 58 /** 59 * This demo shows a terrain with collision detection, 60 * that you can walk around in with a first-person perspective. 61 * This code combines HelloCollision and HelloTerrain. 62 */ 63 public class HelloTerrainCollision extends SimpleApplication 64 implements ActionListener { 65 66 private BulletAppState bulletAppState; 67 private RigidBodyControl landscape; 68 private CharacterControl player; 69 private Vector3f walkDirection = new Vector3f(); 70 private boolean left = false, right = false, up = false, down = false; 71 private TerrainQuad terrain; 72 private Material mat_terrain; 73 74 public static void main(String[] args) { 75 HelloTerrainCollision app = new HelloTerrainCollision(); 76 app.start(); 77 } 78 79 @Override 80 public void simpleInitApp() { 81 /** Set up Physics */ 82 bulletAppState = new BulletAppState(); 83 stateManager.attach(bulletAppState); 84 //bulletAppState.getPhysicsSpace().enableDebug(assetManager); 85 86 flyCam.setMoveSpeed(100); 87 setUpKeys(); 88 89 /** 1. Create terrain material and load four textures into it. */ 90 mat_terrain = new Material(assetManager, 91 "Common/MatDefs/Terrain/Terrain.j3md"); 92 93 /** 1.1) Add ALPHA map (for red-blue-green coded splat textures) */ 94 mat_terrain.setTexture("Alpha", assetManager.loadTexture( 95 "Textures/Terrain/splat/alphamap.png")); 96 97 /** 1.2) Add GRASS texture into the red layer (Tex1). */ 98 Texture grass = assetManager.loadTexture( 99 "Textures/Terrain/splat/grass.jpg"); 100 grass.setWrap(WrapMode.Repeat); 101 mat_terrain.setTexture("Tex1", grass); 102 mat_terrain.setFloat("Tex1Scale", 64f); 103 104 /** 1.3) Add DIRT texture into the green layer (Tex2) */ 105 Texture dirt = assetManager.loadTexture( 106 "Textures/Terrain/splat/dirt.jpg"); 107 dirt.setWrap(WrapMode.Repeat); 108 mat_terrain.setTexture("Tex2", dirt); 109 mat_terrain.setFloat("Tex2Scale", 32f); 110 111 /** 1.4) Add ROAD texture into the blue layer (Tex3) */ 112 Texture rock = assetManager.loadTexture( 113 "Textures/Terrain/splat/road.jpg"); 114 rock.setWrap(WrapMode.Repeat); 115 mat_terrain.setTexture("Tex3", rock); 116 mat_terrain.setFloat("Tex3Scale", 128f); 117 118 /** 2. Create the height map */ 119 AbstractHeightMap heightmap = null; 120 Texture heightMapImage = assetManager.loadTexture( 121 "Textures/Terrain/splat/mountains512.png"); 122 heightmap = new ImageBasedHeightMap(heightMapImage.getImage()); 123 heightmap.load(); 124 125 /** 3. We have prepared material and heightmap. 126 * Now we create the actual terrain: 127 * 3.1) Create a TerrainQuad and name it "my terrain". 128 * 3.2) A good value for terrain tiles is 64x64 -- so we supply 64+1=65. 129 * 3.3) We prepared a heightmap of size 512x512 -- so we supply 512+1=513. 130 * 3.4) As LOD step scale we supply Vector3f(1,1,1). 131 * 3.5) We supply the prepared heightmap itself. 132 */ 133 terrain = new TerrainQuad("my terrain", 65, 513, heightmap.getHeightMap()); 134 135 /** 4. We give the terrain its material, position & scale it, and attach it. */ 136 terrain.setMaterial(mat_terrain); 137 terrain.setLocalTranslation(0, -100, 0); 138 terrain.setLocalScale(2f, 1f, 2f); 139 rootNode.attachChild(terrain); 140 141 /** 5. The LOD (level of detail) depends on were the camera is: */ 142 List<Camera> cameras = new ArrayList<Camera>(); 143 cameras.add(getCamera()); 144 TerrainLodControl control = new TerrainLodControl(terrain, cameras); 145 terrain.addControl(control); 146 147 /** 6. Add physics: */ 148 // We set up collision detection for the scene by creating a 149 // compound collision shape and a static RigidBodyControl with mass zero.*/ 150 CollisionShape terrainShape = 151 CollisionShapeFactory.createMeshShape((Node) terrain); 152 landscape = new RigidBodyControl(terrainShape, 0); 153 terrain.addControl(landscape); 154 155 // We set up collision detection for the player by creating 156 // a capsule collision shape and a CharacterControl. 157 // The CharacterControl offers extra settings for 158 // size, stepheight, jumping, falling, and gravity. 159 // We also put the player in its starting position. 160 CapsuleCollisionShape capsuleShape = new CapsuleCollisionShape(1.5f, 6f, 1); 161 player = new CharacterControl(capsuleShape, 0.05f); 162 player.setJumpSpeed(20); 163 player.setFallSpeed(30); 164 player.setGravity(30); 165 player.setPhysicsLocation(new Vector3f(0, 10, 0)); 166 167 // We attach the scene and the player to the rootnode and the physics space, 168 // to make them appear in the game world. 169 bulletAppState.getPhysicsSpace().add(terrain); 170 bulletAppState.getPhysicsSpace().add(player); 171 172 } 173 /** We over-write some navigational key mappings here, so we can 174 * add physics-controlled walking and jumping: */ 175 private void setUpKeys() { 176 inputManager.addMapping("Left", new KeyTrigger(KeyInput.KEY_A)); 177 inputManager.addMapping("Right", new KeyTrigger(KeyInput.KEY_D)); 178 inputManager.addMapping("Up", new KeyTrigger(KeyInput.KEY_W)); 179 inputManager.addMapping("Down", new KeyTrigger(KeyInput.KEY_S)); 180 inputManager.addMapping("Jump", new KeyTrigger(KeyInput.KEY_SPACE)); 181 inputManager.addListener(this, "Left"); 182 inputManager.addListener(this, "Right"); 183 inputManager.addListener(this, "Up"); 184 inputManager.addListener(this, "Down"); 185 inputManager.addListener(this, "Jump"); 186 } 187 188 /** These are our custom actions triggered by key presses. 189 * We do not walk yet, we just keep track of the direction the user pressed. */ 190 public void onAction(String binding, boolean value, float tpf) { 191 if (binding.equals("Left")) { 192 if (value) { left = true; } else { left = false; } 193 } else if (binding.equals("Right")) { 194 if (value) { right = true; } else { right = false; } 195 } else if (binding.equals("Up")) { 196 if (value) { up = true; } else { up = false; } 197 } else if (binding.equals("Down")) { 198 if (value) { down = true; } else { down = false; } 199 } else if (binding.equals("Jump")) { 200 player.jump(); 201 } 202 } 203 204 /** 205 * This is the main event loop--walking happens here. 206 * We check in which direction the player is walking by interpreting 207 * the camera direction forward (camDir) and to the side (camLeft). 208 * The setWalkDirection() command is what lets a physics-controlled player walk. 209 * We also make sure here that the camera moves with player. 210 */ 211 @Override 212 public void simpleUpdate(float tpf) { 213 Vector3f camDir = cam.getDirection().clone().multLocal(0.6f); 214 Vector3f camLeft = cam.getLeft().clone().multLocal(0.4f); 215 walkDirection.set(0, 0, 0); 216 if (left) { walkDirection.addLocal(camLeft); } 217 if (right) { walkDirection.addLocal(camLeft.negate()); } 218 if (up) { walkDirection.addLocal(camDir); } 219 if (down) { walkDirection.addLocal(camDir.negate()); } 220 player.setWalkDirection(walkDirection); 221 cam.setLocation(player.getPhysicsLocation()); 222 } 223 } 224 225