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.terrain; 33 34 import com.jme3.bullet.collision.shapes.SphereCollisionShape; 35 import com.jme3.app.SimpleApplication; 36 import com.jme3.bounding.BoundingBox; 37 import com.jme3.bullet.BulletAppState; 38 import com.jme3.bullet.collision.shapes.SphereCollisionShape; 39 import com.jme3.bullet.control.RigidBodyControl; 40 import com.jme3.collision.CollisionResult; 41 import com.jme3.collision.CollisionResults; 42 import com.jme3.font.BitmapText; 43 import com.jme3.input.KeyInput; 44 import com.jme3.input.MouseInput; 45 import com.jme3.input.controls.ActionListener; 46 import com.jme3.input.controls.KeyTrigger; 47 import com.jme3.input.controls.MouseButtonTrigger; 48 import com.jme3.light.DirectionalLight; 49 import com.jme3.light.PointLight; 50 import com.jme3.material.Material; 51 import com.jme3.math.ColorRGBA; 52 import com.jme3.math.Ray; 53 import com.jme3.math.Vector2f; 54 import com.jme3.math.Vector3f; 55 import com.jme3.scene.Geometry; 56 import com.jme3.scene.Node; 57 import com.jme3.scene.shape.Box; 58 import com.jme3.scene.shape.Sphere; 59 import com.jme3.terrain.geomipmap.TerrainLodControl; 60 import com.jme3.terrain.geomipmap.TerrainQuad; 61 import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator; 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 jme3tools.converters.ImageToAwt; 67 68 /** 69 * Creates a terrain object and a collision node to go with it. Then 70 * drops several balls from the sky that collide with the terrain 71 * and roll around. 72 * Left click to place a sphere on the ground where the crosshairs intersect the terrain. 73 * Hit keys 1 or 2 to raise/lower the terrain at that spot. 74 * 75 * @author Brent Owens 76 */ 77 public class TerrainTestCollision extends SimpleApplication { 78 79 TerrainQuad terrain; 80 Node terrainPhysicsNode; 81 Material matRock; 82 Material matWire; 83 boolean wireframe = false; 84 protected BitmapText hintText; 85 PointLight pl; 86 Geometry lightMdl; 87 Geometry collisionMarker; 88 private BulletAppState bulletAppState; 89 Geometry collisionSphere; 90 Geometry collisionBox; 91 Geometry selectedCollisionObject; 92 93 public static void main(String[] args) { 94 TerrainTestCollision app = new TerrainTestCollision(); 95 app.start(); 96 } 97 98 @Override 99 public void initialize() { 100 super.initialize(); 101 loadHintText(); 102 initCrossHairs(); 103 } 104 105 @Override 106 public void simpleInitApp() { 107 bulletAppState = new BulletAppState(); 108 bulletAppState.setThreadingType(BulletAppState.ThreadingType.PARALLEL); 109 stateManager.attach(bulletAppState); 110 setupKeys(); 111 matRock = new Material(assetManager, "Common/MatDefs/Terrain/Terrain.j3md"); 112 matRock.setTexture("Alpha", assetManager.loadTexture("Textures/Terrain/splat/alphamap.png")); 113 Texture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png"); 114 Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); 115 grass.setWrap(WrapMode.Repeat); 116 matRock.setTexture("Tex1", grass); 117 matRock.setFloat("Tex1Scale", 64f); 118 Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg"); 119 dirt.setWrap(WrapMode.Repeat); 120 matRock.setTexture("Tex2", dirt); 121 matRock.setFloat("Tex2Scale", 32f); 122 Texture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg"); 123 rock.setWrap(WrapMode.Repeat); 124 matRock.setTexture("Tex3", rock); 125 matRock.setFloat("Tex3Scale", 128f); 126 matWire = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); 127 matWire.getAdditionalRenderState().setWireframe(true); 128 matWire.setColor("Color", ColorRGBA.Green); 129 AbstractHeightMap heightmap = null; 130 try { 131 heightmap = new ImageBasedHeightMap(heightMapImage.getImage(), 0.25f); 132 heightmap.load(); 133 134 } catch (Exception e) { 135 } 136 137 terrain = new TerrainQuad("terrain", 65, 513, heightmap.getHeightMap()); 138 TerrainLodControl control = new TerrainLodControl(terrain, getCamera()); 139 control.setLodCalculator( new DistanceLodCalculator(65, 2.7f) ); // patch size, and a multiplier 140 terrain.addControl(control); 141 terrain.setMaterial(matRock); 142 terrain.setLocalScale(new Vector3f(2, 2, 2)); 143 terrain.setLocked(false); // unlock it so we can edit the height 144 rootNode.attachChild(terrain); 145 146 147 /** 148 * Create PhysicsRigidBodyControl for collision 149 */ 150 terrain.addControl(new RigidBodyControl(0)); 151 bulletAppState.getPhysicsSpace().addAll(terrain); 152 153 154 // Add 5 physics spheres to the world, with random sizes and positions 155 // let them drop from the sky 156 for (int i = 0; i < 5; i++) { 157 float r = (float) (8 * Math.random()); 158 Geometry sphere = new Geometry("cannonball", new Sphere(10, 10, r)); 159 sphere.setMaterial(matWire); 160 float x = (float) (20 * Math.random()) - 40; // random position 161 float y = (float) (20 * Math.random()) - 40; // random position 162 float z = (float) (20 * Math.random()) - 40; // random position 163 sphere.setLocalTranslation(new Vector3f(x, 100 + y, z)); 164 sphere.addControl(new RigidBodyControl(new SphereCollisionShape(r), 2)); 165 rootNode.attachChild(sphere); 166 bulletAppState.getPhysicsSpace().add(sphere); 167 } 168 169 collisionBox = new Geometry("collisionBox", new Box(2, 2, 2)); 170 collisionBox.setModelBound(new BoundingBox()); 171 collisionBox.setLocalTranslation(new Vector3f(20, 95, 30)); 172 collisionBox.setMaterial(matWire); 173 rootNode.attachChild(collisionBox); 174 selectedCollisionObject = collisionBox; 175 176 DirectionalLight dl = new DirectionalLight(); 177 dl.setDirection(new Vector3f(1, -0.5f, -0.1f).normalizeLocal()); 178 dl.setColor(new ColorRGBA(0.50f, 0.40f, 0.50f, 1.0f)); 179 rootNode.addLight(dl); 180 181 cam.setLocation(new Vector3f(0, 25, -10)); 182 cam.lookAtDirection(new Vector3f(0, -1, 0).normalizeLocal(), Vector3f.UNIT_Y); 183 } 184 185 public void loadHintText() { 186 hintText = new BitmapText(guiFont, false); 187 hintText.setSize(guiFont.getCharSet().getRenderedSize()); 188 hintText.setLocalTranslation(0, getCamera().getHeight(), 0); 189 //hintText.setText("Hit T to switch to wireframe"); 190 hintText.setText(""); 191 guiNode.attachChild(hintText); 192 } 193 194 protected void initCrossHairs() { 195 //guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt"); 196 BitmapText ch = new BitmapText(guiFont, false); 197 ch.setSize(guiFont.getCharSet().getRenderedSize() * 2); 198 ch.setText("+"); // crosshairs 199 ch.setLocalTranslation( // center 200 settings.getWidth() / 2 - guiFont.getCharSet().getRenderedSize() / 3 * 2, 201 settings.getHeight() / 2 + ch.getLineHeight() / 2, 0); 202 guiNode.attachChild(ch); 203 } 204 205 private void setupKeys() { 206 flyCam.setMoveSpeed(50); 207 inputManager.addMapping("wireframe", new KeyTrigger(KeyInput.KEY_T)); 208 inputManager.addListener(actionListener, "wireframe"); 209 inputManager.addMapping("Lefts", new KeyTrigger(KeyInput.KEY_H)); 210 inputManager.addMapping("Rights", new KeyTrigger(KeyInput.KEY_K)); 211 inputManager.addMapping("Ups", new KeyTrigger(KeyInput.KEY_U)); 212 inputManager.addMapping("Downs", new KeyTrigger(KeyInput.KEY_J)); 213 inputManager.addMapping("Forwards", new KeyTrigger(KeyInput.KEY_Y)); 214 inputManager.addMapping("Backs", new KeyTrigger(KeyInput.KEY_I)); 215 inputManager.addListener(actionListener, "Lefts"); 216 inputManager.addListener(actionListener, "Rights"); 217 inputManager.addListener(actionListener, "Ups"); 218 inputManager.addListener(actionListener, "Downs"); 219 inputManager.addListener(actionListener, "Forwards"); 220 inputManager.addListener(actionListener, "Backs"); 221 inputManager.addMapping("shoot", new MouseButtonTrigger(MouseInput.BUTTON_LEFT)); 222 inputManager.addListener(actionListener, "shoot"); 223 inputManager.addMapping("cameraDown", new MouseButtonTrigger(MouseInput.BUTTON_RIGHT)); 224 inputManager.addListener(actionListener, "cameraDown"); 225 } 226 227 @Override 228 public void update() { 229 super.update(); 230 } 231 232 private void createCollisionMarker() { 233 Sphere s = new Sphere(6, 6, 1); 234 collisionMarker = new Geometry("collisionMarker"); 235 collisionMarker.setMesh(s); 236 Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); 237 mat.setColor("Color", ColorRGBA.Orange); 238 collisionMarker.setMaterial(mat); 239 rootNode.attachChild(collisionMarker); 240 } 241 private ActionListener actionListener = new ActionListener() { 242 243 public void onAction(String binding, boolean keyPressed, float tpf) { 244 if (binding.equals("wireframe") && !keyPressed) { 245 wireframe = !wireframe; 246 if (!wireframe) { 247 terrain.setMaterial(matWire); 248 } else { 249 terrain.setMaterial(matRock); 250 } 251 } else if (binding.equals("shoot") && !keyPressed) { 252 253 Vector3f origin = cam.getWorldCoordinates(new Vector2f(settings.getWidth() / 2, settings.getHeight() / 2), 0.0f); 254 Vector3f direction = cam.getWorldCoordinates(new Vector2f(settings.getWidth() / 2, settings.getHeight() / 2), 0.3f); 255 direction.subtractLocal(origin).normalizeLocal(); 256 257 258 Ray ray = new Ray(origin, direction); 259 CollisionResults results = new CollisionResults(); 260 int numCollisions = terrain.collideWith(ray, results); 261 if (numCollisions > 0) { 262 CollisionResult hit = results.getClosestCollision(); 263 if (collisionMarker == null) { 264 createCollisionMarker(); 265 } 266 Vector2f loc = new Vector2f(hit.getContactPoint().x, hit.getContactPoint().z); 267 float height = terrain.getHeight(loc); 268 System.out.println("collide " + hit.getContactPoint() + ", height: " + height + ", distance: " + hit.getDistance()); 269 collisionMarker.setLocalTranslation(new Vector3f(hit.getContactPoint().x, height, hit.getContactPoint().z)); 270 } 271 } else if (binding.equals("cameraDown") && !keyPressed) { 272 getCamera().lookAtDirection(new Vector3f(0, -1, 0), Vector3f.UNIT_Y); 273 } else if (binding.equals("Lefts") && !keyPressed) { 274 Vector3f oldLoc = selectedCollisionObject.getLocalTranslation().clone(); 275 selectedCollisionObject.move(-0.5f, 0, 0); 276 testCollision(oldLoc); 277 } else if (binding.equals("Rights") && !keyPressed) { 278 Vector3f oldLoc = selectedCollisionObject.getLocalTranslation().clone(); 279 selectedCollisionObject.move(0.5f, 0, 0); 280 testCollision(oldLoc); 281 } else if (binding.equals("Forwards") && !keyPressed) { 282 Vector3f oldLoc = selectedCollisionObject.getLocalTranslation().clone(); 283 selectedCollisionObject.move(0, 0, 0.5f); 284 testCollision(oldLoc); 285 } else if (binding.equals("Backs") && !keyPressed) { 286 Vector3f oldLoc = selectedCollisionObject.getLocalTranslation().clone(); 287 selectedCollisionObject.move(0, 0, -0.5f); 288 testCollision(oldLoc); 289 } else if (binding.equals("Ups") && !keyPressed) { 290 Vector3f oldLoc = selectedCollisionObject.getLocalTranslation().clone(); 291 selectedCollisionObject.move(0, 0.5f, 0); 292 testCollision(oldLoc); 293 } else if (binding.equals("Downs") && !keyPressed) { 294 Vector3f oldLoc = selectedCollisionObject.getLocalTranslation().clone(); 295 selectedCollisionObject.move(0, -0.5f, 0); 296 testCollision(oldLoc); 297 } 298 299 } 300 }; 301 302 private void testCollision(Vector3f oldLoc) { 303 if (terrain.collideWith(selectedCollisionObject.getWorldBound(), new CollisionResults()) > 0) { 304 selectedCollisionObject.setLocalTranslation(oldLoc); 305 } 306 } 307 } 308