flipped terrain texture coordinates. New tangent calculations. Warning if heightmap is different size than specified terrain

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@8060 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
3.0
bre..ns 13 years ago
parent 11b250ffe1
commit 77718d1f15
  1. 85
      engine/src/terrain/com/jme3/terrain/geomipmap/LODGeomap.java
  2. 14
      engine/src/terrain/com/jme3/terrain/geomipmap/TerrainPatch.java
  3. 22
      engine/src/terrain/com/jme3/terrain/geomipmap/TerrainQuad.java

@ -47,6 +47,7 @@ import com.jme3.scene.Mesh;
import com.jme3.scene.Mesh.Mode; import com.jme3.scene.Mesh.Mode;
import com.jme3.scene.VertexBuffer.Type; import com.jme3.scene.VertexBuffer.Type;
import com.jme3.util.BufferUtils; import com.jme3.util.BufferUtils;
import com.jme3.util.TangentBinormalGenerator;
import java.io.IOException; import java.io.IOException;
/** /**
@ -80,10 +81,12 @@ public class LODGeomap extends GeoMap {
FloatBuffer tb = writeTexCoordArray(null, tcOffset, tcScale, offsetAmount, totalSize); FloatBuffer tb = writeTexCoordArray(null, tcOffset, tcScale, offsetAmount, totalSize);
FloatBuffer nb = writeNormalArray(null, scale); FloatBuffer nb = writeNormalArray(null, scale);
IntBuffer ib = writeIndexArrayLodDiff(null, lod, rightLod, topLod, leftLod, bottomLod); IntBuffer ib = writeIndexArrayLodDiff(null, lod, rightLod, topLod, leftLod, bottomLod);
FloatBuffer tanb = writeTangentArray(null, scale);
Mesh m = new Mesh(); Mesh m = new Mesh();
m.setMode(Mode.TriangleStrip); m.setMode(Mode.TriangleStrip);
m.setBuffer(Type.Position, 3, pb); m.setBuffer(Type.Position, 3, pb);
m.setBuffer(Type.Normal, 3, nb); m.setBuffer(Type.Normal, 3, nb);
m.setBuffer(Type.Tangent, 3, tanb);
m.setBuffer(Type.TexCoord, 2, tb); m.setBuffer(Type.TexCoord, 2, tb);
m.setBuffer(Type.Index, 3, ib); m.setBuffer(Type.Index, 3, ib);
m.setStatic(); m.setStatic();
@ -110,8 +113,8 @@ public class LODGeomap extends GeoMap {
Vector2f tcStore = new Vector2f(); Vector2f tcStore = new Vector2f();
for (int y = 0; y < getHeight(); y++) { // work from bottom of heightmap up, so we don't flip the coords
for (int y = getHeight()-1; y >= 0; y--) {
for (int x = 0; x < getWidth(); x++) { for (int x = 0; x < getWidth(); x++) {
getUV(x, y, tcStore, offset, offsetAmount, totalSize); getUV(x, y, tcStore, offset, offsetAmount, totalSize);
float tx = tcStore.x * scale.x; float tx = tcStore.x * scale.x;
@ -125,8 +128,8 @@ public class LODGeomap extends GeoMap {
} }
public Vector2f getUV(int x, int y, Vector2f store, Vector2f offset, float offsetAmount, int totalSize) { public Vector2f getUV(int x, int y, Vector2f store, Vector2f offset, float offsetAmount, int totalSize) {
float offsetX = offset.x + (offsetAmount * 1.0f);//stepScale.x); float offsetX = offset.x + (offsetAmount * 1.0f);
float offsetY = offset.y + (offsetAmount * 1.0f);//stepScale.z); float offsetY = -offset.y + (offsetAmount * 1.0f);//note the -, we flip the tex coords
store.set((((float) x) + offsetX) / (float) totalSize, // calculates percentage of texture here store.set((((float) x) + offsetX) / (float) totalSize, // calculates percentage of texture here
(((float) y) + offsetY) / (float) totalSize); (((float) y) + offsetY) / (float) totalSize);
@ -620,7 +623,44 @@ public class LODGeomap extends GeoMap {
//System.out.println("Index buffer size: "+num); //System.out.println("Index buffer size: "+num);
return num; return num;
} }
public FloatBuffer writeTangentArray(FloatBuffer store, Vector3f scale) {
if (!isLoaded()) {
throw new NullPointerException();
}
if (store != null) {
if (store.remaining() < getWidth() * getHeight() * 3) {
throw new BufferUnderflowException();
}
} else {
store = BufferUtils.createFloatBuffer(getWidth() * getHeight() * 3);
}
store.rewind();
Vector3f tangent = new Vector3f();
Vector3f v1 = new Vector3f();
Vector3f v2 = new Vector3f();
for (int r = 0; r < getHeight(); r++) {
for (int c = 0; c < getWidth(); c++) {
v1.set(c, getValue(c, r), r);
if (c == getWidth()-1) { // last column
v2.set(c+1, getValue(c, r ), r); // use same height
} else {
v2.set(c+1, getValue(c+1, r), r);
}
tangent.set(v2.mult(scale).subtract(v1.mult(scale)));
BufferUtils.setInBuffer(tangent, store, (r * getWidth() + c)); // save the tangent
}
}
return store;
}
@Override @Override
public FloatBuffer writeNormalArray(FloatBuffer store, Vector3f scale) { public FloatBuffer writeNormalArray(FloatBuffer store, Vector3f scale) {
if (!isLoaded()) { if (!isLoaded()) {
@ -653,34 +693,34 @@ public class LODGeomap extends GeoMap {
if (c == 0) { // first column if (c == 0) { // first column
rightPoint.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); bottomPoint.set(c, getValue(c, r + 1), r + 1);
normal.set(getNormal(bottomPoint, rootPoint, rightPoint)); normal.set(getNormal(bottomPoint, rootPoint, rightPoint, scale));
} else if (c == getWidth() - 1) { // last column } else if (c == getWidth() - 1) { // last column
leftPoint.set(c - 1, getValue(c - 1, r), r); leftPoint.set(c - 1, getValue(c - 1, r), r);
bottomPoint.set(c, getValue(c, r + 1), r + 1); bottomPoint.set(c, getValue(c, r + 1), r + 1);
normal.set(getNormal(leftPoint, rootPoint, bottomPoint)); normal.set(getNormal(leftPoint, rootPoint, bottomPoint, scale));
} else { // all middle columns } else { // all middle columns
leftPoint.set(c - 1, getValue(c - 1, r), r); leftPoint.set(c - 1, getValue(c - 1, r), r);
rightPoint.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); bottomPoint.set(c, getValue(c, r + 1), r + 1);
Vector3f n1 = getNormal(leftPoint, rootPoint, bottomPoint); Vector3f n1 = getNormal(leftPoint, rootPoint, bottomPoint, scale);
Vector3f n2 = getNormal(bottomPoint, rootPoint, rightPoint); Vector3f n2 = getNormal(bottomPoint, rootPoint, rightPoint, scale);
normal.set(n1.add(n2).normalizeLocal()); normal.set(n1.add(n2).normalizeLocal());
} }
} else if (r == getHeight() - 1) { // last row } else if (r == getHeight() - 1) { // last row
if (c == 0) { // first column if (c == 0) { // first column
topPoint.set(c, getValue(c, r - 1), r - 1); topPoint.set(c, getValue(c, r - 1), r - 1);
rightPoint.set(c + 1, getValue(c + 1, r), r); rightPoint.set(c + 1, getValue(c + 1, r), r);
normal.set(getNormal(rightPoint, rootPoint, topPoint)); normal.set(getNormal(rightPoint, rootPoint, topPoint, scale));
} else if (c == getWidth() - 1) { // last column } else if (c == getWidth() - 1) { // last column
topPoint.set(c, getValue(c, r - 1), r - 1); topPoint.set(c, getValue(c, r - 1), r - 1);
leftPoint.set(c - 1, getValue(c - 1, r), r); leftPoint.set(c - 1, getValue(c - 1, r), r);
normal.set(getNormal(topPoint, rootPoint, leftPoint)); normal.set(getNormal(topPoint, rootPoint, leftPoint, scale));
} else { // all middle columns } else { // all middle columns
topPoint.set(c, getValue(c, r - 1), r - 1); topPoint.set(c, getValue(c, r - 1), r - 1);
leftPoint.set(c - 1, getValue(c - 1, r), r); leftPoint.set(c - 1, getValue(c - 1, r), r);
rightPoint.set(c + 1, getValue(c + 1, r), r); rightPoint.set(c + 1, getValue(c + 1, r), r);
Vector3f n1 = getNormal(topPoint, rootPoint, leftPoint); Vector3f n1 = getNormal(topPoint, rootPoint, leftPoint, scale);
Vector3f n2 = getNormal(rightPoint, rootPoint, topPoint); Vector3f n2 = getNormal(rightPoint, rootPoint, topPoint, scale);
normal.set(n1.add(n2).normalizeLocal()); normal.set(n1.add(n2).normalizeLocal());
} }
} else { // all middle rows } else { // all middle rows
@ -688,25 +728,25 @@ public class LODGeomap extends GeoMap {
topPoint.set(c, getValue(c, r - 1), r - 1); topPoint.set(c, getValue(c, r - 1), r - 1);
rightPoint.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); bottomPoint.set(c, getValue(c, r + 1), r + 1);
Vector3f n1 = getNormal(rightPoint, rootPoint, topPoint); Vector3f n1 = getNormal(rightPoint, rootPoint, topPoint, scale);
Vector3f n2 = getNormal(bottomPoint, rootPoint, rightPoint); Vector3f n2 = getNormal(bottomPoint, rootPoint, rightPoint, scale);
normal.set(n1.add(n2).normalizeLocal()); normal.set(n1.add(n2).normalizeLocal());
} else if (c == getWidth() - 1) { // last column } else if (c == getWidth() - 1) { // last column
topPoint.set(c, getValue(c, r - 1), r - 1); topPoint.set(c, getValue(c, r - 1), r - 1);
leftPoint.set(c - 1, getValue(c - 1, r), r); leftPoint.set(c - 1, getValue(c - 1, r), r);
bottomPoint.set(c, getValue(c, r + 1), r + 1); bottomPoint.set(c, getValue(c, r + 1), r + 1);
Vector3f n1 = getNormal(topPoint, rootPoint, leftPoint); Vector3f n1 = getNormal(topPoint, rootPoint, leftPoint, scale);
Vector3f n2 = getNormal(leftPoint, rootPoint, bottomPoint); Vector3f n2 = getNormal(leftPoint, rootPoint, bottomPoint, scale);
normal.set(n1.add(n2).normalizeLocal()); normal.set(n1.add(n2).normalizeLocal());
} else { // all middle columns } else { // all middle columns
topPoint.set(c, getValue(c, r - 1), r - 1); topPoint.set(c, getValue(c, r - 1), r - 1);
leftPoint.set(c - 1, getValue(c - 1, r), r); leftPoint.set(c - 1, getValue(c - 1, r), r);
rightPoint.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); bottomPoint.set(c, getValue(c, r + 1), r + 1);
Vector3f n1 = getNormal(topPoint, rootPoint, leftPoint); Vector3f n1 = getNormal(topPoint, rootPoint, leftPoint, scale);
Vector3f n2 = getNormal(leftPoint, rootPoint, bottomPoint); Vector3f n2 = getNormal(leftPoint, rootPoint, bottomPoint, scale);
Vector3f n3 = getNormal(bottomPoint, rootPoint, rightPoint); Vector3f n3 = getNormal(bottomPoint, rootPoint, rightPoint, scale);
Vector3f n4 = getNormal(rightPoint, rootPoint, topPoint); Vector3f n4 = getNormal(rightPoint, rootPoint, topPoint, scale);
normal.set(n1.add(n2).add(n3).add(n4).normalizeLocal()); normal.set(n1.add(n2).add(n3).add(n4).normalizeLocal());
} }
} }
@ -719,9 +759,10 @@ public class LODGeomap extends GeoMap {
return store; return store;
} }
private Vector3f getNormal(Vector3f firstPoint, Vector3f rootPoint, Vector3f secondPoint) { private Vector3f getNormal(Vector3f firstPoint, Vector3f rootPoint, Vector3f secondPoint, Vector3f scale) {
Vector3f normal = new Vector3f(); Vector3f normal = new Vector3f();
normal.set(firstPoint).subtractLocal(rootPoint).crossLocal(secondPoint.subtract(rootPoint)).normalizeLocal(); //scale = Vector3f.UNIT_XYZ;
normal.set(firstPoint.mult(scale)).subtractLocal(rootPoint.mult(scale)).crossLocal(secondPoint.mult(scale).subtract(rootPoint.mult(scale))).normalizeLocal();
return normal; return normal;
} }

@ -331,8 +331,11 @@ public class TerrainPatch extends Geometry {
* recalculate all of this normal vectors in this terrain patch * recalculate all of this normal vectors in this terrain patch
*/ */
protected void updateNormals() { protected void updateNormals() {
FloatBuffer newNormalBuffer = geomap.writeNormalArray(null, stepScale); TangentBinormalGenerator.generate(this);
FloatBuffer newNormalBuffer = geomap.writeNormalArray(null, getWorldScale());
getMesh().getBuffer(Type.Normal).updateData(newNormalBuffer); getMesh().getBuffer(Type.Normal).updateData(newNormalBuffer);
FloatBuffer newTangentBuffer = geomap.writeTangentArray(null, getWorldScale());
getMesh().getBuffer(Type.Tangent).updateData(newTangentBuffer);
} }
/** /**
@ -683,6 +686,7 @@ public class TerrainPatch extends Geometry {
Vector3f normal, Vector3f normal,
Vector3f tangent) Vector3f tangent)
{ {
Vector3f scale = getWorldScale();
v[0] = topPoint; v[0] = topPoint;
v[1] = rootPoint; v[1] = rootPoint;
v[2] = leftPoint; v[2] = leftPoint;
@ -693,7 +697,7 @@ public class TerrainPatch extends Geometry {
Vector3f t1 = Vector3f.ZERO; Vector3f t1 = Vector3f.ZERO;
if (topPoint != null && leftPoint != null) { if (topPoint != null && leftPoint != null) {
TriangleData td1 = TangentBinormalGenerator.processTriangle(indexes, v, t); TriangleData td1 = TangentBinormalGenerator.processTriangle(indexes, v, t);
n1 = getNormal(topPoint, rootPoint, leftPoint); n1 = getNormal(topPoint.mult(scale), rootPoint.mult(scale), leftPoint.mult(scale));
t1 = td1.tangent; t1 = td1.tangent;
} }
v[0] = leftPoint; v[0] = leftPoint;
@ -706,7 +710,7 @@ public class TerrainPatch extends Geometry {
Vector3f t2 = Vector3f.ZERO; Vector3f t2 = Vector3f.ZERO;
if (leftPoint != null && bottomPoint != null) { if (leftPoint != null && bottomPoint != null) {
TriangleData td2 = TangentBinormalGenerator.processTriangle(indexes, v, t); TriangleData td2 = TangentBinormalGenerator.processTriangle(indexes, v, t);
n2 = getNormal(leftPoint, rootPoint, bottomPoint); n2 = getNormal(leftPoint.mult(scale), rootPoint.mult(scale), bottomPoint.mult(scale));
t2 = td2.tangent; t2 = td2.tangent;
} }
v[0] = bottomPoint; v[0] = bottomPoint;
@ -719,7 +723,7 @@ public class TerrainPatch extends Geometry {
Vector3f t3 = Vector3f.ZERO; Vector3f t3 = Vector3f.ZERO;
if (rightPoint != null && bottomPoint != null) { if (rightPoint != null && bottomPoint != null) {
TriangleData td3 = TangentBinormalGenerator.processTriangle(indexes, v, t); TriangleData td3 = TangentBinormalGenerator.processTriangle(indexes, v, t);
n3 = getNormal(bottomPoint, rootPoint, rightPoint); n3 = getNormal(bottomPoint.mult(scale), rootPoint.mult(scale), rightPoint.mult(scale));
t3 = td3.tangent; t3 = td3.tangent;
} }
v[0] = rightPoint; v[0] = rightPoint;
@ -732,7 +736,7 @@ public class TerrainPatch extends Geometry {
Vector3f t4 = Vector3f.ZERO; Vector3f t4 = Vector3f.ZERO;
if (rightPoint != null && topPoint != null) { if (rightPoint != null && topPoint != null) {
TriangleData td4 = TangentBinormalGenerator.processTriangle(indexes, v, t); TriangleData td4 = TangentBinormalGenerator.processTriangle(indexes, v, t);
n4 = getNormal(rightPoint, rootPoint, topPoint); n4 = getNormal(rightPoint.mult(scale), rootPoint.mult(scale), topPoint.mult(scale));
t4 = td4.tangent; t4 = td4.tangent;
} }

@ -67,6 +67,8 @@ import com.jme3.util.TangentBinormalGenerator;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
/** /**
* A terrain quad is a node in the quad tree of the terrain system. * A terrain quad is a node in the quad tree of the terrain system.
@ -103,6 +105,7 @@ public class TerrainQuad extends Node implements Terrain {
private BoundingBox affectedAreaBBox; // only set in the root quad private BoundingBox affectedAreaBBox; // only set in the root quad
private TerrainPicker picker; private TerrainPicker picker;
private Vector3f lastScale = Vector3f.UNIT_XYZ;
protected ExecutorService executor; protected ExecutorService executor;
@ -152,6 +155,9 @@ public class TerrainQuad extends Node implements Terrain {
if (!FastMath.isPowerOfTwo(size - 1)) { if (!FastMath.isPowerOfTwo(size - 1)) {
throw new RuntimeException("size given: " + size + " Terrain quad sizes may only be (2^N + 1)"); throw new RuntimeException("size given: " + size + " Terrain quad sizes may only be (2^N + 1)");
} }
if (FastMath.sqrt(heightMap.length) > size) {
Logger.getLogger(this.getClass().getName()).log(Level.WARNING, "Heightmap size is larger than the terrain size. Make sure your heightmap image is the same size as the terrain!");
}
if (heightMap == null) if (heightMap == null)
heightMap = generateDefaultHeightMap(size); heightMap = generateDefaultHeightMap(size);
@ -728,7 +734,7 @@ public class TerrainQuad extends Node implements Terrain {
patch1.setModelBound(new BoundingBox()); patch1.setModelBound(new BoundingBox());
patch1.updateModelBound(); patch1.updateModelBound();
patch1.setLodCalculator(lodCalculatorFactory); patch1.setLodCalculator(lodCalculatorFactory);
TangentBinormalGenerator.generate(patch1); //TangentBinormalGenerator.generate(patch1);
// 2 lower left // 2 lower left
float[] heightBlock2 = createHeightSubBlock(heightMap, 0, split - 1, float[] heightBlock2 = createHeightSubBlock(heightMap, 0, split - 1,
@ -750,7 +756,7 @@ public class TerrainQuad extends Node implements Terrain {
patch2.setModelBound(new BoundingBox()); patch2.setModelBound(new BoundingBox());
patch2.updateModelBound(); patch2.updateModelBound();
patch2.setLodCalculator(lodCalculatorFactory); patch2.setLodCalculator(lodCalculatorFactory);
TangentBinormalGenerator.generate(patch2); //TangentBinormalGenerator.generate(patch2);
// 3 upper right // 3 upper right
float[] heightBlock3 = createHeightSubBlock(heightMap, split - 1, 0, float[] heightBlock3 = createHeightSubBlock(heightMap, split - 1, 0,
@ -772,7 +778,7 @@ public class TerrainQuad extends Node implements Terrain {
patch3.setModelBound(new BoundingBox()); patch3.setModelBound(new BoundingBox());
patch3.updateModelBound(); patch3.updateModelBound();
patch3.setLodCalculator(lodCalculatorFactory); patch3.setLodCalculator(lodCalculatorFactory);
TangentBinormalGenerator.generate(patch3); //TangentBinormalGenerator.generate(patch3);
// 4 lower right // 4 lower right
float[] heightBlock4 = createHeightSubBlock(heightMap, split - 1, float[] heightBlock4 = createHeightSubBlock(heightMap, split - 1,
@ -794,7 +800,7 @@ public class TerrainQuad extends Node implements Terrain {
patch4.setModelBound(new BoundingBox()); patch4.setModelBound(new BoundingBox());
patch4.updateModelBound(); patch4.updateModelBound();
patch4.setLodCalculator(lodCalculatorFactory); patch4.setLodCalculator(lodCalculatorFactory);
TangentBinormalGenerator.generate(patch4); //TangentBinormalGenerator.generate(patch4);
} }
public float[] createHeightSubBlock(float[] heightMap, int x, public float[] createHeightSubBlock(float[] heightMap, int x,
@ -870,7 +876,13 @@ public class TerrainQuad extends Node implements Terrain {
} }
protected boolean needToRecalculateNormals() { protected boolean needToRecalculateNormals() {
return affectedAreaBBox != null; if (affectedAreaBBox != null)
return true;
if (!lastScale.equals(getWorldScale())) {
lastScale = getWorldScale();
return true;
}
return false;
} }
public float getHeightmapHeight(Vector2f xz) { public float getHeightmapHeight(Vector2f xz) {

Loading…
Cancel
Save