New TerrainManager class implemented

This commit is contained in:
Ziver Koc 2020-06-04 23:25:08 +02:00
parent 7966d86a2d
commit 33258c7820
5 changed files with 211 additions and 23 deletions

View file

@ -47,7 +47,6 @@ public class CookeryClient extends SimpleApplication {
// cam.lookAt(new Vector3f(0f,0f,0f), Vector3f.UNIT_Z);
flyCam.setEnabled(false);
// stateManager.detach(stateManager.getState(FlyCamAppState.class));
ChaseCamera chaseCam = new ChaseCamera(cam, player.getGfxNode(), inputManager);
chaseCam.setDefaultDistance(10);
chaseCam.setRotationSpeed(2);

View file

@ -0,0 +1,15 @@
package se.cookery.gfx.terrain;
public interface TerrainHeightMap {
/**
* @return the number of squares on the width that make a single TerrainMesh instance.
*/
int getSquareCountWidth();
/**
* @return the number of squares on the height that make a single TerrainMesh instance.
*/
int getSquareCountHeight();
}

View file

@ -0,0 +1,81 @@
package se.cookery.gfx.terrain;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import se.cookery.gfx.util.MaterialUtil;
/**
* This class represents a infinite terrain object (using multiple buffered TerrainMesh objects)
*/
public class TerrainManager {
protected static final int BUFFER_SIZE = 3;
private float squareWidth;
private float squareHeight;
private TerrainHeightMap heightMap;
private Geometry[][] terrainBuffer;
private Vector3f terrainZero; // The top-left coordinate for the center square
private Node node = new Node("Terrain Node");
/**
* Create a new instance of TerrainManager
*
* @param squareWidth is the width of a single square
* @param squareHeight is the height of a single square
* @param heightMap is the height map for the terrain
*/
public TerrainManager(float squareWidth, float squareHeight, TerrainHeightMap heightMap, Material material) {
this.squareWidth = squareWidth;
this.squareHeight = squareHeight;
this.heightMap = heightMap;
this.terrainBuffer = new Geometry[BUFFER_SIZE][BUFFER_SIZE];
for (int x=0; x<BUFFER_SIZE; x++) {
for (int z=0; z<BUFFER_SIZE; z++) {
TerrainMesh terrainMesh = new TerrainMesh(squareWidth, squareHeight, heightMap.getSquareCountWidth(), heightMap.getSquareCountHeight());
terrainBuffer[x][z] = new Geometry("TerrainMesh " + x + "x" + z, terrainMesh);
terrainBuffer[x][z].setMaterial(material);
node.attachChild(terrainBuffer[x][z]);
}
}
setTerrainCenter(Vector3f.ZERO);
}
/**
* Method will move the terrain center by reorganizing the buffer
* by deallocating out of scope terrain and allocating new terrain that is in scope.
*/
protected void setTerrainCenter(Vector3f newCenter) {
Vector3f newTerrainZero = new Vector3f(
(int) newCenter.getX()/(squareWidth*heightMap.getSquareCountWidth()),
0,
(int) newCenter.getZ()/(squareHeight*heightMap.getSquareCountHeight()));
if (newTerrainZero.equals(terrainZero))
return;
terrainZero = newTerrainZero;
int centerIndex = BUFFER_SIZE/2;
for (int x=0; x<BUFFER_SIZE; x++) {
for (int z=0; z<BUFFER_SIZE; z++) {
TerrainMesh terrainMesh = (TerrainMesh) terrainBuffer[x][z].getMesh();
terrainBuffer[x][z].setLocalTranslation(
terrainZero.getX() + (x - centerIndex) * terrainMesh.getWidth(),
0 ,
terrainZero.getZ() + (z - centerIndex) * terrainMesh.getHeight());
}
}
}
public Node getNode() {
return node;
}
}

View file

@ -9,24 +9,25 @@ import com.jme3.scene.VertexBuffer;
*/
public class TerrainMesh extends Mesh {
private float width;
private float height;
private int widthCount;
private int heightCount;
private float squareWidth;
private float squareHeight;
private int squareCountWidth;
private int squareCountHeight;
/**
* Created a terrain Mesh with the given sizes.
*
* @param width is the width of a single square
* @param height is the height of a single square
* @param widthCount the number of squares that make up the whole terrain
* @param heightCount the number of squares that make up the whole terrain
* @param squareWidth is the width of a single square
* @param squareHeight is the height of a single square
* @param squareCountWidth the number of squares that make up the whole terrain
* @param squareCountHeight the number of squares that make up the whole terrain
*/
public TerrainMesh(float width, float height, int widthCount, int heightCount){
this.width = width;
this.height = height;
this.widthCount = widthCount;
this.heightCount = heightCount;
public TerrainMesh(float squareWidth, float squareHeight, int squareCountWidth, int squareCountHeight){
this.squareWidth = squareWidth;
this.squareHeight = squareHeight;
this.squareCountWidth = squareCountWidth;
this.squareCountHeight = squareCountHeight;
updateGeometry();
}
@ -45,16 +46,16 @@ public class TerrainMesh extends Mesh {
* Generates a vertex buffer containing vertex xyz coordinates.
*/
protected float[] generateVertexBuffer() {
int vertexCountWidth = widthCount + 1;
int vertexCountHeight = heightCount + 1;
int vertexCountWidth = squareCountWidth + 1;
int vertexCountHeight = squareCountHeight + 1;
float[] vertexBuf = new float[3 * vertexCountWidth * vertexCountHeight];
for (int row=0; row<vertexCountHeight; row++) {
for (int col=0; col<vertexCountWidth; col++) {
int vertexIndex = (3 * vertexCountWidth * row) + (3 * col);
vertexBuf[vertexIndex + 0] = width * col; // X
vertexBuf[vertexIndex + 0] = squareWidth * col; // X
vertexBuf[vertexIndex + 1] = 0; // Y
vertexBuf[vertexIndex + 2] = height * row; // Z
vertexBuf[vertexIndex + 2] = squareHeight * row; // Z
}
}
@ -65,17 +66,17 @@ public class TerrainMesh extends Mesh {
* Generates a index buffer containing planes for the mesh.
*/
protected short[] generateIndexBuffer() {
int vertexCountWidth = widthCount + 1;
int triangleCount = widthCount * heightCount * 2;
int vertexCountWidth = squareCountWidth + 1;
int triangleCount = squareCountWidth * squareCountHeight * 2;
short[] indexBuf = new short[3 * triangleCount];
for (int row=0; row<heightCount; row++) {
for (int col = 0; col<widthCount; col++) {
for (int row = 0; row< squareCountHeight; row++) {
for (int col = 0; col< squareCountWidth; col++) {
int vertexTopLeft = (row * vertexCountWidth) + col;
int vertexTopRight = vertexTopLeft + 1;
int vertexBottomLeft = ((row + 1) * vertexCountWidth) + col;
int vertexBottomRight = vertexBottomLeft + 1;
int indexOffset = 3 * 2 * ((row * widthCount) + col);
int indexOffset = 3 * 2 * ((row * squareCountWidth) + col);
indexBuf[indexOffset + 0] = (short) vertexTopLeft;
indexBuf[indexOffset + 1] = (short) vertexBottomLeft;
@ -114,4 +115,21 @@ public class TerrainMesh extends Mesh {
0, 0, 1
};
}
public float getWidth() {
return squareWidth * squareCountWidth;
}
public float getHeight() {
return squareHeight * squareCountHeight;
}
public float getSquareWidth() {
return squareWidth;
}
public float getSquareHeight() {
return squareHeight;
}
}

View file

@ -0,0 +1,75 @@
package se.cookery.gfx.terrain;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class TerrainManagerTest {
private static class TerrainHeightMapImpl implements TerrainHeightMap {
public int getSquareCountWidth() {
return 1;
}
public int getSquareCountHeight() {
return 1;
}
}
@Test
void initialization() {
TerrainManager manager = new TerrainManager(1, 1, new TerrainHeightMapImpl(), null);
Node node = manager.getNode();
assertEquals(TerrainManager.BUFFER_SIZE*TerrainManager.BUFFER_SIZE, node.getChildren().size());
for (int x=0; x<TerrainManager.BUFFER_SIZE; x++) {
for (int y=0; y<TerrainManager.BUFFER_SIZE; y++) {
assertNotNull(node.getChild("TerrainMesh " + x + "x" + y), "TerrainMesh missing: TerrainMesh " + x + "x" + y);
}
}
}
@Test
void setTerrainCenter() {
TerrainManager manager = new TerrainManager(1, 1, new TerrainHeightMapImpl(), null);
Node node = manager.getNode();
assertEquals(new Vector3f(-1, 0, -1), node.getChild("TerrainMesh 0x0").getLocalTranslation());
assertEquals(new Vector3f( 0, 0, -1), node.getChild("TerrainMesh 1x0").getLocalTranslation());
assertEquals(new Vector3f( 1, 0, -1), node.getChild("TerrainMesh 2x0").getLocalTranslation());
assertEquals(new Vector3f(-1, 0, 0), node.getChild("TerrainMesh 0x1").getLocalTranslation());
assertEquals(new Vector3f( 0, 0, 0), node.getChild("TerrainMesh 1x1").getLocalTranslation());
assertEquals(new Vector3f( 1, 0, 0), node.getChild("TerrainMesh 2x1").getLocalTranslation());
assertEquals(new Vector3f(-1, 0, 1), node.getChild("TerrainMesh 0x2").getLocalTranslation());
assertEquals(new Vector3f( 0, 0, 1), node.getChild("TerrainMesh 1x2").getLocalTranslation());
assertEquals(new Vector3f( 1, 0, 1), node.getChild("TerrainMesh 2x2").getLocalTranslation());
manager.setTerrainCenter(new Vector3f(10, 0, 10));
assertEquals(new Vector3f( 9, 0, 9), node.getChild("TerrainMesh 0x0").getLocalTranslation());
assertEquals(new Vector3f(10, 0, 9), node.getChild("TerrainMesh 1x0").getLocalTranslation());
assertEquals(new Vector3f(11, 0, 9), node.getChild("TerrainMesh 2x0").getLocalTranslation());
assertEquals(new Vector3f( 9, 0, 10), node.getChild("TerrainMesh 0x1").getLocalTranslation());
assertEquals(new Vector3f(10, 0, 10), node.getChild("TerrainMesh 1x1").getLocalTranslation());
assertEquals(new Vector3f(11, 0, 10), node.getChild("TerrainMesh 2x1").getLocalTranslation());
assertEquals(new Vector3f( 9, 0, 11), node.getChild("TerrainMesh 0x2").getLocalTranslation());
assertEquals(new Vector3f(10, 0, 11), node.getChild("TerrainMesh 1x2").getLocalTranslation());
assertEquals(new Vector3f(11, 0, 11), node.getChild("TerrainMesh 2x2").getLocalTranslation());
manager.setTerrainCenter(new Vector3f(0.1f, 0, 0.1f));
assertEquals(new Vector3f(-1, 0, -1), node.getChild("TerrainMesh 0x0").getLocalTranslation());
assertEquals(new Vector3f( 0, 0, -1), node.getChild("TerrainMesh 1x0").getLocalTranslation());
assertEquals(new Vector3f( 1, 0, -1), node.getChild("TerrainMesh 2x0").getLocalTranslation());
assertEquals(new Vector3f(-1, 0, 0), node.getChild("TerrainMesh 0x1").getLocalTranslation());
assertEquals(new Vector3f( 0, 0, 0), node.getChild("TerrainMesh 1x1").getLocalTranslation());
assertEquals(new Vector3f( 1, 0, 0), node.getChild("TerrainMesh 2x1").getLocalTranslation());
assertEquals(new Vector3f(-1, 0, 1), node.getChild("TerrainMesh 0x2").getLocalTranslation());
assertEquals(new Vector3f( 0, 0, 1), node.getChild("TerrainMesh 1x2").getLocalTranslation());
assertEquals(new Vector3f( 1, 0, 1), node.getChild("TerrainMesh 2x2").getLocalTranslation());
}
}