* Make Geomap and LODGeomap use a float[] heightmap internally instead of FloatBuffer

* Remove normal map buffer from Geomap, not needed since its generated procedurally 
 * Optimized LODGeomap normal buffer generation to create less garbage

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@8734 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
3.0
sha..rd 13 years ago
parent b192166072
commit 268a464191
  1. 209
      engine/src/terrain/com/jme3/terrain/GeoMap.java
  2. 141
      engine/src/terrain/com/jme3/terrain/geomipmap/LODGeomap.java
  3. 11
      engine/src/terrain/com/jme3/terrain/geomipmap/TerrainPatch.java
  4. 8
      engine/src/terrain/com/jme3/terrain/geomipmap/TerrainQuad.java

@ -53,38 +53,25 @@ import java.nio.IntBuffer;
*/
public class GeoMap implements Savable {
protected FloatBuffer hdata;
protected ByteBuffer ndata;
protected float[] hdata;
protected int width, height, maxval;
public GeoMap() {}
public GeoMap(FloatBuffer heightData, ByteBuffer normalData, int width, int height, int maxval){
public GeoMap(float[] heightData, int width, int height, int maxval){
this.hdata = heightData;
this.ndata = normalData;
this.width = width;
this.height = height;
this.maxval = maxval;
}
public GeoMap(int width, int height, int maxval) {
this(BufferUtils.createFloatBuffer(width*height), null, width,height,maxval);
}
public FloatBuffer getHeightData(){
public float[] getHeightData(){
if (!isLoaded())
return null;
return hdata;
}
public ByteBuffer getNormalData(){
if (!isLoaded() || !hasNormalmap())
return null;
return ndata;
}
/**
* @return The maximum possible value that <code>getValue()</code> can
* return. Mostly depends on the source data format (byte, short, int, etc).
@ -105,7 +92,7 @@ public class GeoMap implements Savable {
* @throws NullPointerException If isLoaded() is false
*/
public float getValue(int x, int y) {
return hdata.get(y*width+x);
return hdata[y*width+x];
}
/**
@ -120,50 +107,9 @@ public class GeoMap implements Savable {
* @throws NullPointerException If isLoaded() is false
*/
public float getValue(int i) {
return hdata.get(i);
}
/**
* Returns the normal at a point
*
* If store is null, then a new vector is returned,
* otherwise, the result is stored in the provided vector
* and then returned from this method
*
* @param x the X coordinate
* @param y the Y coordinate
* @param store A preallocated vector for storing the normal data, optional
* @returns store, or a new vector with the normal data if store is null
*
* @throws NullPointerException If isLoaded() or hasNormalmap() is false
*/
public Vector3f getNormal(int x, int y, Vector3f store) {
return getNormal(y*width+x,store);
return hdata[i];
}
/**
* Returns the normal at an index
*
* If store is null, then a new vector is returned,
* otherwise, the result is stored in the provided vector
* and then returned from this method
*
* See getHeight(int) for information about index lookup
*
* @param i the index
* @param store A preallocated vector for storing the normal data, optional
* @returns store, or a new vector with the normal data if store is null
*
* @throws NullPointerException If isLoaded() or hasNormalmap() is false
*/
public Vector3f getNormal(int i, Vector3f store) {
ndata.position( i*3 );
if (store==null) store = new Vector3f();
store.setX( (((float)(ndata.get() & 0xFF)/255f)-0.5f)*2f );
store.setY( (((float)(ndata.get() & 0xFF)/255f)-0.5f)*2f );
store.setZ( (((float)(ndata.get() & 0xFF)/255f)-0.5f)*2f );
return store;
}
/**
* Returns the width of this Geomap
@ -183,43 +129,6 @@ public class GeoMap implements Savable {
return height;
}
/**
* Copies a section of this geomap as a new geomap
*/
public GeoMap copySubGeomap(int x, int y, int w, int h){
FloatBuffer nhdata = BufferUtils.createFloatBuffer(w * h);
hdata.position(y*width+x);
for (int cy = 0; cy < height; cy++){
hdata.limit(hdata.position()+w);
nhdata.put(hdata);
hdata.limit(hdata.capacity());
hdata.position(hdata.position()+width);
}
nhdata.flip();
ByteBuffer nndata = null;
if (ndata!=null){
nndata = BufferUtils.createByteBuffer(w*h*3);
ndata.position( (y*width+x)*3 );
for (int cy = 0; cy < height; cy++){
ndata.limit(ndata.position()+w*3);
nndata.put(ndata);
ndata.limit(ndata.capacity());
ndata.position(ndata.position()+width*3);
}
nndata.flip();
}
return new GeoMap(nhdata,nndata,w,h,maxval);
}
/**
* Returns true if this Geomap has a normalmap associated with it
*/
public boolean hasNormalmap() {
return ndata != null;
}
/**
* Returns true if the Geomap data is loaded in memory
* If false, then the data is unavailable- must be loaded with load()
@ -250,65 +159,55 @@ public class GeoMap implements Savable {
store = BufferUtils.createFloatBuffer(getWidth()*getHeight()*3);
}
store.rewind();
if (!hasNormalmap()){
Vector3f oppositePoint = new Vector3f();
Vector3f adjacentPoint = new Vector3f();
Vector3f rootPoint = new Vector3f();
Vector3f tempNorm = new Vector3f();
int normalIndex = 0;
for (int y = 0; y < getHeight(); y++) {
for (int x = 0; x < getWidth(); x++) {
rootPoint.set(x, getValue(x,y), y);
if (y == getHeight() - 1) {
if (x == getWidth() - 1) { // case #4 : last row, last col
// left cross up
Vector3f oppositePoint = new Vector3f();
Vector3f adjacentPoint = new Vector3f();
Vector3f rootPoint = new Vector3f();
Vector3f tempNorm = new Vector3f();
int normalIndex = 0;
for (int y = 0; y < getHeight(); y++) {
for (int x = 0; x < getWidth(); x++) {
rootPoint.set(x, getValue(x,y), y);
if (y == getHeight() - 1) {
if (x == getWidth() - 1) { // case #4 : last row, last col
// left cross up
// adj = normalIndex - getWidth();
// opp = normalIndex - 1;
adjacentPoint.set(x, getValue(x,y-1), y-1);
oppositePoint.set(x-1, getValue(x-1, y), y);
} else { // case #3 : last row, except for last col
// right cross up
adjacentPoint.set(x, getValue(x,y-1), y-1);
oppositePoint.set(x-1, getValue(x-1, y), y);
} else { // case #3 : last row, except for last col
// right cross up
// adj = normalIndex + 1;
// opp = normalIndex - getWidth();
adjacentPoint.set(x+1, getValue(x+1,y), y);
oppositePoint.set(x, getValue(x,y-1), y-1);
}
} else {
if (x == getWidth() - 1) { // case #2 : last column except for last row
// left cross down
adjacentPoint.set(x-1, getValue(x-1,y), y);
oppositePoint.set(x, getValue(x,y+1), y+1);
adjacentPoint.set(x+1, getValue(x+1,y), y);
oppositePoint.set(x, getValue(x,y-1), y-1);
}
} else {
if (x == getWidth() - 1) { // case #2 : last column except for last row
// left cross down
adjacentPoint.set(x-1, getValue(x-1,y), y);
oppositePoint.set(x, getValue(x,y+1), y+1);
// adj = normalIndex - 1;
// opp = normalIndex + getWidth();
} else { // case #1 : most cases
// right cross down
adjacentPoint.set(x, getValue(x,y+1), y+1);
oppositePoint.set(x+1, getValue(x+1,y), y);
} else { // case #1 : most cases
// right cross down
adjacentPoint.set(x, getValue(x,y+1), y+1);
oppositePoint.set(x+1, getValue(x+1,y), y);
// adj = normalIndex + getWidth();
// opp = normalIndex + 1;
}
}
}
tempNorm.set(adjacentPoint).subtractLocal(rootPoint)
.crossLocal(oppositePoint.subtractLocal(rootPoint));
tempNorm.multLocal(scale).normalizeLocal();
tempNorm.set(adjacentPoint).subtractLocal(rootPoint)
.crossLocal(oppositePoint.subtractLocal(rootPoint));
tempNorm.multLocal(scale).normalizeLocal();
// store.put(tempNorm.x).put(tempNorm.y).put(tempNorm.z);
BufferUtils.setInBuffer(tempNorm, store,
normalIndex);
normalIndex++;
}
}
}else{
Vector3f temp = new Vector3f();
for (int z = 0; z < getHeight(); z++){
for (int x = 0; x < getWidth(); x++){
getNormal(x,z,temp);
store.put(temp.x).put(temp.y).put(temp.z);
}
BufferUtils.setInBuffer(tempNorm, store,
normalIndex);
normalIndex++;
}
}
@ -337,9 +236,8 @@ public class GeoMap implements Savable {
}else{
store = BufferUtils.createFloatBuffer(width*height*3);
}
hdata.rewind();
assert hdata.limit() == height*width;
assert hdata.length == height*width;
Vector3f offset = new Vector3f(-getWidth() * scale.x * 0.5f,
0,
@ -347,10 +245,11 @@ public class GeoMap implements Savable {
if (!center)
offset.zero();
int i = 0;
for (int z = 0; z < height; z++){
for (int x = 0; x < width; x++){
store.put( (float)x*scale.x + offset.x );
store.put( (float)hdata.get()*scale.y );
store.put( (float)hdata[i++]*scale.y );
store.put( (float)z*scale.z + offset.z );
}
}
@ -433,24 +332,6 @@ public class GeoMap implements Savable {
return m;
}
/**
* Populate the height data from the supplied mesh.
* The mesh's dimensions should be the same as width and height
* of this geomap
*/
public void populateHdataFromMesh(Mesh mesh) {
hdata = BufferUtils.createFloatBuffer(width*height);
hdata.rewind();
VertexBuffer pb = mesh.getBuffer(Type.Position);
FloatBuffer fb = (FloatBuffer) pb.getData();
for (int r=0; r<height; r++) {
for (int c=0; c<width; c++) {
float f = fb.get( (width*r) + c + 1);
hdata.put( f );
}
}
}
public void write(JmeExporter ex) throws IOException {
OutputCapsule oc = ex.getCapsule(this);
oc.write(hdata, "hdata", null);
@ -461,7 +342,7 @@ public class GeoMap implements Savable {
public void read(JmeImporter im) throws IOException {
InputCapsule ic = im.getCapsule(this);
hdata = ic.readFloatBuffer("hdata", null);
hdata = ic.readFloatArray("hdata", null);
width = ic.readInt("width", 0);
height = ic.readInt("height", 0);
maxval = ic.readInt("maxval", 0);

@ -49,6 +49,7 @@ import com.jme3.scene.VertexBuffer;
import com.jme3.scene.VertexBuffer.Type;
import com.jme3.util.BufferUtils;
import com.jme3.util.TangentBinormalGenerator;
import com.jme3.util.TempVars;
import java.io.IOException;
/**
@ -69,8 +70,8 @@ public class LODGeomap extends GeoMap {
public LODGeomap() {
}
public LODGeomap(int size, FloatBuffer heightMap) {
super(heightMap, null, size, size, 1);
public LODGeomap(int size, float[] heightMap) {
super(heightMap, size, size, 1);
}
public Mesh createMesh(Vector3f scale, Vector2f tcScale, Vector2f tcOffset, float offsetAmount, int totalSize, boolean center) {
@ -98,10 +99,6 @@ public class LODGeomap extends GeoMap {
return m;
}
protected void removeNormalBuffer() {
ndata = null;
}
public FloatBuffer writeTexCoordArray(FloatBuffer store, Vector2f offset, Vector2f scale, float offsetAmount, int totalSize) {
if (store != null) {
if (store.remaining() < getWidth() * getHeight() * 2) {
@ -762,94 +759,150 @@ public class LODGeomap extends GeoMap {
}
store.rewind();
Vector3f rootPoint = new Vector3f();
Vector3f rightPoint = new Vector3f();
Vector3f leftPoint = new Vector3f();
Vector3f topPoint = new Vector3f();
Vector3f bottomPoint = new Vector3f();
TempVars vars = TempVars.get();
Vector3f rootPoint = vars.vect1;
Vector3f rightPoint = vars.vect2;
Vector3f leftPoint = vars.vect3;
Vector3f topPoint = vars.vect4;
Vector3f bottomPoint = vars.vect5;
Vector3f tmp1 = vars.vect6;
Vector3f tmp2 = vars.vect7;
// calculate normals for each polygon
for (int r = 0; r < getHeight(); r++) {
for (int c = 0; c < getWidth(); c++) {
rootPoint.set(c, getValue(c, r), r);
Vector3f normal = new Vector3f();
Vector3f normal = vars.vect8;
if (r == 0) { // first row
if (c == 0) { // first column
rightPoint.set(c + 1, getValue(c + 1, r), r);
bottomPoint.set(c, getValue(c, r + 1), r + 1);
normal.set(getNormal(bottomPoint, rootPoint, rightPoint, scale));
getNormal(bottomPoint, rootPoint, rightPoint, scale, normal);
normal.set(Vector3f.UNIT_Y);
} else if (c == getWidth() - 1) { // last column
leftPoint.set(c - 1, getValue(c - 1, r), r);
bottomPoint.set(c, getValue(c, r + 1), r + 1);
normal.set(getNormal(leftPoint, rootPoint, bottomPoint, scale));
getNormal(leftPoint, rootPoint, bottomPoint, scale, normal);
normal.set(Vector3f.UNIT_Y);
} else { // all middle columns
leftPoint.set(c - 1, getValue(c - 1, r), r);
rightPoint.set(c + 1, getValue(c + 1, r), r);
bottomPoint.set(c, getValue(c, r + 1), r + 1);
Vector3f n1 = getNormal(leftPoint, rootPoint, bottomPoint, scale);
Vector3f n2 = getNormal(bottomPoint, rootPoint, rightPoint, scale);
normal.set(n1.add(n2).normalizeLocal());
normal.set( getNormal(leftPoint, rootPoint, bottomPoint, scale, tmp1) );
normal.add( getNormal(bottomPoint, rootPoint, rightPoint, scale, tmp1) );
normal.normalizeLocal();
normal.set(Vector3f.UNIT_Y);
}
} else if (r == getHeight() - 1) { // last row
if (c == 0) { // first column
topPoint.set(c, getValue(c, r - 1), r - 1);
rightPoint.set(c + 1, getValue(c + 1, r), r);
normal.set(getNormal(rightPoint, rootPoint, topPoint, scale));
getNormal(rightPoint, rootPoint, topPoint, scale, normal);
normal.set(Vector3f.UNIT_Y);
} else if (c == getWidth() - 1) { // last column
topPoint.set(c, getValue(c, r - 1), r - 1);
leftPoint.set(c - 1, getValue(c - 1, r), r);
normal.set(getNormal(topPoint, rootPoint, leftPoint, scale));
getNormal(topPoint, rootPoint, leftPoint, scale, normal);
normal.set(Vector3f.UNIT_Y);
} else { // all middle columns
topPoint.set(c, getValue(c, r - 1), r - 1);
leftPoint.set(c - 1, getValue(c - 1, r), r);
rightPoint.set(c + 1, getValue(c + 1, r), r);
Vector3f n1 = getNormal(topPoint, rootPoint, leftPoint, scale);
Vector3f n2 = getNormal(rightPoint, rootPoint, topPoint, scale);
normal.set(n1.add(n2).normalizeLocal());
normal.set( getNormal(topPoint, rootPoint, leftPoint, scale, tmp1) );
normal.add( getNormal(rightPoint, rootPoint, topPoint, scale, tmp1) );
normal.normalizeLocal();
normal.set(Vector3f.UNIT_Y);
}
} else { // all middle rows
if (c == 0) { // first column
topPoint.set(c, getValue(c, r - 1), r - 1);
rightPoint.set(c + 1, getValue(c + 1, r), r);
bottomPoint.set(c, getValue(c, r + 1), r + 1);
Vector3f n1 = getNormal(rightPoint, rootPoint, topPoint, scale);
Vector3f n2 = getNormal(bottomPoint, rootPoint, rightPoint, scale);
normal.set(n1.add(n2).normalizeLocal());
normal.set( getNormal(rightPoint, rootPoint, topPoint, scale, tmp1) );
normal.add( getNormal(bottomPoint, rootPoint, rightPoint, scale, tmp1) );
normal.normalizeLocal();
normal.set(Vector3f.UNIT_Y);
} else if (c == getWidth() - 1) { // last column
topPoint.set(c, getValue(c, r - 1), r - 1);
leftPoint.set(c - 1, getValue(c - 1, r), r);
bottomPoint.set(c, getValue(c, r + 1), r + 1);
Vector3f n1 = getNormal(topPoint, rootPoint, leftPoint, scale);
Vector3f n2 = getNormal(leftPoint, rootPoint, bottomPoint, scale);
normal.set(n1.add(n2).normalizeLocal());
normal.set( getNormal(topPoint, rootPoint, leftPoint, scale, tmp1) );
normal.add( getNormal(leftPoint, rootPoint, bottomPoint, scale, tmp1) );
normal.normalizeLocal();
normal.set(Vector3f.UNIT_Y);
} else { // all middle columns
topPoint.set(c, getValue(c, r - 1), r - 1);
leftPoint.set(c - 1, getValue(c - 1, r), r);
rightPoint.set(c + 1, getValue(c + 1, r), r);
bottomPoint.set(c, getValue(c, r + 1), r + 1);
Vector3f n1 = getNormal(topPoint, rootPoint, leftPoint, scale);
Vector3f n2 = getNormal(leftPoint, rootPoint, bottomPoint, scale);
Vector3f n3 = getNormal(bottomPoint, rootPoint, rightPoint, scale);
Vector3f n4 = getNormal(rightPoint, rootPoint, topPoint, scale);
normal.set(n1.add(n2).add(n3).add(n4).normalizeLocal());
normal.set( getNormal(topPoint, rootPoint, leftPoint, scale, tmp1 ) );
normal.add( getNormal(leftPoint, rootPoint, bottomPoint, scale, tmp1) );
normal.add( getNormal(bottomPoint, rootPoint, rightPoint, scale, tmp1) );
normal.add( getNormal(rightPoint, rootPoint, topPoint, scale, tmp1) );
normal.normalizeLocal();
normal.set(Vector3f.UNIT_Y);
}
}
normal.set(Vector3f.UNIT_Y);
BufferUtils.setInBuffer(normal, store, (r * getWidth() + c)); // save the normal
}
}
vars.release();
return store;
}
private Vector3f getNormal(Vector3f firstPoint, Vector3f rootPoint, Vector3f secondPoint, Vector3f scale) {
Vector3f normal = new Vector3f();
//scale = Vector3f.UNIT_XYZ;
normal.set(firstPoint.mult(scale)).subtractLocal(rootPoint.mult(scale)).crossLocal(secondPoint.mult(scale).subtract(rootPoint.mult(scale))).normalizeLocal();
return normal;
private Vector3f getNormal(Vector3f firstPoint, Vector3f rootPoint, Vector3f secondPoint, Vector3f scale, Vector3f store) {
// store.set(firstPoint).subtractLocal(rootPoint);
// tmp.set(secondPoint).subtractLocal(rootPoint);
// store.crossLocal(tmp).mult(scale).normalizeLocal();
// store.set(
// firstPoint.mult(scale)).subtractLocal(rootPoint.mult(scale))
// .crossLocal(
// secondPoint.mult(scale).subtract(rootPoint.mult(scale))).normalizeLocal();
float x1 = firstPoint.x - rootPoint.x;
float y1 = firstPoint.y - rootPoint.y;
float z1 = firstPoint.z - rootPoint.z;
float x2 = secondPoint.x - rootPoint.x;
float y2 = secondPoint.y - rootPoint.y;
float z2 = secondPoint.z - rootPoint.z;
float x3 = (y1 * z2) - (z1 * y2);
float y3 = (z1 * x2) - (x1 * z2);
float z3 = (x1 * y2) - (y1 * x2);
x3 *= scale.x;
y3 *= scale.y;
z3 *= scale.z;
float inv = 1.0f / FastMath.sqrt(x3 * x3 + y3 * y3 + z3 * z3);
store.x = x3 * inv;
store.y = y3 * inv;
store.z = z3 * inv;
return store;
}
/**
@ -953,10 +1006,10 @@ public class LODGeomap extends GeoMap {
Triangle t = new Triangle(new Vector3f(), new Vector3f(), new Vector3f());
Triangle t2 = new Triangle(new Vector3f(), new Vector3f(), new Vector3f());
float h1 = hdata.get(index); // top left
float h2 = hdata.get(index + 1); // top right
float h3 = hdata.get(index + width); // bottom left
float h4 = hdata.get(index + width + 1); // bottom right
float h1 = hdata[index]; // top left
float h2 = hdata[index + 1]; // top right
float h3 = hdata[index + width]; // bottom left
float h4 = hdata[index + width + 1]; // bottom right
if ((gridX == 0 && gridY == 0) || (gridX == width - 1 && gridY == width - 1)) {

@ -186,10 +186,7 @@ public class TerrainPatch extends Geometry {
setLocalTranslation(origin);
FloatBuffer heightBuffer = BufferUtils.createFloatBuffer(size*size);
heightBuffer.put(heightMap);
geomap = new LODGeomap(size, heightBuffer);
geomap = new LODGeomap(size, heightMap);
Mesh m = geomap.createMesh(stepScale, new Vector2f(1,1), offset, offsetAmount, totalSize, false);
setMesh(m);
@ -216,7 +213,7 @@ public class TerrainPatch extends Geometry {
return lodEntropy;
}
public FloatBuffer getHeightmap() {
public float[] getHeightmap() {
return geomap.getHeightData();
}
@ -300,10 +297,10 @@ public class TerrainPatch extends Geometry {
continue;
int idx = lh.z * size + lh.x;
if (overrideHeight) {
geomap.getHeightData().put(idx, lh.h);
geomap.getHeightData()[idx] = lh.h;
} else {
float h = getMesh().getFloatBuffer(Type.Position).get(idx*3+1);
geomap.getHeightData().put(idx, h+lh.h);
geomap.getHeightData()[idx] = h+lh.h;
}
}

@ -1796,13 +1796,13 @@ public class TerrainQuad extends Node implements Terrain {
if (getChild(0) instanceof TerrainPatch) {
for (Spatial s : getChildren()) {
if ( ((TerrainPatch)s).getQuadrant() == 1)
ul = BufferUtils.getFloatArray(((TerrainPatch)s).getHeightmap());
ul = ((TerrainPatch)s).getHeightmap();
else if(((TerrainPatch) s).getQuadrant() == 2)
bl = BufferUtils.getFloatArray(((TerrainPatch)s).getHeightmap());
bl = ((TerrainPatch)s).getHeightmap();
else if(((TerrainPatch) s).getQuadrant() == 3)
ur = BufferUtils.getFloatArray(((TerrainPatch)s).getHeightmap());
ur = ((TerrainPatch)s).getHeightmap();
else if(((TerrainPatch) s).getQuadrant() == 4)
br = BufferUtils.getFloatArray(((TerrainPatch)s).getHeightmap());
br = ((TerrainPatch)s).getHeightmap();
}
}
else {

Loading…
Cancel
Save