diff --git a/engine/src/terrain/com/jme3/terrain/Terrain.java b/engine/src/terrain/com/jme3/terrain/Terrain.java index 85dd32e45..9c6cd76f8 100644 --- a/engine/src/terrain/com/jme3/terrain/Terrain.java +++ b/engine/src/terrain/com/jme3/terrain/Terrain.java @@ -34,7 +34,6 @@ package com.jme3.terrain; import com.jme3.material.Material; import com.jme3.math.Vector2f; import com.jme3.math.Vector3f; -import com.jme3.scene.Spatial; import java.util.List; /** @@ -107,20 +106,7 @@ public interface Terrain { * Infinite or "paged" terrains will not be able to support this, so use with caution. */ public float[] getHeightMap(); - - /** - * Tell the terrain system to use/not use Level of Detail algorithms. - * This is allowed to be ignored if a particular implementation cannot support it. - */ - public void useLOD(boolean useLod); - - /** - * Check if the terrain is using LOD techniques. - * If a terrain system only supports enabled LOD, then this - * should always return true. - */ - public boolean isUsingLOD(); - + /** * This is calculated by the specific LOD algorithm. * A value of one means that the terrain is showing full detail. @@ -130,24 +116,19 @@ public interface Terrain { public int getMaxLod(); /** - * Called in the update (pre or post, up to you) method of your game. + * Called by an LodControl. * Calculates the level of detail of the terrain and adjusts its geometry. * This is where the Terrain's LOD algorithm will change the detail of * the terrain based on how far away this position is from the particular * terrain patch. - * @param location often the Camera's location + * @param location: the Camera's location. A list of one camera location is normal + * if you just have one camera in your scene. */ public void update(List location); - /** - * Get the spatial instance of this Terrain. Right now just used in the - * terrain editor in JMP. - */ - public Spatial getSpatial(); - /** * Lock or unlock the meshes of this terrain. - * Locked meshes are uneditable but have better performance. + * Locked meshes are un-editable but have better performance. * This should call the underlying getMesh().setStatic()/setDynamic() methods. * @param locked or unlocked */ @@ -169,17 +150,15 @@ public interface Terrain { public Material getMaterial(); /** - * Calculates the percentage along the terrain (in X-Z plane) that the - * supplied point (worldX,worldY) is, starting from the x=0, z=0 world - * position of the terrain. - * This method must take into account local translations and scale of the terrain. - * Used for painting onto an alpha image for texture splatting. + * Used for painting to get the number of vertices along the edge of the + * terrain. + * This is an un-scaled size, and should represent the vertex count (ie. the + * texture coord count) along an edge of a square terrain. * - * @param worldX world position on X axis - * @param worldY world position on Z axis - * @return a point (U,V in the range [0,1] ) + * In the standard TerrainQuad default implementation, this will return + * the "totalSize" of the terrain (512 or so). */ - public Vector2f getPointPercentagePosition(float worldX, float worldY); + public int getTerrainSize(); /** * Get the scale of the texture coordinates. Normally if the texture is diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainLodControl.java b/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainLodControl.java index 9cd2c82c2..ab68ce037 100644 --- a/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainLodControl.java +++ b/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainLodControl.java @@ -41,6 +41,7 @@ import java.util.List; import com.jme3.renderer.Camera; import com.jme3.renderer.RenderManager; import com.jme3.renderer.ViewPort; +import com.jme3.scene.Node; import com.jme3.scene.Spatial; import com.jme3.scene.control.AbstractControl; import com.jme3.scene.control.Control; @@ -61,22 +62,27 @@ import java.util.ArrayList; */ public class TerrainLodControl extends AbstractControl { - private TerrainQuad terrain; + private Terrain terrain; private List cameras; private List cameraLocations = new ArrayList(); public TerrainLodControl() { } + public TerrainLodControl(Terrain terrain, Camera camera) { + List cams = new ArrayList(); + cams.add(camera); + this.terrain = terrain; + this.cameras = cams; + } + /** * Only uses the first camera right now. * @param terrain to act upon (must be a Spatial) * @param cameras one or more cameras to reference for LOD calc */ public TerrainLodControl(Terrain terrain, List cameras) { - if (terrain instanceof TerrainQuad) { - this.terrain = (TerrainQuad) terrain; - } + this.terrain = terrain; this.cameras = cameras; } @@ -107,11 +113,17 @@ public class TerrainLodControl extends AbstractControl { cameraClone.add(c); } } - return new TerrainLodControl((TerrainQuad) spatial, cameraClone); + return new TerrainLodControl((Terrain) spatial, cameraClone); } return null; } + public void setCamera(Camera camera) { + List cams = new ArrayList(); + cams.add(camera); + setCameras(cams); + } + public void setCameras(List cameras) { this.cameras = cameras; cameraLocations.clear(); @@ -123,12 +135,12 @@ public class TerrainLodControl extends AbstractControl { @Override public void setSpatial(Spatial spatial) { super.setSpatial(spatial); - if (spatial instanceof TerrainQuad) { - this.terrain = (TerrainQuad) spatial; + if (spatial instanceof Terrain) { + this.terrain = (Terrain) spatial; } } - public void setTerrain(TerrainQuad terrain) { + public void setTerrain(Terrain terrain) { this.terrain = terrain; } @@ -136,13 +148,13 @@ public class TerrainLodControl extends AbstractControl { public void write(JmeExporter ex) throws IOException { super.write(ex); OutputCapsule oc = ex.getCapsule(this); - oc.write(terrain, "terrain", null); + oc.write((Node)terrain, "terrain", null); } @Override public void read(JmeImporter im) throws IOException { super.read(im); InputCapsule ic = im.getCapsule(this); - terrain = (TerrainQuad) ic.readSavable("terrain", null); + terrain = (Terrain) ic.readSavable("terrain", null); } } diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainQuad.java b/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainQuad.java index dc0b5d7d3..bb0643b32 100644 --- a/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainQuad.java +++ b/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainQuad.java @@ -67,7 +67,6 @@ import com.jme3.util.TangentBinormalGenerator; import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.logging.Logger; /** * A terrain quad is a node in the quad tree of the terrain system. @@ -99,7 +98,6 @@ public class TerrainQuad extends Node implements Terrain { protected List lastCameraLocations; // used for LOD calc private boolean lodCalcRunning = false; - private boolean usingLOD = true; private int maxLod = -1; private HashMap updatedPatches; private final Object updatePatchesLock = new Object(); @@ -274,10 +272,6 @@ public class TerrainQuad extends Node implements Terrain { return 0; } - public Spatial getSpatial() { - return this; - } - public void generateEntropy(ProgressMonitor progressMonitor) { // only check this on the root quad if (isRootQuad()) @@ -312,11 +306,11 @@ public class TerrainQuad extends Node implements Terrain { public Material getMaterial() { // get the material from one of the children. They all share the same material if (children != null) { - for (int i = children.size(); --i >= 0;) { - Spatial child = children.get(i); - if (child instanceof TerrainQuad) { - return ((TerrainQuad)child).getMaterial(); - } else if (child instanceof TerrainPatch) { + for (int i = children.size(); --i >= 0;) { + Spatial child = children.get(i); + if (child instanceof TerrainQuad) { + return ((TerrainQuad)child).getMaterial(); + } else if (child instanceof TerrainPatch) { return ((TerrainPatch)child).getMaterial(); } } @@ -383,12 +377,11 @@ public class TerrainQuad extends Node implements Terrain { */ private void updateQuadLODs() { synchronized (updatePatchesLock) { - //if (true) - // return; + if (updatedPatches == null || updatedPatches.isEmpty()) return; - //TODO do the actual geometry update here + // do the actual geometry update here for (UpdatedTerrainPatch utp : updatedPatches.values()) { utp.updateAll(); } @@ -582,7 +575,7 @@ public class TerrainQuad extends Node implements Terrain { offsetAmount += quarterSize; if (lodCalculatorFactory == null) - lodCalculatorFactory = new LodDistanceCalculatorFactory(); // set a default one + lodCalculatorFactory = new LodDistanceCalculatorFactory(); // set a default one // 1 upper left float[] heightBlock1 = createHeightSubBlock(heightMap, 0, 0, split); @@ -690,7 +683,7 @@ public class TerrainQuad extends Node implements Terrain { int split = (size + 1) >> 1; if (lodCalculatorFactory == null) - lodCalculatorFactory = new LodDistanceCalculatorFactory(); // set a default one + lodCalculatorFactory = new LodDistanceCalculatorFactory(); // set a default one offsetAmount += quarterSize; @@ -1104,13 +1097,9 @@ public class TerrainQuad extends Node implements Terrain { return (x >= 0 && x <= totalSize && z >= 0 && z <= totalSize); } - public Vector2f getPointPercentagePosition(float worldX, float worldY) { - Vector2f uv = new Vector2f(worldX,worldY); - uv.subtractLocal(getLocalTranslation().x, getLocalTranslation().z); // center it on 0,0 - uv.addLocal(totalSize/2, totalSize/2); // shift the bottom left corner up to 0,0 - uv.divideLocal(totalSize); // get the location as a percentage - - return uv; + + public int getTerrainSize() { + return totalSize; } @@ -1510,10 +1499,6 @@ public class TerrainQuad extends Node implements Terrain { quadrant = c.readInt("quadrant", 0); totalSize = c.readInt("totalSize", 0); lodCalculatorFactory = (LodCalculatorFactory) c.readSavable("lodCalculatorFactory", null); - - // the terrain is re-built on load, so we need to run this once - //affectedAreaBBox = new BoundingBox(new Vector3f(0,0,0), size, Float.MAX_VALUE, size); - //updateNormals(); } @Override @@ -1564,14 +1549,6 @@ public class TerrainQuad extends Node implements Terrain { return maxLod; } - public void useLOD(boolean useLod) { - usingLOD = useLod; - } - - public boolean isUsingLOD() { - return usingLOD; -} - public int getPatchSize() { return patchSize; } @@ -1583,9 +1560,6 @@ public class TerrainQuad extends Node implements Terrain { public float[] getHeightMap() { - //if (true) - // return heightMap; - float[] hm = null; int length = ((size-1)/2)+1; int area = size*size; diff --git a/engine/src/test/jme3test/terrain/TerrainTestReadWrite.java b/engine/src/test/jme3test/terrain/TerrainTestReadWrite.java index 6bd5ff852..0b0a376cf 100644 --- a/engine/src/test/jme3test/terrain/TerrainTestReadWrite.java +++ b/engine/src/test/jme3test/terrain/TerrainTestReadWrite.java @@ -33,6 +33,7 @@ package jme3test.terrain; import com.jme3.app.SimpleApplication; import com.jme3.bounding.BoundingBox; +import com.jme3.export.Savable; import com.jme3.export.binary.BinaryExporter; import com.jme3.export.binary.BinaryImporter; import com.jme3.font.BitmapText; @@ -44,6 +45,8 @@ import com.jme3.material.Material; import com.jme3.math.ColorRGBA; import com.jme3.math.Vector3f; import com.jme3.renderer.Camera; +import com.jme3.scene.Node; +import com.jme3.terrain.Terrain; import com.jme3.terrain.geomipmap.TerrainLodControl; import com.jme3.terrain.geomipmap.TerrainQuad; import com.jme3.terrain.heightmap.AbstractHeightMap; @@ -69,7 +72,7 @@ import jme3tools.converters.ImageToAwt; */ public class TerrainTestReadWrite extends SimpleApplication { - private TerrainQuad terrain; + private Terrain terrain; protected BitmapText hintText; private float grassScale = 64; private float dirtScale = 16; @@ -147,8 +150,6 @@ public class TerrainTestReadWrite extends SimpleApplication { // CREATE HEIGHTMAP AbstractHeightMap heightmap = null; try { - //heightmap = new HillHeightMap(1025, 1000, 50, 100, (byte) 3); - heightmap = new ImageBasedHeightMap(ImageToAwt.convert(heightMapImage.getImage(), false, true, 0), 1f); heightmap.load(); @@ -160,15 +161,17 @@ public class TerrainTestReadWrite extends SimpleApplication { loadTerrain(); } else { // create the terrain as normal, and give it a control for LOD management - terrain = new TerrainQuad("terrain", 65, 513, heightmap.getHeightMap());//, new LodPerspectiveCalculatorFactory(getCamera(), 4)); // add this in to see it use entropy for LOD calculations + TerrainQuad terrainQuad = new TerrainQuad("terrain", 65, 129, heightmap.getHeightMap());//, new LodPerspectiveCalculatorFactory(getCamera(), 4)); // add this in to see it use entropy for LOD calculations List cameras = new ArrayList(); cameras.add(getCamera()); - TerrainLodControl control = new TerrainLodControl(terrain, cameras); - terrain.addControl(control); - terrain.setMaterial(matTerrain); - terrain.setLocalTranslation(0, -100, 0); - terrain.setLocalScale(2f, 1f, 2f); - rootNode.attachChild(terrain); + TerrainLodControl control = new TerrainLodControl(terrainQuad, cameras); + terrainQuad.addControl(control); + terrainQuad.setMaterial(matTerrain); + terrainQuad.setLocalTranslation(0, -100, 0); + terrainQuad.setLocalScale(4f, 0.25f, 4f); + rootNode.attachChild(terrainQuad); + + this.terrain = terrainQuad; } DirectionalLight light = new DirectionalLight(); @@ -211,7 +214,7 @@ public class TerrainTestReadWrite extends SimpleApplication { fos = new FileOutputStream(new File("terrainsave.jme")); // we just use the exporter and pass in the terrain - BinaryExporter.getInstance().save(terrain, new BufferedOutputStream(fos)); + BinaryExporter.getInstance().save((Savable)terrain, new BufferedOutputStream(fos)); fos.flush(); float duration = (System.currentTimeMillis() - start) / 1000.0f; @@ -237,29 +240,28 @@ public class TerrainTestReadWrite extends SimpleApplication { long start = System.currentTimeMillis(); // remove the existing terrain and detach it from the root node. if (terrain != null) { - terrain.removeFromParent(); - terrain.removeControl(TerrainLodControl.class); - terrain.detachAllChildren(); + Node existingTerrain = (Node)terrain; + existingTerrain.removeFromParent(); + existingTerrain.removeControl(TerrainLodControl.class); + existingTerrain.detachAllChildren(); terrain = null; } // import the saved terrain, and attach it back to the root node - fis = new FileInputStream(new File("terrainsave.jme")); + File f = new File("terrainsave.jme"); + fis = new FileInputStream(f); BinaryImporter imp = BinaryImporter.getInstance(); imp.setAssetManager(assetManager); terrain = (TerrainQuad) imp.load(new BufferedInputStream(fis)); - rootNode.attachChild(terrain); + rootNode.attachChild((Node)terrain); float duration = (System.currentTimeMillis() - start) / 1000.0f; System.out.println("Load took " + duration + " seconds"); - // now we have to add back the cameras to the LOD control, since we didn't want to duplicate them on save - List cameras = new ArrayList(); - cameras.add(getCamera()); - TerrainLodControl lodControl = terrain.getControl(TerrainLodControl.class); - if (lodControl != null) { - lodControl.setCameras(cameras); - } + // now we have to add back the camera to the LOD control + TerrainLodControl lodControl = ((Node)terrain).getControl(TerrainLodControl.class); + if (lodControl != null) + lodControl.setCamera(getCamera()); } catch (IOException ex) { Logger.getLogger(TerrainTestReadWrite.class.getName()).log(Level.SEVERE, null, ex); @@ -286,10 +288,10 @@ public class TerrainTestReadWrite extends SimpleApplication { public void onAction(String name, boolean pressed, float tpf) { if (name.equals("clone") && !pressed) { - TerrainQuad clone = terrain.clone(); - terrain.removeFromParent(); + Terrain clone = (Terrain) ((Node)terrain).clone(); + ((Node)terrain).removeFromParent(); terrain = clone; - getRootNode().attachChild(terrain); + getRootNode().attachChild((Node)terrain); } } };