* performance improvements to terrain height modification

* api change to let you pass in many points to be adjusted in the terrain

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@7643 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
3.0
bre..ns 14 years ago
parent 026b7f2211
commit 993b220922
  1. 20
      engine/src/terrain/com/jme3/terrain/Terrain.java
  2. 21
      engine/src/terrain/com/jme3/terrain/geomipmap/TerrainPatch.java
  3. 215
      engine/src/terrain/com/jme3/terrain/geomipmap/TerrainQuad.java
  4. 20
      engine/src/test/jme3test/terrain/TerrainTestModifyHeight.java

@ -75,6 +75,16 @@ public interface Terrain {
*/
public void setHeight(Vector2f xzCoordinate, float height);
/**
* Set the height at many points. The two lists must be the same size.
* Each xz coordinate entry matches to a height entry, 1 for 1. So the
* first coordinate matches to the first height value, the last to the
* last etc.
* @param xz a list of coordinates where the hight will be set
* @param height the heights that match the xz coordinates
*/
public void setHeight(List<Vector2f> xz, List<Float> height);
/**
* Raise/lower the height in one call (instead of getHeight then setHeight).
* @param xzCoordinate world coordinate to adjust the terrain height
@ -82,6 +92,16 @@ public interface Terrain {
*/
public void adjustHeight(Vector2f xzCoordinate, float delta);
/**
* Raise/lower the height at many points. The two lists must be the same size.
* Each xz coordinate entry matches to a height entry, 1 for 1. So the
* first coordinate matches to the first height value, the last to the
* last etc.
* @param xz a list of coordinates where the hight will be adjusted
* @param height +- value to adjust the height by, that matches the xz coordinates
*/
public void adjustHeight(List<Vector2f> xz, List<Float> height);
/**
* Get the heightmap of the entire terrain.
* This can return null if that terrain object does not store the height data.

@ -55,6 +55,7 @@ import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.VertexBuffer;
import com.jme3.scene.VertexBuffer.Type;
import com.jme3.terrain.geomipmap.TerrainQuad.LocationHeight;
import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
import com.jme3.terrain.geomipmap.lodcalc.LodCalculator;
import com.jme3.terrain.geomipmap.lodcalc.LodCalculatorFactory;
@ -306,16 +307,24 @@ public class TerrainPatch extends Geometry {
return geomap.getGridTrianglesAtPoint(x, z, getWorldScale() , getWorldTranslation());
}
public void setHeight(float x, float z, float height) {
if (x < 0 || z < 0 || x >= size || z >= size)
return;
int idx = (int) (z * size + x);
geomap.getHeightData().put(idx, height);
protected void setHeight(List<LocationHeight> locationHeights, boolean overrideHeight) {
for (LocationHeight lh : locationHeights) {
if (lh.x < 0 || lh.z < 0 || lh.x >= size || lh.z >= size)
continue;
int idx = lh.z * size + lh.x;
if (overrideHeight) {
geomap.getHeightData().put(idx, lh.h);
} else {
float h = getMesh().getFloatBuffer(Type.Position).get(idx*3+1);
geomap.getHeightData().put(idx, h+lh.h);
}
}
FloatBuffer newVertexBuffer = geomap.writeVertexArray(null, stepScale, false);
getMesh().clearBuffer(Type.Position);
getMesh().setBuffer(Type.Position, 3, newVertexBuffer);
// normals are updated from the terrain controller on update()
}
public void adjustHeight(float x, float z, float delta) {

@ -860,8 +860,8 @@ public class TerrainQuad extends Node implements Terrain {
public float getHeightmapHeight(Vector2f xz) {
// offset
int x = Math.round((xz.x / getLocalScale().x) + totalSize / 2);
int z = Math.round((xz.y / getLocalScale().z) + totalSize / 2);
int x = Math.round((xz.x / getLocalScale().x) + (float)totalSize / 2f);
int z = Math.round((xz.y / getLocalScale().z) + (float)totalSize / 2f);
return getHeightmapHeight(x, z);
}
@ -946,99 +946,84 @@ public class TerrainQuad extends Node implements Terrain {
return 0;
}
// the coord calculations should be the same as getHeight()
public void setHeight(Vector2f xz, float height) {
// offset
int x = Math.round((xz.x / getLocalScale().x) + totalSize / 2);
int z = Math.round((xz.y / getLocalScale().z) + totalSize / 2);
List<Vector2f> coord = new ArrayList<Vector2f>();
coord.add(xz);
List<Float> h = new ArrayList<Float>();
h.add(height);
setHeight(x, z, height); // adjust the actual mesh
setNormalRecalcNeeded(xz);
setHeight(coord, h);
}
protected void setHeight(int x, int z, float newVal) {
int quad = findQuadrant(x, z);
int split = (size + 1) >> 1;
if (children != null) {
for (int i = children.size(); --i >= 0;) {
Spatial spat = children.get(i);
int col = x;
int row = z;
boolean match = false;
public void adjustHeight(Vector2f xz, float delta) {
List<Vector2f> coord = new ArrayList<Vector2f>();
coord.add(xz);
List<Float> h = new ArrayList<Float>();
h.add(delta);
// get the childs quadrant
int childQuadrant = 0;
if (spat instanceof TerrainQuad) {
childQuadrant = ((TerrainQuad) spat).getQuadrant();
} else if (spat instanceof TerrainPatch) {
childQuadrant = ((TerrainPatch) spat).getQuadrant();
adjustHeight(coord, h);
}
if (childQuadrant == 1 && (quad & 1) != 0) {
match = true;
} else if (childQuadrant == 2 && (quad & 2) != 0) {
row = z - split + 1;
match = true;
} else if (childQuadrant == 3 && (quad & 4) != 0) {
col = x - split + 1;
match = true;
} else if (childQuadrant == 4 && (quad & 8) != 0) {
col = x - split + 1;
row = z - split + 1;
match = true;
public void setHeight(List<Vector2f> xz, List<Float> height) {
setHeight(xz, height, true);
}
if (match) {
if (spat instanceof TerrainQuad) {
((TerrainQuad) spat).setHeight(col, row, newVal);
} else if (spat instanceof TerrainPatch) {
((TerrainPatch) spat).setHeight(col, row, newVal);
}
public void adjustHeight(List<Vector2f> xz, List<Float> height) {
setHeight(xz, height, false);
}
}
}
}
protected void setHeight(List<Vector2f> xz, List<Float> height, boolean overrideHeight) {
if (xz.size() != height.size())
throw new IllegalArgumentException("Both lists must be the same length!");
protected boolean isPointOnTerrain(int x, int z) {
return (x >= 0 && x <= totalSize && z >= 0 && z <= totalSize);
}
int halfSize = totalSize / 2;
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
List<LocationHeight> locations = new ArrayList<LocationHeight>();
return uv;
// offset
for (int i=0; i<xz.size(); i++) {
int x = Math.round((xz.get(i).x / getLocalScale().x) + halfSize);
int z = Math.round((xz.get(i).y / getLocalScale().z) + halfSize);
locations.add(new LocationHeight(x,z,height.get(i)));
}
setHeight(locations, overrideHeight); // adjust height of the actual mesh
public void adjustHeight(Vector2f xz, float delta) {
int x = Math.round((xz.x / getLocalScale().x) + totalSize / 2);
int z = Math.round((xz.y / getLocalScale().z) + totalSize / 2);
// signal that the normals need updating
for (int i=0; i<xz.size(); i++)
setNormalRecalcNeeded(xz.get(i) );
}
if (!isPointOnTerrain(x,z))
return;
protected class LocationHeight {
int x;
int z;
float h;
adjustHeight(x, z,delta);
LocationHeight(){}
setNormalRecalcNeeded(xz);
LocationHeight(int x, int z, float h){
this.x = x;
this.z = z;
this.h = h;
}
}
protected void adjustHeight(int x, int z, float delta) {
int quad = findQuadrant(x, z);
int split = (size + 1) >> 1;
if (children != null) {
protected void setHeight(List<LocationHeight> locations, boolean overrideHeight) {
if (children == null)
return;
List<LocationHeight> quadLH1 = new ArrayList<LocationHeight>();
List<LocationHeight> quadLH2 = new ArrayList<LocationHeight>();
List<LocationHeight> quadLH3 = new ArrayList<LocationHeight>();
List<LocationHeight> quadLH4 = new ArrayList<LocationHeight>();
Spatial quad1 = null;
Spatial quad2 = null;
Spatial quad3 = null;
Spatial quad4 = null;
// get the child quadrants
for (int i = children.size(); --i >= 0;) {
Spatial spat = children.get(i);
int col = x;
int row = z;
boolean match = false;
// get the childs quadrant
int childQuadrant = 0;
if (spat instanceof TerrainQuad) {
childQuadrant = ((TerrainQuad) spat).getQuadrant();
@ -1046,30 +1031,84 @@ public class TerrainQuad extends Node implements Terrain {
childQuadrant = ((TerrainPatch) spat).getQuadrant();
}
if (childQuadrant == 1 && (quad & 1) != 0) {
match = true;
} else if (childQuadrant == 2 && (quad & 2) != 0) {
row = z - split + 1;
match = true;
} else if (childQuadrant == 3 && (quad & 4) != 0) {
col = x - split + 1;
match = true;
} else if (childQuadrant == 4 && (quad & 8) != 0) {
col = x - split + 1;
row = z - split + 1;
match = true;
if (childQuadrant == 1)
quad1 = spat;
else if (childQuadrant == 2)
quad2 = spat;
else if (childQuadrant == 3)
quad3 = spat;
else if (childQuadrant == 4)
quad4 = spat;
}
if (match) {
if (spat instanceof TerrainQuad) {
((TerrainQuad) spat).adjustHeight(col, row, delta);
} else if (spat instanceof TerrainPatch) {
((TerrainPatch) spat).adjustHeight(col, row, delta);
int split = (size + 1) >> 1;
// 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;
if ((quad & 1) != 0) {
quadLH1.add(lh);
}
if ((quad & 2) != 0) {
row = lh.z - split + 1;
quadLH2.add(new LocationHeight(lh.x, row, lh.h));
}
if ((quad & 4) != 0) {
col = lh.x - split + 1;
quadLH3.add(new LocationHeight(col, lh.z, lh.h));
}
if ((quad & 8) != 0) {
col = lh.x - split + 1;
row = lh.z - split + 1;
quadLH4.add(new LocationHeight(col, row, lh.h));
}
}
// send the locations to the children
if (!quadLH1.isEmpty()) {
if (quad1 instanceof TerrainQuad)
((TerrainQuad)quad1).setHeight(quadLH1, overrideHeight);
else if(quad1 instanceof TerrainPatch)
((TerrainPatch)quad1).setHeight(quadLH1, overrideHeight);
}
if (!quadLH2.isEmpty()) {
if (quad2 instanceof TerrainQuad)
((TerrainQuad)quad2).setHeight(quadLH2, overrideHeight);
else if(quad2 instanceof TerrainPatch)
((TerrainPatch)quad2).setHeight(quadLH2, overrideHeight);
}
if (!quadLH3.isEmpty()) {
if (quad3 instanceof TerrainQuad)
((TerrainQuad)quad3).setHeight(quadLH3, overrideHeight);
else if(quad3 instanceof TerrainPatch)
((TerrainPatch)quad3).setHeight(quadLH3, overrideHeight);
}
if (!quadLH4.isEmpty()) {
if (quad4 instanceof TerrainQuad)
((TerrainQuad)quad4).setHeight(quadLH4, overrideHeight);
else if(quad4 instanceof TerrainPatch)
((TerrainPatch)quad4).setHeight(quadLH4, overrideHeight);
}
}
protected boolean isPointOnTerrain(int x, int z) {
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;
}

@ -66,7 +66,7 @@ public class TerrainTestModifyHeight extends SimpleApplication {
private TerrainQuad terrain;
Material matTerrain;
Material matWire;
boolean wireframe = false;
boolean wireframe = true;
boolean triPlanar = false;
boolean wardiso = false;
boolean minnaert = false;
@ -206,14 +206,14 @@ public class TerrainTestModifyHeight extends SimpleApplication {
if (pressed) {
Vector3f intersection = getWorldIntersection();
if (intersection != null) {
adjustHeight(intersection, 16, 1);
adjustHeight(intersection, 64, 1);
}
}
} else if (name.equals("Lower")) {
if (pressed) {
Vector3f intersection = getWorldIntersection();
if (intersection != null) {
adjustHeight(intersection, 16, -1);
adjustHeight(intersection, 32, -1);
}
}
}
@ -230,8 +230,11 @@ public class TerrainTestModifyHeight extends SimpleApplication {
float xStepAmount = terrain.getLocalScale().x;
float zStepAmount = terrain.getLocalScale().z;
long start = System.currentTimeMillis();
List<Vector2f> locs = new ArrayList<Vector2f>();
List<Float> heights = new ArrayList<Float>();
for (int z = -radiusStepsZ; z < radiusStepsZ; z++) {
for (int x = -radiusStepsZ; x < radiusStepsX; x++) {
for (int x = -radiusStepsX; x < radiusStepsX; x++) {
float locX = loc.x + (x * xStepAmount);
float locZ = loc.z + (z * zStepAmount);
@ -239,13 +242,14 @@ public class TerrainTestModifyHeight extends SimpleApplication {
if (isInRadius(locX - loc.x, locZ - loc.z, radius)) {
// see if it is in the radius of the tool
float h = calculateHeight(radius, height, locX - loc.x, locZ - loc.z);
// increase the height
terrain.adjustHeight(new Vector2f(locX, locZ), h);
locs.add(new Vector2f(locX, locZ));
heights.add(h);
}
}
}
System.out.println("took: " + (System.currentTimeMillis() - start));
terrain.adjustHeight(locs, heights);
//System.out.println("Modified "+locs.size()+" points, took: " + (System.currentTimeMillis() - start)+" ms");
terrain.updateModelBound();
}

Loading…
Cancel
Save