* Cleaned up and simplified the Terrain API.

* Removed dependence on TerrainQuad for TerrainLodControl
* Comments, general cleanup

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@7783 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
3.0
bre..ns 14 years ago
parent 7cb3ca4712
commit 70f1a6f323
  1. 43
      engine/src/terrain/com/jme3/terrain/Terrain.java
  2. 32
      engine/src/terrain/com/jme3/terrain/geomipmap/TerrainLodControl.java
  3. 34
      engine/src/terrain/com/jme3/terrain/geomipmap/TerrainQuad.java
  4. 54
      engine/src/test/jme3test/terrain/TerrainTestReadWrite.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;
/**
@ -108,19 +107,6 @@ public interface Terrain {
*/
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<Vector3f> 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

@ -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<Camera> cameras;
private List<Vector3f> cameraLocations = new ArrayList<Vector3f>();
public TerrainLodControl() {
}
public TerrainLodControl(Terrain terrain, Camera camera) {
List<Camera> cams = new ArrayList<Camera>();
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<Camera> 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<Camera> cams = new ArrayList<Camera>();
cams.add(camera);
setCameras(cams);
}
public void setCameras(List<Camera> 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);
}
}

@ -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<Vector3f> lastCameraLocations; // used for LOD calc
private boolean lodCalcRunning = false;
private boolean usingLOD = true;
private int maxLod = -1;
private HashMap<String,UpdatedTerrainPatch> 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())
@ -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();
}
@ -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;

@ -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<Camera> cameras = new ArrayList<Camera>();
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<Camera> cameras = new ArrayList<Camera>();
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);
}
}
};

Loading…
Cancel
Save