I’ve been doing a terrain editor in jME3, but I’m having trouble when the region fields meet up:
I ripped the BufferGeomap, and created a more straight forward version of a heightmap.
Obviously, I’ll need the surrounding data implemented in this class somehow, but which method do I need to mess with?
package com.conquest.terrain;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.scene.Mesh;
import com.jme3.scene.VertexBuffer.Type;
import com.jme3.util.BufferUtils;
/**
* A height map is a 2d representation of a 3d terrain. It's constructed from an array of height values.
*/
public class HeightMap extends Mesh {
/**
* The terrain scaling factor.
*/
public static final float SCALE = 1f;
/**
* The scaling factor used when a mesh is produced from a geometry map.
*/
public static final Vector3f TERRAIN_SCALE = new Vector3f(SCALE, SCALE / 10000f, SCALE);
/**
* The texture scaling factor used when a mesh is produced.
* How many textures should cover the terrain?
* Obviously one. (1x1)
*/
public static final Vector2f TEXTURE_SCALE = new Vector2f(1f, 1f);
/**
* The width of this map.
*/
private final int width;
/**
* The height of this map.
*/
private final int height;
/**
* The integer array holding height values.
*/
private final int[] data;
/**
* Constructs a new heightmap.
* @param width The width of the heightmap.
* @param height The height of the heightmap.
* @param data The height data.
* @throws IllegalArgumentException if data.length != width * height + 1
*/
public HeightMap(int width, int height, int[] data) {
super();
if(data.length != width * height) {
throw new IllegalArgumentException("Invalid data size: " + data.length + " should have been: " + (width * height));
}
this.width = width;
this.height = height;
this.data = data;
/*
* Should this piece of code be called from a different thread, for performance improments?
*/
setBuffer(Type.Position, 3, createVertexBuffer(TERRAIN_SCALE));
setBuffer(Type.Normal, 3, createNormalBuffer(TERRAIN_SCALE));
setBuffer(Type.TexCoord, 2, createTextureCoordinateBuffer(TEXTURE_SCALE));
setBuffer(Type.Index, 3, createIndexBuffer());
setStatic();
updateBound();
}
/**
* Gets the height value of a specific coordinate set.
* @return The height value of a specific coordinate set.
*/
public int getValue(int x, int y) {
return getValue(y*width+x);
}
/**
* Gets the height value of a specific index.
* @return The height value of a specific index.
*/
public int getValue(int index) {
return data[index];
}
/**
* Gets the width of this map.
* @return The width of this map.
*/
public int getWidth() {
return width;
}
/**
* Gets the height of this map.
* @return The height of this map.
*/
public int getHeight() {
return height;
}
/**
* Gets the height data of this map.
* @return The height data of this map.
*/
public int[] getData() {
return data;
}
public FloatBuffer createTextureCoordinateBuffer(Vector2f scale){
FloatBuffer store = BufferUtils.createFloatBuffer(width * height * 2);
for (int y = 0; y < height; y++){
for (int x = 0; x < width; x++){
float tcx = (float)x / (float)width;
float tcy = (float)y / (float)height;
store.put(tcx * scale.x);
store.put(tcy * scale.y);
}
}
return store;
}
/**
* Creates a float buffer containing all the corner points for this map.
*/
public FloatBuffer createVertexBuffer(Vector3f scale) {
FloatBuffer store = BufferUtils.createFloatBuffer(width * height * 3);
int index = 0;
for (int z = 0; z < height; z++){
for (int x = 0; x < width; x++){
store.put(x * scale.x);
store.put(data[index++] * scale.y);
store.put(z * scale.z);
if(x == 0) {
//G
}
}
}
return store;
}
/*
* (non-Javadoc)
* Subclasses are encourged to provide a better implementation
* which directly accesses the data rather than using getNormal
*/
public FloatBuffer createNormalBuffer(Vector3f scale) {
FloatBuffer store = BufferUtils.createFloatBuffer(width * height * 3);
Vector3f oppositePoint = new Vector3f();
Vector3f adjacentPoint = new Vector3f();
Vector3f rootPoint = new Vector3f();
Vector3f tempNorm = new Vector3f();
int normalIndex = 0;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
rootPoint.set(x, getValue(x,y), y);
if (y == height - 1) {
if (x == width - 1) { // case #4
// left cross up
adjacentPoint.set(x, getValue(x,y-1), y-1);
oppositePoint.set(x-1, getValue(x-1, y), y);
} else { // case #3
// right cross up
adjacentPoint.set(x+1, getValue(x+1,y), y);
oppositePoint.set(x, getValue(x,y-1), y-1);
}
} else {
if (x == width - 1) { // case #2
// left cross down
adjacentPoint.set(x-1, getValue(x-1,y), y);
oppositePoint.set(x, getValue(x,y+1), y+1);
} else { // case #1
// right cross down
adjacentPoint.set(x, getValue(x,y+1), y+1);
oppositePoint.set(x+1, getValue(x+1,y), y);
}
}
tempNorm.set(adjacentPoint).subtractLocal(rootPoint).crossLocal(oppositePoint.subtractLocal(rootPoint));
tempNorm.multLocal(scale).normalizeLocal();
BufferUtils.setInBuffer(tempNorm, store, normalIndex++);
}
}
return store;
}
public IntBuffer createIndexBuffer(){
int faceN = (width-1)*(height-1)*2;
IntBuffer store = BufferUtils.createIntBuffer(faceN*3);
int i = 0;
for (int z = 0; z < height-1; z++){
for (int x = 0; x < width-1; x++){
store.put(i).put(i+width).put(i+width+1);
store.put(i+width+1).put(i+1).put(i);
i++;
// TODO: There's probably a better way to do this..
if (x==width-2) {
i++;
}
}
}
store.flip();
return store;
}
}
Thoughts: I could combine alot of those regions as one huge heightmap, but I'd be better off with small blocks, culling away. (Heigher FPS results)
Thanks in advance.