* 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. 175
      engine/src/terrain/com/jme3/terrain/geomipmap/TerrainGrid.java
  2. 36
      engine/src/terrain/com/jme3/terrain/geomipmap/TerrainQuad.java

@ -50,12 +50,17 @@ import com.jme3.terrain.heightmap.HeightMapGrid;
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
*/
public class TerrainGrid extends TerrainQuad {
protected static final Logger log = Logger.getLogger(TerrainGrid.class.getCanonicalName());
protected Vector3f currentCell;
protected Vector3f currentCamCell;
protected int quarterSize;
protected int quadSize;
protected HeightMapGrid heightMapGrid;
@ -77,7 +82,6 @@ public class TerrainGrid extends TerrainQuad {
}
public void run() {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
int quadIdx = i * 4 + j;
@ -86,7 +90,7 @@ public class TerrainGrid extends TerrainQuad {
if (q == null) {
// create the new Quad since it doesn't exist
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());
log.log(Level.FINE, "Loaded TerrainQuad {0}", q.getName());
}
@ -116,13 +120,13 @@ public class TerrainGrid extends TerrainQuad {
}
protected int getQuadrant(int quadIndex) {
if (quadIndex == 9) {
if (quadIndex == 5) {
return 1;
} else if (quadIndex == 5) {
} else if (quadIndex == 9) {
return 2;
} else if (quadIndex == 10) {
return 3;
} else if (quadIndex == 6) {
return 3;
} else if (quadIndex == 10) {
return 4;
}
return 0; // error
@ -141,16 +145,22 @@ public class TerrainGrid extends TerrainQuad {
this.totalSize = maxVisibleSize;
this.offset = offset;
this.offsetAmount = offsetAmount;
//this.lodCalculatorFactory = lodCalculatorFactory;
this.gridOffset = new int[]{0,0};
//if (lodCalculatorFactory == null) {
// lodCalculatorFactory = new LodDistanceCalculatorFactory();
//}
/*
* -z
* |
* 1|3
* -x ----+---- x
* 2|4
* |
* z
*/
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, -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());
}
@ -170,7 +180,7 @@ public class TerrainGrid extends TerrainQuad {
if (this.material == null) {
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);
for (TerrainGridListener l : this.listeners.values()) {
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
// 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 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.
gridOffset[0] = Math.round(camCell.x*(size/2));
gridOffset[1] = Math.round(camCell.z*(size/2));
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);
for (TerrainGridListener l : this.listeners.values()) {
l.gridMoved(camCell);
@ -199,10 +210,21 @@ public class TerrainGrid extends TerrainQuad {
super.update(locations, lodCalculator);
}
public Vector3f getCell(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);
return new Vector3f(FastMath.floor(v.x), 0, FastMath.floor(v.z));
public Vector3f getCamCell(Vector3f location) {
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));
}
/**
* 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) {
@ -211,7 +233,7 @@ public class TerrainGrid extends TerrainQuad {
this.getQuad(idx).removeControl(RigidBodyControl.class);
}
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));
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) {
this.removeQuad(quadrant);
//q.setMaterial(this.material);
//q.setLocalTranslation(quadOrigins[quadrant - 1]);
q.setQuadrant((short) quadrant);
this.attachChild(q);
@ -234,53 +255,66 @@ public class TerrainGrid extends TerrainQuad {
for (TerrainGridListener l : listeners.values()) {
l.tileAttached(cam, q);
}
System.err.println("attachQuadAt "+quadrant+", children: "+this.getChildren().size());
updateModelBound();
}
protected void updateChildrens(Vector3f cam) {
// ---------------------------------------------------
// LRU cache is used, so elements that need to remain
// should be touched.
// ---------------------------------------------------
/**
* Called when the camera has moved into a new cell. We need to
* update what quads are in the scene now.
*
* 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 dy = 0;
if (currentCell != null) {
dx = (int) (cam.x - currentCell.x);
dy = (int) (cam.z - currentCell.z);
if (currentCamCell != null) {
dx = (int) (camCell.x - currentCamCell.x);
dy = (int) (camCell.z - currentCamCell.z);
}
int kxm = 0;
int kxM = 4;
int kym = 0;
int kyM = 4;
if (dx == -1) {
kxM = 3;
} else if (dx == 1) {
kxm = 1;
int xMin = 0;
int xMax = 4;
int yMin = 0;
int yMax = 4;
if (dx == -1) { // camera moved to -X direction
xMax = 3;
} else if (dx == 1) { // camera moved to +X direction
xMin = 1;
}
if (dy == -1) {
kyM = 3;
} else if (dy == 1) {
kym = 1;
if (dy == -1) { // camera moved to -Y direction
yMax = 3;
} else if (dy == 1) { // camera moved to +Y direction
yMin = 1;
}
for (int i = kym; i < kyM; i++) {
for (int j = kxm; j < kxM; j++) {
cache.get(cam.add(quadIndex[i * 4 + j]));
// Touch the items in the cache that we are and will be interested in.
// We activate cells in the direction we are moving. If we didn't move
// 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) {
// use the same executor as the LODControl
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) {
@ -288,7 +322,7 @@ public class TerrainGrid extends TerrainQuad {
}
public Vector3f getCurrentCell() {
return this.currentCell;
return this.currentCamCell;
}
public void removeListener(String id) {
@ -320,43 +354,4 @@ public class TerrainGrid extends TerrainQuad {
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
* top right. This will seem upside down, but the texture coordinates compensate; and
* it allows you to easily get the heightmap values at real world X,Z coordinates.
* top right.
*
* +x
* ^
@ -108,7 +107,7 @@ public class TerrainQuad extends Node implements Terrain {
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 LodCalculator lodCalculator;
@ -646,13 +645,14 @@ public class TerrainQuad extends Node implements Terrain {
/**
* Quadrants, world coordinates, and heightmap coordinates (Y-up):
* x
* | u
* 2|4 v
* -z ----+---- z
* -v 1|3
* -u |
* -x
*
* -z
* -u |
* -v 1|3
* -x ----+---- x
* 2|4 u
* | v
* z
* <code>createQuad</code> generates four new quads from this quad.
* 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.
@ -669,7 +669,7 @@ public class TerrainQuad extends Node implements Terrain {
//if (lodCalculator == null)
// 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);
Vector3f origin1 = new Vector3f(-quarterSize * stepScale.x, 0,
@ -687,7 +687,7 @@ public class TerrainQuad extends Node implements Terrain {
quad1.quadrant = 1;
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,
split);
@ -707,7 +707,7 @@ public class TerrainQuad extends Node implements Terrain {
quad2.quadrant = 2;
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,
split);
@ -727,7 +727,7 @@ public class TerrainQuad extends Node implements Terrain {
quad3.quadrant = 3;
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,
split - 1, split);
@ -779,7 +779,7 @@ public class TerrainQuad extends Node implements Terrain {
offsetAmount += quarterSize;
// 1 upper left
// 1 lower left
float[] heightBlock1 = createHeightSubBlock(heightMap, 0, 0, split);
Vector3f origin1 = new Vector3f(-halfSize * stepScale.x, 0, -halfSize
@ -801,7 +801,7 @@ public class TerrainQuad extends Node implements Terrain {
//patch1.setLodCalculator(lodCalculator);
//TangentBinormalGenerator.generate(patch1);
// 2 lower left
// 2 upper left
float[] heightBlock2 = createHeightSubBlock(heightMap, 0, split - 1,
split);
@ -823,7 +823,7 @@ public class TerrainQuad extends Node implements Terrain {
//patch2.setLodCalculator(lodCalculator);
//TangentBinormalGenerator.generate(patch2);
// 3 upper right
// 3 lower right
float[] heightBlock3 = createHeightSubBlock(heightMap, split - 1, 0,
split);
@ -845,7 +845,7 @@ public class TerrainQuad extends Node implements Terrain {
//patch3.setLodCalculator(lodCalculator);
//TangentBinormalGenerator.generate(patch3);
// 4 lower right
// 4 upper right
float[] heightBlock4 = createHeightSubBlock(heightMap, split - 1,
split - 1, split);

Loading…
Cancel
Save