Here is my code. I am sure I am doing silly things in places. Preliminary tests with big maps aren’t promising either with me needing to allocate 120Mb to the jvm just to draw a 128x128 map using tiles of only 4 vertices each.
The FPS on a 128x128 map drops to 17 even on my beastly machine as opposed to around 590 FPS for a 32x32 map so I am not sure if the clipping or the qaudtree is being built correctly. Do I need to be putting bounding boxes around the Nodes? I’m also not sure if I am handling the TriMeshes and SharedMeshes and thier textures, render states and bounding boxes properly.
Please let me know if you see any mistakes.
import java.net.URL;
import java.util.HashMap;
import com.jme.app.SimpleGame;
import com.jme.scene.Node;
import com.jme.scene.TriMesh;
import com.jme.scene.SharedMesh;
import com.jme.bounding.BoundingBox;
import com.jme.math.Vector3f;
import com.jme.math.Vector2f;
import com.jme.image.Image;
import com.jme.image.Texture;
import com.jme.util.TextureManager;
import com.jme.scene.state.TextureState;
import com.jme.system.JmeException;
import com.jme.input.*;
public class QuadTree extends SimpleGame {
private final int tileSize = 1;
private int xStart;
private int zStart;
int[][] testMap = new int[64][64];
URL waterImage;
URL grassImage;
private HashMap sharedTiles = new HashMap();
public static void main(String[] args) {
QuadTree app = new QuadTree();
app.setDialogBehaviour(SimpleGame.ALWAYS_SHOW_PROPS_DIALOG);
app.start();
}
public QuadTree() {
int tilesInXDirection = testMap[0].length;
int tilesInZDirection = testMap.length;
for (int x = 0; x < tilesInXDirection; x++) {
for (int z = 0; z < tilesInZDirection; z++) {
testMap[z][x] = 2;
}
}
xStart = (int) (0 - ((tileSize * tilesInXDirection) / 2));
zStart = (int) (0 - ((tileSize * (tilesInZDirection)) / 2));
waterImage = QuadTree.class.getClassLoader().getResource("img/water.png");
grassImage = QuadTree.class.getClassLoader().getResource("img/grass.jpg");
}
private SharedMesh generateTile(int tileX, int tileZ) {
int tileType = testMap[tileZ][tileX];
SharedMesh sharedMesh;
TriMesh realMesh;
if (sharedTiles.containsKey(String.valueOf(tileType))) {
realMesh = (TriMesh) sharedTiles.get(String.valueOf(tileType));
} else {
realMesh = new TriMesh(String.valueOf(tileType));
sharedTiles.put(String.valueOf(tileType), realMesh);
Vector3f[] vertexes={
new Vector3f(0,0,0),
new Vector3f(tileSize,0,0),
new Vector3f(0,0,tileSize),
new Vector3f(tileSize,0,tileSize)
};
Vector2f[] texCoords={
new Vector2f(0,0),
new Vector2f(1,0),
new Vector2f(0,1),
new Vector2f(1,1)
};
int[] indexes = {0, 1, 2, 1, 2, 3};
realMesh.reconstruct(vertexes, null, null, texCoords, indexes);
realMesh.setModelBound(new BoundingBox());
realMesh.updateModelBound();
TextureState ts = display.getRenderer().createTextureState();
URL useTexture;
if (tileType == 1) {
useTexture = waterImage;
} else {
useTexture = grassImage;
}
Texture t = TextureManager.loadTexture(useTexture, Texture.MM_LINEAR_LINEAR, Texture.FM_LINEAR, Image.GUESS_FORMAT_NO_S3TC, 1.0f, true);
ts.setTexture(t);
realMesh.setRenderState(ts);
}
sharedMesh = new SharedMesh(String.valueOf(tileX) + "," + String.valueOf(tileZ), realMesh);
int realX = xStart + (tileX * tileSize);
int realZ = zStart + (tileZ * tileSize);
sharedMesh.setLocalTranslation(new Vector3f(realX, 0, realZ));
return sharedMesh;
}
private Node buildNode(int x1, int z1, int x2, int z2) {
int Dx = x2 - x1;
int Dz = z2 - z1;
Node thisNode = new Node(x1 + "," + x2 + "x" + z1 + "," + z2);
if ((Dx / 2 > 1) && (Dz / 2 > 1)) {
// create 4 holding nodes
thisNode.attachChild(buildNode(x1, z1, x1 + Dx / 2, z1 + Dz / 2));
thisNode.attachChild(buildNode(x1, z1 + Dz / 2, x1 + Dx / 2, z2));
thisNode.attachChild(buildNode(x1 + Dx / 2, z1, x2, z1 + Dz / 2));
thisNode.attachChild(buildNode(x1 + Dx / 2, z1 + Dz / 2, x2, z2));
} else {
// create leaf nodes (the tiles)
thisNode.attachChild(generateTile(x1, z1));
thisNode.attachChild(generateTile(x1, z1 + 1));
thisNode.attachChild(generateTile(x1 + 1, z1));
thisNode.attachChild(generateTile(x1 + 1, z1 + 1));
}
return thisNode;
}
protected void simpleInitGame() {
rootNode.attachChild(buildNode(0, 0, testMap[0].length, testMap.length));
lightState.setEnabled(false);
try {
cam = display.getRenderer().createCamera(display.getWidth(), display.getHeight());
} catch (JmeException e) {
e.printStackTrace();
System.exit(1);
}
cam.setFrustumPerspective(45.0f, (float) display.getWidth() / (float) display.getHeight(), 1, 1000);
Vector3f loc = new Vector3f(0.0f, 90.0f, 0.0f);
Vector3f left = new Vector3f(-1.0f, 0.0f, 0.0f);
Vector3f up = new Vector3f(0.0f, 0.0f, -1.0f);
Vector3f dir = new Vector3f(0.0f, -1.0f, 0.0f);
cam.setFrame(loc, left, up, dir);
cam.update();
display.getRenderer().setCamera(cam);
input = new FirstPersonHandler(this, cam, properties.getRenderer());
input.setKeySpeed(50f);
input.setMouseSpeed(1f);
}
}