Home | History | Annotate | Download | only in terrain
      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