Bläddra i källkod

added in a NeighbourFinder interface to TerrainQuad so it can be used for tiling outside of TerrainGrid

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@9385 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
bre..ns 13 år sedan
förälder
incheckning
f654109aa4

+ 42 - 0
engine/src/terrain/com/jme3/terrain/geomipmap/NeighbourFinder.java

@@ -0,0 +1,42 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.terrain.geomipmap;
+
+/**
+ * Used for TerrainQuad to find neighbours that are not part of the
+ * same quad tree. Normally TerrainQuads function in a quad tree and
+ * use the neighbour methods getRightQuad, getLeftQuad etc. to update
+ * LOD values of the terrain (and for some other routines).
+ * 
+ * With this you can have a parent, control or spatial, that manages a group of
+ * TerrainQuads by linking them together through these four methods.
+ * 
+ * The general orientation of TerrainQuads and their sun-quads is as such:
+ * 
+ * 
+ *  +-- x+ ---->
+ *  |
+ *  |    1 | 3 (quadrants)
+ *  z+   --+--
+ *  |    2 | 4
+ *  |
+ * \/
+ * 
+ * Your implementation will still have to manage getHeight, getNormal, and 
+ * most other Terrain.java interface methods; often by offsetting the XZ
+ * coordinate parameters.
+ * 
+ * @author sploreg
+ */
+public interface NeighbourFinder {
+    
+    public TerrainQuad getRightQuad(TerrainQuad center);
+    
+    public TerrainQuad getLeftQuad(TerrainQuad center);
+    
+    public TerrainQuad getTopQuad(TerrainQuad center);
+    
+    public TerrainQuad getDownQuad(TerrainQuad center);
+}

+ 83 - 13
engine/src/terrain/com/jme3/terrain/geomipmap/TerrainQuad.java

