import com.jme3.material.Material;\r
import com.jme3.math.Vector2f;\r
import com.jme3.math.Vector3f;\r
-import com.jme3.scene.Spatial;\r
import java.util.List;\r
\r
/**\r
* Infinite or "paged" terrains will not be able to support this, so use with caution.\r
*/\r
public float[] getHeightMap();\r
-\r
- /**\r
- * Tell the terrain system to use/not use Level of Detail algorithms.\r
- * This is allowed to be ignored if a particular implementation cannot support it.\r
- */\r
- public void useLOD(boolean useLod);\r
-\r
- /**\r
- * Check if the terrain is using LOD techniques.\r
- * If a terrain system only supports enabled LOD, then this\r
- * should always return true.\r
- */\r
- public boolean isUsingLOD();\r
-\r
+ \r
/**\r
* This is calculated by the specific LOD algorithm.\r
* A value of one means that the terrain is showing full detail.\r
public int getMaxLod();\r
\r
/**\r
- * Called in the update (pre or post, up to you) method of your game.\r
+ * Called by an LodControl.\r
* Calculates the level of detail of the terrain and adjusts its geometry.\r
* This is where the Terrain's LOD algorithm will change the detail of\r
* the terrain based on how far away this position is from the particular\r
* terrain patch.\r
- * @param location often the Camera's location\r
+ * @param location: the Camera's location. A list of one camera location is normal \r
+ * if you just have one camera in your scene.\r
*/\r
public void update(List<Vector3f> location);\r
\r
/**\r
- * Get the spatial instance of this Terrain. Right now just used in the \r
- * terrain editor in JMP.\r
- */\r
- public Spatial getSpatial();\r
-\r
- /**\r
* Lock or unlock the meshes of this terrain.\r
- * Locked meshes are uneditable but have better performance.\r
+ * Locked meshes are un-editable but have better performance.\r
* This should call the underlying getMesh().setStatic()/setDynamic() methods.\r
* @param locked or unlocked\r
*/\r
public Material getMaterial();\r
\r
/**\r
- * Calculates the percentage along the terrain (in X-Z plane) that the\r
- * supplied point (worldX,worldY) is, starting from the x=0, z=0 world\r
- * position of the terrain.\r
- * This method must take into account local translations and scale of the terrain.\r
- * Used for painting onto an alpha image for texture splatting.\r
+ * Used for painting to get the number of vertices along the edge of the\r
+ * terrain.\r
+ * This is an un-scaled size, and should represent the vertex count (ie. the\r
+ * texture coord count) along an edge of a square terrain.\r
* \r
- * @param worldX world position on X axis\r
- * @param worldY world position on Z axis\r
- * @return a point (U,V in the range [0,1] )\r
+ * In the standard TerrainQuad default implementation, this will return\r
+ * the "totalSize" of the terrain (512 or so).\r
*/\r
- public Vector2f getPointPercentagePosition(float worldX, float worldY);\r
+ public int getTerrainSize();\r
\r
/**\r
* Get the scale of the texture coordinates. Normally if the texture is\r
import com.jme3.renderer.Camera;\r
import com.jme3.renderer.RenderManager;\r
import com.jme3.renderer.ViewPort;\r
+import com.jme3.scene.Node;\r
import com.jme3.scene.Spatial;\r
import com.jme3.scene.control.AbstractControl;\r
import com.jme3.scene.control.Control;\r
*/\r
public class TerrainLodControl extends AbstractControl {\r
\r
- private TerrainQuad terrain;\r
+ private Terrain terrain;\r
private List<Camera> cameras;\r
private List<Vector3f> cameraLocations = new ArrayList<Vector3f>();\r
\r
public TerrainLodControl() {\r
}\r
\r
+ public TerrainLodControl(Terrain terrain, Camera camera) {\r
+ List<Camera> cams = new ArrayList<Camera>();\r
+ cams.add(camera);\r
+ this.terrain = terrain;\r
+ this.cameras = cams;\r
+ }\r
+ \r
/**\r
* Only uses the first camera right now.\r
* @param terrain to act upon (must be a Spatial)\r
* @param cameras one or more cameras to reference for LOD calc\r
*/\r
public TerrainLodControl(Terrain terrain, List<Camera> cameras) {\r
- if (terrain instanceof TerrainQuad) {\r
- this.terrain = (TerrainQuad) terrain;\r
- }\r
+ this.terrain = terrain;\r
this.cameras = cameras;\r
}\r
\r
cameraClone.add(c);\r
}\r
}\r
- return new TerrainLodControl((TerrainQuad) spatial, cameraClone);\r
+ return new TerrainLodControl((Terrain) spatial, cameraClone);\r
}\r
return null;\r
}\r
\r
+ public void setCamera(Camera camera) {\r
+ List<Camera> cams = new ArrayList<Camera>();\r
+ cams.add(camera);\r
+ setCameras(cams);\r
+ }\r
+ \r
public void setCameras(List<Camera> cameras) {\r
this.cameras = cameras;\r
cameraLocations.clear();\r
@Override\r
public void setSpatial(Spatial spatial) {\r
super.setSpatial(spatial);\r
- if (spatial instanceof TerrainQuad) {\r
- this.terrain = (TerrainQuad) spatial;\r
+ if (spatial instanceof Terrain) {\r
+ this.terrain = (Terrain) spatial;\r
}\r
}\r
\r
- public void setTerrain(TerrainQuad terrain) {\r
+ public void setTerrain(Terrain terrain) {\r
this.terrain = terrain;\r
}\r
\r
public void write(JmeExporter ex) throws IOException {\r
super.write(ex);\r
OutputCapsule oc = ex.getCapsule(this);\r
- oc.write(terrain, "terrain", null);\r
+ oc.write((Node)terrain, "terrain", null);\r
}\r
\r
@Override\r
public void read(JmeImporter im) throws IOException {\r
super.read(im);\r
InputCapsule ic = im.getCapsule(this);\r
- terrain = (TerrainQuad) ic.readSavable("terrain", null);\r
+ terrain = (Terrain) ic.readSavable("terrain", null);\r
}\r
}\r
import java.util.ArrayList;\r
import java.util.List;\r
import java.util.Map;\r
-import java.util.logging.Logger;\r
\r
/**\r
* A terrain quad is a node in the quad tree of the terrain system.\r
\r
protected List<Vector3f> lastCameraLocations; // used for LOD calc\r
private boolean lodCalcRunning = false;\r
- private boolean usingLOD = true;\r
private int maxLod = -1;\r
private HashMap<String,UpdatedTerrainPatch> updatedPatches;\r
private final Object updatePatchesLock = new Object();\r
return 0;\r
}\r
\r
- public Spatial getSpatial() {\r
- return this;\r
- }\r
-\r
public void generateEntropy(ProgressMonitor progressMonitor) {\r
// only check this on the root quad\r
if (isRootQuad())\r
public Material getMaterial() {\r
// get the material from one of the children. They all share the same material\r
if (children != null) {\r
- for (int i = children.size(); --i >= 0;) {\r
- Spatial child = children.get(i);\r
- if (child instanceof TerrainQuad) {\r
- return ((TerrainQuad)child).getMaterial();\r
- } else if (child instanceof TerrainPatch) {\r
+ for (int i = children.size(); --i >= 0;) {\r
+ Spatial child = children.get(i);\r
+ if (child instanceof TerrainQuad) {\r
+ return ((TerrainQuad)child).getMaterial();\r
+ } else if (child instanceof TerrainPatch) {\r
return ((TerrainPatch)child).getMaterial();\r
}\r
}\r
*/\r
private void updateQuadLODs() {\r
synchronized (updatePatchesLock) {\r
- //if (true)\r
- // return;\r
+ \r
if (updatedPatches == null || updatedPatches.isEmpty())\r
return;\r
\r
- //TODO do the actual geometry update here\r
+ // do the actual geometry update here\r
for (UpdatedTerrainPatch utp : updatedPatches.values()) {\r
utp.updateAll();\r
}\r
offsetAmount += quarterSize;\r
\r
if (lodCalculatorFactory == null)\r
- lodCalculatorFactory = new LodDistanceCalculatorFactory(); // set a default one\r
+ lodCalculatorFactory = new LodDistanceCalculatorFactory(); // set a default one\r
\r
// 1 upper left\r
float[] heightBlock1 = createHeightSubBlock(heightMap, 0, 0, split);\r
int split = (size + 1) >> 1;\r
\r
if (lodCalculatorFactory == null)\r
- lodCalculatorFactory = new LodDistanceCalculatorFactory(); // set a default one\r
+ lodCalculatorFactory = new LodDistanceCalculatorFactory(); // set a default one\r
\r
offsetAmount += quarterSize;\r
\r
return (x >= 0 && x <= totalSize && z >= 0 && z <= totalSize);\r
}\r
\r
- public Vector2f getPointPercentagePosition(float worldX, float worldY) {\r
- Vector2f uv = new Vector2f(worldX,worldY);\r
- uv.subtractLocal(getLocalTranslation().x, getLocalTranslation().z); // center it on 0,0\r
- uv.addLocal(totalSize/2, totalSize/2); // shift the bottom left corner up to 0,0\r
- uv.divideLocal(totalSize); // get the location as a percentage\r
-\r
- return uv;\r
+ \r
+ public int getTerrainSize() {\r
+ return totalSize;\r
}\r
\r
\r
quadrant = c.readInt("quadrant", 0);\r
totalSize = c.readInt("totalSize", 0);\r
lodCalculatorFactory = (LodCalculatorFactory) c.readSavable("lodCalculatorFactory", null);\r
-\r
- // the terrain is re-built on load, so we need to run this once\r
- //affectedAreaBBox = new BoundingBox(new Vector3f(0,0,0), size, Float.MAX_VALUE, size);\r
- //updateNormals();\r
}\r
\r
@Override\r
return maxLod;\r
}\r
\r
- public void useLOD(boolean useLod) {\r
- usingLOD = useLod;\r
- }\r
-\r
- public boolean isUsingLOD() {\r
- return usingLOD;\r
-}\r
-\r
public int getPatchSize() {\r
return patchSize;\r
}\r
\r
public float[] getHeightMap() {\r
\r
- //if (true)\r
- // return heightMap;\r
-\r
float[] hm = null;\r
int length = ((size-1)/2)+1;\r
int area = size*size;\r
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;
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;
*/
public class TerrainTestReadWrite extends SimpleApplication {
- private TerrainQuad terrain;
+ private Terrain terrain;
protected BitmapText hintText;
private float grassScale = 64;
private float dirtScale = 16;
// 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();
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();
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;
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);
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);
}
}
};