1
0
mirror of https://github.com/Dejvino/roadtrip synced 2024-11-22 07:52:36 +00:00

Map: Fine-grained terrain grid implemented. Now there is smoother loading and unloading of terrain tiles.

This commit is contained in:
Dejvino 2017-01-25 00:38:14 +01:00
parent c9a5429193
commit bdadc3c739
2 changed files with 49 additions and 39 deletions

View File

@ -41,6 +41,7 @@ import com.jme3.math.FastMath;
import com.jme3.math.Vector2f; import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f; import com.jme3.math.Vector3f;
import com.jme3.scene.Spatial; import com.jme3.scene.Spatial;
import com.jme3.scene.control.LodControl;
import com.jme3.scene.control.UpdateControl; import com.jme3.scene.control.UpdateControl;
import com.jme3.terrain.Terrain; import com.jme3.terrain.Terrain;
import com.jme3.terrain.geomipmap.LRUCache; import com.jme3.terrain.geomipmap.LRUCache;
@ -122,10 +123,11 @@ public class FineTerrainGrid extends TerrainQuad {
protected HeightMapGrid heightMapGrid; protected HeightMapGrid heightMapGrid;
private TerrainGridTileLoader gridTileLoader; private TerrainGridTileLoader gridTileLoader;
protected Vector3f[] quadIndex; protected Vector3f[] quadIndex;
protected Set<TerrainGridListener> listeners = new HashSet<TerrainGridListener>(); protected Set<TerrainGridListener> listeners = new HashSet<>();
protected Material material; protected Material material;
//cache needs to be 1 row (4 cells) larger than what we care is cached //cache needs to be 1 row (4 cells) larger than what we care is cached
protected LRUCache<Vector3f, TerrainQuad> cache = new LRUCache<Vector3f, TerrainQuad>(20); protected LRUCache<Vector3f, TerrainQuad> cache = new LRUCache<>(
getSubdivisionsPerSide() * getSubdivisionsPerSide() + getSubdivisionsPerSide());
protected int cellsLoaded = 0; protected int cellsLoaded = 0;
protected int[] gridOffset; protected int[] gridOffset;
protected boolean runOnce = false; protected boolean runOnce = false;
@ -135,6 +137,11 @@ public class FineTerrainGrid extends TerrainQuad {
return size; return size;
} }
public int getSubdivisionsPerSide()
{
return 8;
}
protected class UpdateQuadCache implements Runnable { protected class UpdateQuadCache implements Runnable {
protected final Vector3f location; protected final Vector3f location;
@ -153,9 +160,9 @@ public class FineTerrainGrid extends TerrainQuad {
* neighbours). * neighbours).
*/ */
public void run() { public void run() {
for (int i = 0; i < 4; i++) { for (int i = 0; i < getSubdivisionsPerSide(); i++) {
for (int j = 0; j < 4; j++) { for (int j = 0; j < getSubdivisionsPerSide(); j++) {
int quadIdx = i * 4 + j; int quadIdx = i * getSubdivisionsPerSide() + j;
final Vector3f quadCell = location.add(quadIndex[quadIdx]); final Vector3f quadCell = location.add(quadIndex[quadIdx]);
TerrainQuad q = cache.get(quadCell); TerrainQuad q = cache.get(quadCell);
if (q == null) { if (q == null) {
@ -221,20 +228,18 @@ public class FineTerrainGrid extends TerrainQuad {
} }
protected boolean isCenter(int quadIndex) { protected boolean isCenter(int quadIndex) {
return quadIndex == 9 || quadIndex == 5 || quadIndex == 10 || quadIndex == 6; // The only thing that is not a center for us here are the edges of the grid.
int w = getSubdivisionsPerSide();
// left / right edge
if (quadIndex % w == 0 || (quadIndex + 1) % w == 0) return false;
// top / down edge
if (quadIndex < w || quadIndex >= (w*w - w)) return false;
// center
return true;
} }
protected int getQuadrant(int quadIndex) { protected int getQuadrant(int quadIndex) {
if (quadIndex == 5) { return quadIndex + 1; // whatever, just not 0
return 1;
} else if (quadIndex == 9) {
return 2;
} else if (quadIndex == 6) {
return 3;
} else if (quadIndex == 10) {
return 4;
}
return 0; // error
} }
public FineTerrainGrid(String name, int patchSize, int maxVisibleSize, Vector3f scale, TerrainGridTileLoader terrainQuadGrid, public FineTerrainGrid(String name, int patchSize, int maxVisibleSize, Vector3f scale, TerrainGridTileLoader terrainQuadGrid,
@ -268,8 +273,8 @@ public class FineTerrainGrid extends TerrainQuad {
private void initData() { private void initData() {
int maxVisibleSize = size; int maxVisibleSize = size;
this.quarterSize = maxVisibleSize >> 2; this.quarterSize = maxVisibleSize / getSubdivisionsPerSide();
this.quadSize = (maxVisibleSize + 1) >> 1; this.quadSize = (maxVisibleSize / getSubdivisionsPerSide()) + 1;
this.totalSize = maxVisibleSize; this.totalSize = maxVisibleSize;
this.gridOffset = new int[]{0, 0}; this.gridOffset = new int[]{0, 0};
@ -282,11 +287,14 @@ public class FineTerrainGrid extends TerrainQuad {
* | * |
* z * z
*/ */
this.quadIndex = new Vector3f[]{ // generate a grid of quad positions
new Vector3f(-1, 0, -1), new Vector3f(0, 0, -1), new Vector3f(1, 0, -1), new Vector3f(2, 0, -1), this.quadIndex = new Vector3f[getSubdivisionsPerSide() * getSubdivisionsPerSide()];
new Vector3f(-1, 0, 0), new Vector3f(0, 0, 0), new Vector3f(1, 0, 0), new Vector3f(2, 0, 0), int i = 0;
new Vector3f(-1, 0, 1), new Vector3f(0, 0, 1), new Vector3f(1, 0, 1), new Vector3f(2, 0, 1), for (int z = -getSubdivisionsPerSide() / 2 + 1; z <= getSubdivisionsPerSide() / 2; z++) {
new Vector3f(-1, 0, 2), new Vector3f(0, 0, 2), new Vector3f(1, 0, 2), new Vector3f(2, 0, 2)}; for (int x = -getSubdivisionsPerSide() / 2 + 1; x <= getSubdivisionsPerSide() / 2; x++) {
quadIndex[i++] = new Vector3f(x, 0, z);
}
}
} }
@ -357,7 +365,7 @@ public class FineTerrainGrid extends TerrainQuad {
} }
protected void removeQuad(TerrainQuad q) { protected void removeQuad(TerrainQuad q) {
if (q != null && ( (q.getQuadrant() > 0 && q.getQuadrant()<5) || q.getParent() != null) ) { if (q != null && ( q.getParent() != null) ) {
for (TerrainGridListener l : listeners) { for (TerrainGridListener l : listeners) {
l.tileDetached(getTileCell(q.getWorldTranslation()), q); l.tileDetached(getTileCell(q.getWorldTranslation()), q);
} }
@ -372,7 +380,6 @@ public class FineTerrainGrid extends TerrainQuad {
* @param shifted quads are still attached to the parent and don't need to re-load * @param shifted quads are still attached to the parent and don't need to re-load
*/ */
protected void attachQuadAt(TerrainQuad q, int quadrant, Vector3f quadCell, boolean shifted) { protected void attachQuadAt(TerrainQuad q, int quadrant, Vector3f quadCell, boolean shifted) {
q.setQuadrant((short) quadrant); q.setQuadrant((short) quadrant);
if (!shifted) if (!shifted)
this.attachChild(q); this.attachChild(q);
@ -386,7 +393,6 @@ public class FineTerrainGrid extends TerrainQuad {
} }
} }
updateModelBound(); updateModelBound();
} }
@ -412,17 +418,17 @@ public class FineTerrainGrid extends TerrainQuad {
} }
int xMin = 0; int xMin = 0;
int xMax = 4; int xMax = getSubdivisionsPerSide();
int yMin = 0; int yMin = 0;
int yMax = 4; int yMax = getSubdivisionsPerSide();
if (dx == -1) { // camera moved to -X direction if (dx == -1) { // camera moved to -X direction
xMax = 3; xMax = getSubdivisionsPerSide() - 1;
} else if (dx == 1) { // camera moved to +X direction } else if (dx == 1) { // camera moved to +X direction
xMin = 1; xMin = 1;
} }
if (dy == -1) { // camera moved to -Y direction if (dy == -1) { // camera moved to -Y direction
yMax = 3; yMax = getSubdivisionsPerSide() - 1;
} else if (dy == 1) { // camera moved to +Y direction } else if (dy == 1) { // camera moved to +Y direction
yMin = 1; yMin = 1;
} }
@ -432,7 +438,7 @@ public class FineTerrainGrid extends TerrainQuad {
// either way in one of the axes (say X or Y axis) then they are all touched. // 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 i = yMin; i < yMax; i++) {
for (int j = xMin; j < xMax; j++) { for (int j = xMin; j < xMax; j++) {
cache.get(camCell.add(quadIndex[i * 4 + j])); cache.get(camCell.add(quadIndex[i * getSubdivisionsPerSide() + j]));
} }
} }

View File

@ -16,6 +16,7 @@ import com.jme3.scene.Spatial;
import com.jme3.terrain.geomipmap.*; import com.jme3.terrain.geomipmap.*;
import com.jme3.terrain.geomipmap.grid.FractalTileLoader; import com.jme3.terrain.geomipmap.grid.FractalTileLoader;
import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator; import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
import com.jme3.terrain.geomipmap.lodcalc.LodCalculator;
import com.jme3.terrain.noise.ShaderUtils; import com.jme3.terrain.noise.ShaderUtils;
import com.jme3.terrain.noise.basis.FilteredBasis; import com.jme3.terrain.noise.basis.FilteredBasis;
import com.jme3.terrain.noise.filter.IterativeFilter; import com.jme3.terrain.noise.filter.IterativeFilter;
@ -35,7 +36,7 @@ import roadtrip.view.model.GameWorldState;
*/ */
public class GameWorldView { public class GameWorldView {
public static boolean DEBUG = false;//true; public static boolean DEBUG = true;
private final GameWorldState state; private final GameWorldState state;
@ -157,9 +158,11 @@ public class GameWorldView {
//terrain.terrainGrid.setLocalScale(2f, 1f, 2f); //terrain.terrainGrid.setLocalScale(2f, 1f, 2f);
this.rootNode.attachChild(terrain.terrainGrid); this.rootNode.attachChild(terrain.terrainGrid);
TerrainLodControl control = new FineTerrainGridLodControl(terrain.terrainGrid, camera); final TerrainLodControl lodControl = new FineTerrainGridLodControl(terrain.terrainGrid, camera);
control.setLodCalculator(new DistanceLodCalculator(patchSize + 1, 3.7f)); // patch size, and a multiplier lodControl.setLodCalculator(new DistanceLodCalculator(patchSize + 1, 3.7f)); // patch size, and a multiplier
terrain.terrainGrid.addControl(control); terrain.terrainGrid.addControl(lodControl);
final Spatial treeModel = assetManager.loadModel("Models/tree.j3o");
final FineTerrainGrid terrainGrid = terrain.terrainGrid; final FineTerrainGrid terrainGrid = terrain.terrainGrid;
terrainGrid.addListener(new TerrainGridListener() { terrainGrid.addListener(new TerrainGridListener() {
@ -170,6 +173,7 @@ public class GameWorldView {
@Override @Override
public void tileAttached(Vector3f cell, TerrainQuad quad) { public void tileAttached(Vector3f cell, TerrainQuad quad) {
lodControl.forceUpdate();
while(quad.getControl(RigidBodyControl.class)!=null){ while(quad.getControl(RigidBodyControl.class)!=null){
quad.removeControl(RigidBodyControl.class); quad.removeControl(RigidBodyControl.class);
} }
@ -180,7 +184,8 @@ public class GameWorldView {
String quadObjectsNodeKey = getQuadObjectsNodeKey(quad); String quadObjectsNodeKey = getQuadObjectsNodeKey(quad);
Node objects = new Node(quadObjectsNodeKey); Node objects = new Node(quadObjectsNodeKey);
populateQuadObjectsNode(quad, objects); // TODO: fix
//populateQuadObjectsNode(quad, objects);
rootNode.attachChild(objects); rootNode.attachChild(objects);
} }
@ -189,7 +194,6 @@ public class GameWorldView {
ProceduralMapQuadBlock mapQuadBlock = state.proceduralMap.getMapQuadBlock(quad); ProceduralMapQuadBlock mapQuadBlock = state.proceduralMap.getMapQuadBlock(quad);
// Add map objects (for now - trees) // Add map objects (for now - trees)
Spatial treeModel = assetManager.loadModel("Models/tree.j3o");
//System.out.println("Grid @ " + terrainGrid.getLocalTranslation() + " s " + terrainGrid.getLocalScale()); //System.out.println("Grid @ " + terrainGrid.getLocalTranslation() + " s " + terrainGrid.getLocalScale());
//System.out.println("Quad " + quad.getName() + " @ " + quad.getLocalTranslation()); //System.out.println("Quad " + quad.getName() + " @ " + quad.getLocalTranslation());
for (MapObjectInstance mapObject : mapQuadBlock.getMapObjects()) { for (MapObjectInstance mapObject : mapQuadBlock.getMapObjects()) {
@ -202,7 +206,7 @@ public class GameWorldView {
if (control != null) { if (control != null) {
modelInstance.addControl(control); modelInstance.addControl(control);
control.setPhysicsLocation(pos); control.setPhysicsLocation(pos);
physicsSpace.add(control); //physicsSpace.add(control);
} }
objects.attachChild(modelInstance); objects.attachChild(modelInstance);
} }