@@ -106,6 +106,8 @@ public class TerrainQuad extends Node implements Terrain {
     private TerrainPicker picker;
     private Vector3f lastScale = Vector3f.UNIT_XYZ;
 
+    protected NeighbourFinder neighbourFinder;
+    
     public TerrainQuad() {
         super("Terrain");
     }
@@ -197,6 +199,10 @@ public class TerrainQuad extends Node implements Terrain {
         split(patchSize, heightMap);
     }
 
+    public void setNeighbourFinder(NeighbourFinder neighbourFinder) {
+        this.neighbourFinder = neighbourFinder;
+    }
+
     /**
      * Forces the recalculation of all normals on the terrain.
      */
@@ -824,6 +830,8 @@ public class TerrainQuad extends Node implements Terrain {
         int x = Math.round((xz.x / getWorldScale().x) + halfSize);
         int z = Math.round((xz.y / getWorldScale().z) + halfSize);
 
+        if (!isInside(x, z))
+            return Float.NaN;
         return getHeightmapHeight(x, z);
     }
 
@@ -921,6 +929,17 @@ public class TerrainQuad extends Node implements Terrain {
         return null;
     }
 
+    /**
+     * is the 2d point inside the terrain?
+     * @param x local coordinate
+     * @param z local coordinate
+     */
+    private boolean isInside(int x, int z) {
+        if (x < 0 || z < 0 || x > totalSize || z > totalSize)
+            return false;
+        return true;
+    }
+
     /**
      * Used for searching for a child and keeping
      * track of its quadrant
@@ -984,6 +1003,8 @@ public class TerrainQuad extends Node implements Terrain {
         // offset
         float x = (float)(((xz.x - getWorldTranslation().x) / getWorldScale().x) + (float)(totalSize-1) / 2f);
         float z = (float)(((xz.y - getWorldTranslation().z) / getWorldScale().z) + (float)(totalSize-1) / 2f);
+        if (!isInside((int)x, (int)z))
+            return Float.NaN;
         float height = getHeight((int)x, (int)z, (x%1f), (z%1f));
         height *= getWorldScale().y;
         return height;
@@ -1075,6 +1096,8 @@ public class TerrainQuad extends Node implements Terrain {
         for (int i=0; i<xz.size(); i++) {
             int x = Math.round((xz.get(i).x / getWorldScale().x) + halfSize);
             int z = Math.round((xz.get(i).y / getWorldScale().z) + halfSize);
+            if (!isInside(x, z))
+                continue;
             locations.add(new LocationHeight(x,z,height.get(i)));
         }
 
@@ -1137,7 +1160,6 @@ public class TerrainQuad extends Node implements Terrain {
         // distribute each locationHeight into the quadrant it intersects
         for (LocationHeight lh : locations) {
             int quad = findQuadrant(lh.x, lh.z);
-
             int col = lh.x;
             int row = lh.z;
 
@@ -1347,8 +1369,12 @@ public class TerrainQuad extends Node implements Terrain {
     }
 
     protected TerrainQuad findRightQuad() {
+        boolean useFinder = false;
         if (getParent() == null || !(getParent() instanceof TerrainQuad))
-            return null;
+             if (neighbourFinder == null)
+                return null;
+            else
+                useFinder = true;
 
         TerrainQuad pQuad = (TerrainQuad) getParent();
 
@@ -1357,11 +1383,19 @@ public class TerrainQuad extends Node implements Terrain {
         else if (quadrant == 2)
             return pQuad.getQuad(4);
         else if (quadrant == 3) {
-            TerrainQuad quad = pQuad.findRightQuad();
+            TerrainQuad quad = null;
+            if (useFinder)
+                quad = neighbourFinder.getRightQuad(this);
+            else
+                quad = pQuad.findRightQuad();
             if (quad != null)
                 return quad.getQuad(1);
         } else if (quadrant == 4) {
-            TerrainQuad quad = pQuad.findRightQuad();
+            TerrainQuad quad = null;
+            if (useFinder)
+                quad = neighbourFinder.getRightQuad(this);
+            else
+                quad = pQuad.findRightQuad();
             if (quad != null)
                 return quad.getQuad(2);
         }
@@ -1370,8 +1404,12 @@ public class TerrainQuad extends Node implements Terrain {
     }
 
     protected TerrainQuad findDownQuad() {
+        boolean useFinder = false;
         if (getParent() == null || !(getParent() instanceof TerrainQuad))
-            return null;
+             if (neighbourFinder == null)
+                return null;
+            else
+                useFinder = true;
 
         TerrainQuad pQuad = (TerrainQuad) getParent();
 
@@ -1380,11 +1418,19 @@ public class TerrainQuad extends Node implements Terrain {
         else if (quadrant == 3)
             return pQuad.getQuad(4);
         else if (quadrant == 2) {
-            TerrainQuad quad = pQuad.findDownQuad();
+            TerrainQuad quad = null;
+            if (useFinder)
+                quad = neighbourFinder.getDownQuad(this);
+            else
+                quad = pQuad.findDownQuad();
             if (quad != null)
                 return quad.getQuad(1);
         } else if (quadrant == 4) {
-            TerrainQuad quad = pQuad.findDownQuad();
+            TerrainQuad quad = null;
+            if (useFinder)
+                quad = neighbourFinder.getDownQuad(this);
+            else
+                quad = pQuad.findDownQuad();
             if (quad != null)
                 return quad.getQuad(3);
         }
@@ -1393,8 +1439,12 @@ public class TerrainQuad extends Node implements Terrain {
     }
 
     protected TerrainQuad findTopQuad() {
+        boolean useFinder = false;
         if (getParent() == null || !(getParent() instanceof TerrainQuad))
-            return null;
+             if (neighbourFinder == null)
+                return null;
+            else
+                useFinder = true;
 
         TerrainQuad pQuad = (TerrainQuad) getParent();
 
@@ -1403,11 +1453,19 @@ public class TerrainQuad extends Node implements Terrain {
         else if (quadrant == 4)
             return pQuad.getQuad(3);
         else if (quadrant == 1) {
-            TerrainQuad quad = pQuad.findTopQuad();
+            TerrainQuad quad = null;
+            if (useFinder)
+                quad = neighbourFinder.getTopQuad(this);
+            else
+                quad = pQuad.findTopQuad();
             if (quad != null)
                 return quad.getQuad(2);
         } else if (quadrant == 3) {
-            TerrainQuad quad = pQuad.findTopQuad();
+            TerrainQuad quad = null;
+            if (useFinder)
+                quad = neighbourFinder.getTopQuad(this);
+            else
+                quad = pQuad.findTopQuad();
             if (quad != null)
                 return quad.getQuad(4);
         }
@@ -1416,8 +1474,12 @@ public class TerrainQuad extends Node implements Terrain {
     }
 
     protected TerrainQuad findLeftQuad() {
+        boolean useFinder = false;
         if (getParent() == null || !(getParent() instanceof TerrainQuad))
-            return null;
+             if (neighbourFinder == null)
+                return null;
+            else
+                useFinder = true;
 
         TerrainQuad pQuad = (TerrainQuad) getParent();
 
@@ -1426,11 +1488,19 @@ public class TerrainQuad extends Node implements Terrain {
         else if (quadrant == 4)
             return pQuad.getQuad(2);
         else if (quadrant == 1) {
-            TerrainQuad quad = pQuad.findLeftQuad();
+            TerrainQuad quad = null;
+            if (useFinder)
+                quad = neighbourFinder.getLeftQuad(this);
+            else
+                quad = pQuad.findLeftQuad();
             if (quad != null)
                 return quad.getQuad(3);
         } else if (quadrant == 2) {
-            TerrainQuad quad = pQuad.findLeftQuad();
+            TerrainQuad quad = null;
+            if (useFinder)
+                quad = neighbourFinder.getLeftQuad(this);
+            else
+                quad = pQuad.findLeftQuad();
             if (quad != null)
                 return quad.getQuad(4);
         }

+ 294 - 0
engine/src/test/jme3test/terrain/TerrainTestTile.java

@@ -0,0 +1,294 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package jme3test.terrain;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.font.BitmapText;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.light.AmbientLight;
+import com.jme3.light.DirectionalLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Node;
+import com.jme3.terrain.ProgressMonitor;
+import com.jme3.terrain.Terrain;
+import com.jme3.terrain.geomipmap.NeighbourFinder;
+import com.jme3.terrain.geomipmap.TerrainLodControl;
+import com.jme3.terrain.geomipmap.TerrainQuad;
+import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapMode;
+import java.util.List;
+
+/**
+ * Demonstrates the NeighbourFinder interface for TerrainQuads,
+ * allowing you to tile terrains together without having to use
+ * TerrainGrid.
+ * 
+ * @author sploreg
+ */
+public class TerrainTestTile extends SimpleApplication {
+
+    private TiledTerrain terrain;
+    Material matTerrain;
+    Material matWire;
+    boolean wireframe = true;
+    boolean triPlanar = false;
+    boolean wardiso = false;
+    boolean minnaert = false;
+    protected BitmapText hintText;
+    private float grassScale = 256;
+    
+
+    public static void main(String[] args) {
+        TerrainTestTile app = new TerrainTestTile();
+        app.start();
+    }
+
+    
+    
+    @Override
+    public void simpleInitApp() {
+        loadHintText();
+        setupKeys();
+
+        // WIREFRAME material
+        matWire = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+        matWire.getAdditionalRenderState().setWireframe(true);
+        matWire.setColor("Color", ColorRGBA.Green);
+        
+        terrain = new TiledTerrain();
+        rootNode.attachChild(terrain);
+        
+        DirectionalLight light = new DirectionalLight();
+        light.setDirection((new Vector3f(-0.5f, -1f, -0.5f)).normalize());
+        rootNode.addLight(light);
+
+        AmbientLight ambLight = new AmbientLight();
+        ambLight.setColor(new ColorRGBA(1f, 1f, 0.8f, 0.2f));
+        rootNode.addLight(ambLight);
+
+        cam.setLocation(new Vector3f(0, 256, 0));
+        cam.lookAtDirection(new Vector3f(0, -1f, 0).normalizeLocal(), Vector3f.UNIT_X);
+    }
+    
+    public void loadHintText() {
+        hintText = new BitmapText(guiFont, false);
+        hintText.setLocalTranslation(0, getCamera().getHeight(), 0);
+        hintText.setText("Hit 'T' to toggle wireframe");
+        guiNode.attachChild(hintText);
+    }
+
+
+    private void setupKeys() {
+        flyCam.setMoveSpeed(100);
+        inputManager.addMapping("wireframe", new KeyTrigger(KeyInput.KEY_T));
+        inputManager.addListener(actionListener, "wireframe");
+    }
+    private ActionListener actionListener = new ActionListener() {
+
+        public void onAction(String name, boolean pressed, float tpf) {
+            if (name.equals("wireframe") && !pressed) {
+                wireframe = !wireframe;
+                if (!wireframe) {
+                    terrain.setMaterial(matWire);
+                } else {
+                    terrain.setMaterial(matTerrain);
+                }
+            }
+        }
+    };
+
+    /**
+     * A sample class (node in this case) that demonstrates
+     * the use of NeighbourFinder.
+     * It just links up the left,right,top,bottom TerrainQuads
+     * so LOD can work.
+     */
+    private class TiledTerrain extends Node implements Terrain, NeighbourFinder {
+
+        private TerrainQuad terrain1;
+        private TerrainQuad terrain2;
+        private TerrainQuad terrain3;
+        private TerrainQuad terrain4;
+        
+        TiledTerrain() {
+            // TERRAIN TEXTURE material
+            matTerrain = new Material(assetManager, "Common/MatDefs/Terrain/TerrainLighting.j3md");
+            matTerrain.setBoolean("useTriPlanarMapping", false);
+            matTerrain.setBoolean("WardIso", true);
+            matTerrain.setFloat("Shininess", 0);
+
+            // GRASS texture
+            Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg");
+            grass.setWrap(WrapMode.Repeat);
+            matTerrain.setTexture("DiffuseMap", grass);
+            matTerrain.setFloat("DiffuseMap_0_scale", grassScale);
+
+            // CREATE THE TERRAIN
+            terrain1 = new TerrainQuad("terrain", 65, 513, null);
+            TerrainLodControl control1 = new TerrainLodControl(terrain1, getCamera());
+            control1.setLodCalculator( new DistanceLodCalculator(65, 2.7f) ); // patch size, and a multiplier
+            terrain1.addControl(control1);
+            terrain1.setMaterial(matTerrain);
+            terrain1.setLocalTranslation(-256, -100, -256);
+            terrain1.setLocalScale(1f, 1f, 1f);
+            this.attachChild(terrain1);
+
+            terrain2 = new TerrainQuad("terrain", 65, 513, null);
+            TerrainLodControl control2 = new TerrainLodControl(terrain2, getCamera());
+            control2.setLodCalculator( new DistanceLodCalculator(65, 2.7f) ); // patch size, and a multiplier
+            terrain2.addControl(control2);
+            terrain2.setMaterial(matTerrain);
+            terrain2.setLocalTranslation(-256, -100, 256);
+            terrain2.setLocalScale(1f, 1f, 1f);
+            this.attachChild(terrain2);
+
+            terrain3 = new TerrainQuad("terrain", 65, 513, null);
+            TerrainLodControl control3 = new TerrainLodControl(terrain3, getCamera());
+            control3.setLodCalculator( new DistanceLodCalculator(65, 2.7f) ); // patch size, and a multiplier
+            terrain3.addControl(control3);
+            terrain3.setMaterial(matTerrain);
+            terrain3.setLocalTranslation(256, -100, -256);
+            terrain3.setLocalScale(1f, 1f, 1f);
+            this.attachChild(terrain3);
+
+            terrain4 = new TerrainQuad("terrain", 65, 513, null);
+            TerrainLodControl control4 = new TerrainLodControl(terrain4, getCamera());
+            control4.setLodCalculator( new DistanceLodCalculator(65, 2.7f) ); // patch size, and a multiplier
+            terrain4.addControl(control4);
+            terrain4.setMaterial(matTerrain);
+            terrain4.setLocalTranslation(256, -100, 256);
+            terrain4.setLocalScale(1f, 1f, 1f);
+            this.attachChild(terrain4);
+        }
+        
+        /**
+         * 1  3
+         * 2  4
+         */
+        public TerrainQuad getRightQuad(TerrainQuad center) {
+            if (center == terrain1)
+                return terrain3;
+            if (center == terrain2)
+                return terrain4;
+            
+            return null;
+        }
+
+        /**
+         * 1  3
+         * 2  4
+         */
+        public TerrainQuad getLeftQuad(TerrainQuad center) {
+            if (center == terrain3)
+                return terrain1;
+            if (center == terrain4)
+                return terrain2;
+            
+            return null;
+        }
+
+        /**
+         * 1  3
+         * 2  4
+         */
+        public TerrainQuad getTopQuad(TerrainQuad center) {
+            if (center == terrain2)
+                return terrain1;
+            if (center == terrain4)
+                return terrain3;
+            
+            return null;
+        }
+
+        /**
+         * 1  3
+         * 2  4
+         */
+        public TerrainQuad getDownQuad(TerrainQuad center) {
+            if (center == terrain1)
+                return terrain2;
+            if (center == terrain3)
+                return terrain4;
+            
+            return null;
+        }
+        
+        public float getHeight(Vector2f xz) {
+            // you will have to offset the coordinate for each terrain, to center on it
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public Vector3f getNormal(Vector2f xz) {
+            // you will have to offset the coordinate for each terrain, to center on it
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public float getHeightmapHeight(Vector2f xz) {
+            // you will have to offset the coordinate for each terrain, to center on it
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public void setHeight(Vector2f xzCoordinate, float height) {
+            // you will have to offset the coordinate for each terrain, to center on it
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public void setHeight(List<Vector2f> xz, List<Float> height) {
+            // you will have to offset the coordinate for each terrain, to center on it
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public void adjustHeight(Vector2f xzCoordinate, float delta) {
+            // you will have to offset the coordinate for each terrain, to center on it
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public void adjustHeight(List<Vector2f> xz, List<Float> height) {
+            // you will have to offset the coordinate for each terrain, to center on it
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public float[] getHeightMap() {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public int getMaxLod() {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public void setLocked(boolean locked) {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public void generateEntropy(ProgressMonitor monitor) {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public Material getMaterial() {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public Material getMaterial(Vector3f worldLocation) {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public int getTerrainSize() {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public int getNumMajorSubdivisions() {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        
+        
+    }
+}