* fixed terrain grid issues

* added javadoc

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@8672 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
3.0
bre..ns 13 years ago
parent f7cf53af30
commit 253334fd07
  1. 173
      engine/src/terrain/com/jme3/terrain/geomipmap/TerrainGrid.java
  2. 34
      engine/src/terrain/com/jme3/terrain/geomipmap/TerrainQuad.java

@ -50,12 +50,17 @@ import com.jme3.terrain.heightmap.HeightMapGrid;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
/** /**
*
* The grid is indexed by cells. Each cell has an integer XZ coordinate originating at 0,0.
* TerrainGrid will piggyback on the TerrainLodControl so it can use the camera for its
* updates as well. It does this in the overwritten update() method.
*
* @author Anthyon * @author Anthyon
*/ */
public class TerrainGrid extends TerrainQuad { public class TerrainGrid extends TerrainQuad {
protected static final Logger log = Logger.getLogger(TerrainGrid.class.getCanonicalName()); protected static final Logger log = Logger.getLogger(TerrainGrid.class.getCanonicalName());
protected Vector3f currentCell; protected Vector3f currentCamCell;
protected int quarterSize; protected int quarterSize;
protected int quadSize; protected int quadSize;
protected HeightMapGrid heightMapGrid; protected HeightMapGrid heightMapGrid;
@ -77,7 +82,6 @@ public class TerrainGrid extends TerrainQuad {
} }
public void run() { public void run() {
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) { for (int j = 0; j < 4; j++) {
int quadIdx = i * 4 + j; int quadIdx = i * 4 + j;
@ -86,7 +90,7 @@ public class TerrainGrid extends TerrainQuad {
if (q == null) { if (q == null) {
// create the new Quad since it doesn't exist // create the new Quad since it doesn't exist
HeightMap heightMapAt = heightMapGrid.getHeightMapAt(temp); HeightMap heightMapAt = heightMapGrid.getHeightMapAt(temp);
q = new TerrainQuad(getName() + "Quad" + temp, patchSize, totalSize, quadSize, heightMapAt == null ? null : heightMapAt.getHeightMap()); q = new TerrainQuad(getName() + "Quad" + temp, patchSize, quadSize, heightMapAt == null ? null : heightMapAt.getHeightMap());
q.setMaterial(material.clone()); q.setMaterial(material.clone());
log.log(Level.FINE, "Loaded TerrainQuad {0}", q.getName()); log.log(Level.FINE, "Loaded TerrainQuad {0}", q.getName());
} }
@ -116,13 +120,13 @@ public class TerrainGrid extends TerrainQuad {
} }
protected int getQuadrant(int quadIndex) { protected int getQuadrant(int quadIndex) {
if (quadIndex == 9) { if (quadIndex == 5) {
return 1; return 1;
} else if (quadIndex == 5) { } else if (quadIndex == 9) {
return 2; return 2;
} else if (quadIndex == 10) {
return 3;
} else if (quadIndex == 6) { } else if (quadIndex == 6) {
return 3;
} else if (quadIndex == 10) {
return 4; return 4;
} }
return 0; // error return 0; // error
@ -141,16 +145,22 @@ public class TerrainGrid extends TerrainQuad {
this.totalSize = maxVisibleSize; this.totalSize = maxVisibleSize;
this.offset = offset; this.offset = offset;
this.offsetAmount = offsetAmount; this.offsetAmount = offsetAmount;
//this.lodCalculatorFactory = lodCalculatorFactory;
this.gridOffset = new int[]{0,0}; this.gridOffset = new int[]{0,0};
//if (lodCalculatorFactory == null) {
// lodCalculatorFactory = new LodDistanceCalculatorFactory(); /*
//} * -z
* |
* 1|3
* -x ----+---- x
* 2|4
* |
* z
*/
this.quadIndex = new Vector3f[]{ this.quadIndex = new Vector3f[]{
new Vector3f(-1, 0, 2), new Vector3f(0, 0, 2), new Vector3f(1, 0, 2), new Vector3f(2, 0, 2), new Vector3f(-1, 0, -1), new Vector3f(0, 0, -1), new Vector3f(1, 0, -1), new Vector3f(2, 0, -1),
new Vector3f(-1, 0, 1), new Vector3f(0, 0, 1), new Vector3f(1, 0, 1), new Vector3f(2, 0, 1),
new Vector3f(-1, 0, 0), new Vector3f(0, 0, 0), new Vector3f(1, 0, 0), new Vector3f(2, 0, 0), new Vector3f(-1, 0, 0), new Vector3f(0, 0, 0), new Vector3f(1, 0, 0), new Vector3f(2, 0, 0),
new Vector3f(-1, 0, -1), new Vector3f(0, 0, -1), new Vector3f(1, 0, -1), new Vector3f(-2, 0, -1)}; new Vector3f(-1, 0, 1), new Vector3f(0, 0, 1), new Vector3f(1, 0, 1), new Vector3f(2, 0, 1),
new Vector3f(-1, 0, 2), new Vector3f(0, 0, 2), new Vector3f(1, 0, 2), new Vector3f(2, 0, 2)};
addControl(new UpdateControl()); addControl(new UpdateControl());
} }
@ -170,7 +180,7 @@ public class TerrainGrid extends TerrainQuad {
if (this.material == null) { if (this.material == null) {
throw new RuntimeException("Material must be set prior to call of initialize"); throw new RuntimeException("Material must be set prior to call of initialize");
} }
Vector3f camCell = this.getCell(location); Vector3f camCell = this.getCamCell(location);
this.updateChildrens(camCell); this.updateChildrens(camCell);
for (TerrainGridListener l : this.listeners.values()) { for (TerrainGridListener l : this.listeners.values()) {
l.gridMoved(camCell); l.gridMoved(camCell);
@ -184,13 +194,14 @@ public class TerrainGrid extends TerrainQuad {
// 1: every camera has an associated grid, then the location is not enough to identify which camera location has changed // 1: every camera has an associated grid, then the location is not enough to identify which camera location has changed
// 2: grids are associated with locations, and no incremental update is done, we load new grids for new locations, and unload those that are not needed anymore // 2: grids are associated with locations, and no incremental update is done, we load new grids for new locations, and unload those that are not needed anymore
Vector3f cam = locations.get(0); Vector3f cam = locations.get(0);
Vector3f camCell = this.getCell(cam); Vector3f camCell = this.getCamCell(cam); // get the grid index value of where the camera is (ie. 2,1)
if(cellsLoaded>1){ // Check if cells are updated before updating gridoffset. if(cellsLoaded>1){ // Check if cells are updated before updating gridoffset.
gridOffset[0] = Math.round(camCell.x*(size/2)); gridOffset[0] = Math.round(camCell.x*(size/2));
gridOffset[1] = Math.round(camCell.z*(size/2)); gridOffset[1] = Math.round(camCell.z*(size/2));
cellsLoaded=0; cellsLoaded=0;
} }
if (camCell.x != this.currentCell.x || camCell.z != currentCell.z) { if (camCell.x != this.currentCamCell.x || camCell.z != currentCamCell.z) {
// if the camera has moved into a new cell, load new terrain into the visible 4 center quads
this.updateChildrens(camCell); this.updateChildrens(camCell);
for (TerrainGridListener l : this.listeners.values()) { for (TerrainGridListener l : this.listeners.values()) {
l.gridMoved(camCell); l.gridMoved(camCell);
@ -199,10 +210,21 @@ public class TerrainGrid extends TerrainQuad {
super.update(locations, lodCalculator); super.update(locations, lodCalculator);
} }
public Vector3f getCell(Vector3f location) { public Vector3f getCamCell(Vector3f location) {
final Vector3f v = location.add(this.getWorldScale().mult((this.quadSize - 1)/2)).divide(this.getWorldScale().mult(this.quadSize - 1)).add(0.5f, 0, 0.5f); Vector3f tile = getTileCell(location);
Vector3f offsetHalf = new Vector3f(-0.5f, 0, -0.5f);
Vector3f shifted = tile.subtract(offsetHalf);
return new Vector3f(FastMath.floor(shifted.x), 0, FastMath.floor(shifted.z));
}
return new Vector3f(FastMath.floor(v.x), 0, FastMath.floor(v.z)); /**
* Centered at 0,0.
* Get the tile index location in integer form:
* @param location world coordinate
*/
public Vector3f getTileCell(Vector3f location) {
Vector3f tileLoc = location.divide(this.getWorldScale().mult(this.quadSize));
return tileLoc;
} }
protected void removeQuad(int idx) { protected void removeQuad(int idx) {
@ -211,7 +233,7 @@ public class TerrainGrid extends TerrainQuad {
this.getQuad(idx).removeControl(RigidBodyControl.class); this.getQuad(idx).removeControl(RigidBodyControl.class);
} }
for (TerrainGridListener l : listeners.values()) { for (TerrainGridListener l : listeners.values()) {
l.tileDetached(getCell(this.getQuad(idx).getWorldTranslation()), this.getQuad(idx)); l.tileDetached(getTileCell(this.getQuad(idx).getWorldTranslation()), this.getQuad(idx));
} }
this.detachChild(this.getQuad(idx)); this.detachChild(this.getQuad(idx));
cellsLoaded++; // For gridoffset calc., maybe the run() method is a better location for this. cellsLoaded++; // For gridoffset calc., maybe the run() method is a better location for this.
@ -223,8 +245,7 @@ public class TerrainGrid extends TerrainQuad {
*/ */
protected void attachQuadAt(TerrainQuad q, int quadrant, Vector3f cam) { protected void attachQuadAt(TerrainQuad q, int quadrant, Vector3f cam) {
this.removeQuad(quadrant); this.removeQuad(quadrant);
//q.setMaterial(this.material);
//q.setLocalTranslation(quadOrigins[quadrant - 1]);
q.setQuadrant((short) quadrant); q.setQuadrant((short) quadrant);
this.attachChild(q); this.attachChild(q);
@ -234,53 +255,66 @@ public class TerrainGrid extends TerrainQuad {
for (TerrainGridListener l : listeners.values()) { for (TerrainGridListener l : listeners.values()) {
l.tileAttached(cam, q); l.tileAttached(cam, q);
} }
System.err.println("attachQuadAt "+quadrant+", children: "+this.getChildren().size());
updateModelBound(); updateModelBound();
} }
protected void updateChildrens(Vector3f cam) { /**
// --------------------------------------------------- * Called when the camera has moved into a new cell. We need to
// LRU cache is used, so elements that need to remain * update what quads are in the scene now.
// should be touched. *
// --------------------------------------------------- * Step 1: touch cache
* LRU cache is used, so elements that need to remain
* should be touched.
*
* Step 2: load new quads in background thread
* if the camera has moved into a new cell, we load in new quads
* @param camCell the cell the camera is in
*/
protected void updateChildrens(Vector3f camCell) {
int dx = 0; int dx = 0;
int dy = 0; int dy = 0;
if (currentCell != null) { if (currentCamCell != null) {
dx = (int) (cam.x - currentCell.x); dx = (int) (camCell.x - currentCamCell.x);
dy = (int) (cam.z - currentCell.z); dy = (int) (camCell.z - currentCamCell.z);
} }
int kxm = 0; int xMin = 0;
int kxM = 4; int xMax = 4;
int kym = 0; int yMin = 0;
int kyM = 4; int yMax = 4;
if (dx == -1) { if (dx == -1) { // camera moved to -X direction
kxM = 3; xMax = 3;
} else if (dx == 1) { } else if (dx == 1) { // camera moved to +X direction
kxm = 1; xMin = 1;
} }
if (dy == -1) { if (dy == -1) { // camera moved to -Y direction
kyM = 3; yMax = 3;
} else if (dy == 1) { } else if (dy == 1) { // camera moved to +Y direction
kym = 1; yMin = 1;
} }
for (int i = kym; i < kyM; i++) { // Touch the items in the cache that we are and will be interested in.
for (int j = kxm; j < kxM; j++) { // We activate cells in the direction we are moving. If we didn't move
cache.get(cam.add(quadIndex[i * 4 + j])); // either way in one of the axes (say X or Y axis) then they are all touched.
for (int i = yMin; i < yMax; i++) {
for (int j = xMin; j < xMax; j++) {
cache.get(camCell.add(quadIndex[i * 4 + j]));
} }
} }
// --------------------------------------------------- // ---------------------------------------------------
// --------------------------------------------------- // ---------------------------------------------------
if (executor == null) { if (executor == null) {
// use the same executor as the LODControl
executor = createExecutorService(); executor = createExecutorService();
} }
executor.submit(new UpdateQuadCache(cam)); executor.submit(new UpdateQuadCache(camCell));
this.currentCell = cam; this.currentCamCell = camCell;
} }
public void addListener(String id, TerrainGridListener listener) { public void addListener(String id, TerrainGridListener listener) {
@ -288,7 +322,7 @@ public class TerrainGrid extends TerrainQuad {
} }
public Vector3f getCurrentCell() { public Vector3f getCurrentCell() {
return this.currentCell; return this.currentCamCell;
} }
public void removeListener(String id) { public void removeListener(String id) {
@ -320,43 +354,4 @@ public class TerrainGrid extends TerrainQuad {
return super.getHeightmapHeight(x-gridOffset[0], z-gridOffset[1]); return super.getHeightmapHeight(x-gridOffset[0], z-gridOffset[1]);
} }
@Override
protected TerrainQuad findDownQuad() {
if (quadrant == 1) {
return cache.get(currentCell.add(quadIndex[13]));
} else if (quadrant == 3) {
return cache.get(currentCell.add(quadIndex[14]));
}
return null;
}
@Override
protected TerrainQuad findLeftQuad() {
if (quadrant == 1) {
return cache.get(currentCell.add(quadIndex[8]));
} else if (quadrant == 2) {
return cache.get(currentCell.add(quadIndex[4]));
}
return null;
}
@Override
protected TerrainQuad findRightQuad() {
if (quadrant == 3) {
return cache.get(currentCell.add(quadIndex[11]));
} else if (quadrant == 4) {
return cache.get(currentCell.add(quadIndex[7]));
}
return null;
}
@Override
protected TerrainQuad findTopQuad() {
if (quadrant == 2) {
return cache.get(currentCell.add(quadIndex[1]));
} else if (quadrant == 4) {
return cache.get(currentCell.add(quadIndex[2]));
}
return null;
}
} }

@ -80,8 +80,7 @@ import java.util.logging.Logger;
* *
* *
* Heightmap coordinates start from the bottom left of the world and work towards the * Heightmap coordinates start from the bottom left of the world and work towards the
* top right. This will seem upside down, but the texture coordinates compensate; and * top right.
* it allows you to easily get the heightmap values at real world X,Z coordinates.
* *
* +x * +x
* ^ * ^
@ -108,7 +107,7 @@ public class TerrainQuad extends Node implements Terrain {
protected float offsetAmount; protected float offsetAmount;
protected int quadrant = 1; // 1=upper left, 2=lower left, 3=upper right, 4=lower right protected int quadrant = 0; // 1=upper left, 2=lower left, 3=upper right, 4=lower right
//protected LodCalculatorFactory lodCalculatorFactory; //protected LodCalculatorFactory lodCalculatorFactory;
//protected LodCalculator lodCalculator; //protected LodCalculator lodCalculator;
@ -646,13 +645,14 @@ public class TerrainQuad extends Node implements Terrain {
/** /**
* Quadrants, world coordinates, and heightmap coordinates (Y-up): * Quadrants, world coordinates, and heightmap coordinates (Y-up):
* x *
* | u * -z
* 2|4 v
* -z ----+---- z
* -v 1|3
* -u | * -u |
* -x * -v 1|3
* -x ----+---- x
* 2|4 u
* | v
* z
* <code>createQuad</code> generates four new quads from this quad. * <code>createQuad</code> generates four new quads from this quad.
* The heightmap's top left (0,0) coordinate is at the bottom, -x,-z * The heightmap's top left (0,0) coordinate is at the bottom, -x,-z
* coordinate of the terrain, so it grows in the positive x.z direction. * coordinate of the terrain, so it grows in the positive x.z direction.
@ -669,7 +669,7 @@ public class TerrainQuad extends Node implements Terrain {
//if (lodCalculator == null) //if (lodCalculator == null)
// lodCalculator = createDefaultLodCalculator(); // set a default one // lodCalculator = createDefaultLodCalculator(); // set a default one
// 1 upper left of heightmap, lower left quad // 1 upper left of heightmap, upper left quad
float[] heightBlock1 = createHeightSubBlock(heightMap, 0, 0, split); float[] heightBlock1 = createHeightSubBlock(heightMap, 0, 0, split);
Vector3f origin1 = new Vector3f(-quarterSize * stepScale.x, 0, Vector3f origin1 = new Vector3f(-quarterSize * stepScale.x, 0,
@ -687,7 +687,7 @@ public class TerrainQuad extends Node implements Terrain {
quad1.quadrant = 1; quad1.quadrant = 1;
this.attachChild(quad1); this.attachChild(quad1);
// 2 lower left of heightmap, upper left quad // 2 lower left of heightmap, lower left quad
float[] heightBlock2 = createHeightSubBlock(heightMap, 0, split - 1, float[] heightBlock2 = createHeightSubBlock(heightMap, 0, split - 1,
split); split);
@ -707,7 +707,7 @@ public class TerrainQuad extends Node implements Terrain {
quad2.quadrant = 2; quad2.quadrant = 2;
this.attachChild(quad2); this.attachChild(quad2);
// 3 upper right of heightmap, lower right quad // 3 upper right of heightmap, upper right quad
float[] heightBlock3 = createHeightSubBlock(heightMap, split - 1, 0, float[] heightBlock3 = createHeightSubBlock(heightMap, split - 1, 0,
split); split);
@ -727,7 +727,7 @@ public class TerrainQuad extends Node implements Terrain {
quad3.quadrant = 3; quad3.quadrant = 3;
this.attachChild(quad3); this.attachChild(quad3);
// 4 lower right of heightmap, upper right quad // 4 lower right of heightmap, lower right quad
float[] heightBlock4 = createHeightSubBlock(heightMap, split - 1, float[] heightBlock4 = createHeightSubBlock(heightMap, split - 1,
split - 1, split); split - 1, split);
@ -779,7 +779,7 @@ public class TerrainQuad extends Node implements Terrain {
offsetAmount += quarterSize; offsetAmount += quarterSize;
// 1 upper left // 1 lower left
float[] heightBlock1 = createHeightSubBlock(heightMap, 0, 0, split); float[] heightBlock1 = createHeightSubBlock(heightMap, 0, 0, split);
Vector3f origin1 = new Vector3f(-halfSize * stepScale.x, 0, -halfSize Vector3f origin1 = new Vector3f(-halfSize * stepScale.x, 0, -halfSize
@ -801,7 +801,7 @@ public class TerrainQuad extends Node implements Terrain {
//patch1.setLodCalculator(lodCalculator); //patch1.setLodCalculator(lodCalculator);
//TangentBinormalGenerator.generate(patch1); //TangentBinormalGenerator.generate(patch1);
// 2 lower left // 2 upper left
float[] heightBlock2 = createHeightSubBlock(heightMap, 0, split - 1, float[] heightBlock2 = createHeightSubBlock(heightMap, 0, split - 1,
split); split);
@ -823,7 +823,7 @@ public class TerrainQuad extends Node implements Terrain {
//patch2.setLodCalculator(lodCalculator); //patch2.setLodCalculator(lodCalculator);
//TangentBinormalGenerator.generate(patch2); //TangentBinormalGenerator.generate(patch2);
// 3 upper right // 3 lower right
float[] heightBlock3 = createHeightSubBlock(heightMap, split - 1, 0, float[] heightBlock3 = createHeightSubBlock(heightMap, split - 1, 0,
split); split);
@ -845,7 +845,7 @@ public class TerrainQuad extends Node implements Terrain {
//patch3.setLodCalculator(lodCalculator); //patch3.setLodCalculator(lodCalculator);
//TangentBinormalGenerator.generate(patch3); //TangentBinormalGenerator.generate(patch3);
// 4 lower right // 4 upper right
float[] heightBlock4 = createHeightSubBlock(heightMap, split - 1, float[] heightBlock4 = createHeightSubBlock(heightMap, split - 1,
split - 1, split); split - 1, split);

Loading…
Cancel
Save