diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/LODGeomap.java b/engine/src/terrain/com/jme3/terrain/geomipmap/LODGeomap.java index 80d52b3aa..f8e2d3f46 100644 --- a/engine/src/terrain/com/jme3/terrain/geomipmap/LODGeomap.java +++ b/engine/src/terrain/com/jme3/terrain/geomipmap/LODGeomap.java @@ -47,6 +47,7 @@ import com.jme3.scene.Mesh; import com.jme3.scene.Mesh.Mode; import com.jme3.scene.VertexBuffer.Type; import com.jme3.util.BufferUtils; +import com.jme3.util.TangentBinormalGenerator; import java.io.IOException; /** @@ -80,10 +81,12 @@ public class LODGeomap extends GeoMap { FloatBuffer tb = writeTexCoordArray(null, tcOffset, tcScale, offsetAmount, totalSize); FloatBuffer nb = writeNormalArray(null, scale); IntBuffer ib = writeIndexArrayLodDiff(null, lod, rightLod, topLod, leftLod, bottomLod); + FloatBuffer tanb = writeTangentArray(null, scale); Mesh m = new Mesh(); m.setMode(Mode.TriangleStrip); m.setBuffer(Type.Position, 3, pb); m.setBuffer(Type.Normal, 3, nb); + m.setBuffer(Type.Tangent, 3, tanb); m.setBuffer(Type.TexCoord, 2, tb); m.setBuffer(Type.Index, 3, ib); m.setStatic(); @@ -110,8 +113,8 @@ public class LODGeomap extends GeoMap { 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++) { getUV(x, y, tcStore, offset, offsetAmount, totalSize); 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) { - float offsetX = offset.x + (offsetAmount * 1.0f);//stepScale.x); - float offsetY = offset.y + (offsetAmount * 1.0f);//stepScale.z); + float offsetX = offset.x + (offsetAmount * 1.0f); + 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 (((float) y) + offsetY) / (float) totalSize); @@ -620,7 +623,44 @@ public class LODGeomap extends GeoMap { //System.out.println("Index buffer size: "+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 public FloatBuffer writeNormalArray(FloatBuffer store, Vector3f scale) { if (!isLoaded()) { @@ -653,34 +693,34 @@ public class LODGeomap extends GeoMap { 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)); + normal.set(getNormal(bottomPoint, rootPoint, rightPoint, scale)); } 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)); + normal.set(getNormal(leftPoint, rootPoint, bottomPoint, scale)); } 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); - Vector3f n2 = getNormal(bottomPoint, rootPoint, rightPoint); + Vector3f n1 = getNormal(leftPoint, rootPoint, bottomPoint, scale); + Vector3f n2 = getNormal(bottomPoint, rootPoint, rightPoint, scale); normal.set(n1.add(n2).normalizeLocal()); } } 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)); + normal.set(getNormal(rightPoint, rootPoint, topPoint, scale)); } 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)); + normal.set(getNormal(topPoint, rootPoint, leftPoint, scale)); } 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); - Vector3f n2 = getNormal(rightPoint, rootPoint, topPoint); + Vector3f n1 = getNormal(topPoint, rootPoint, leftPoint, scale); + Vector3f n2 = getNormal(rightPoint, rootPoint, topPoint, scale); normal.set(n1.add(n2).normalizeLocal()); } } else { // all middle rows @@ -688,25 +728,25 @@ public class LODGeomap extends GeoMap { 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); - Vector3f n2 = getNormal(bottomPoint, rootPoint, rightPoint); + Vector3f n1 = getNormal(rightPoint, rootPoint, topPoint, scale); + Vector3f n2 = getNormal(bottomPoint, rootPoint, rightPoint, scale); normal.set(n1.add(n2).normalizeLocal()); } 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); - Vector3f n2 = getNormal(leftPoint, rootPoint, bottomPoint); + Vector3f n1 = getNormal(topPoint, rootPoint, leftPoint, scale); + Vector3f n2 = getNormal(leftPoint, rootPoint, bottomPoint, scale); normal.set(n1.add(n2).normalizeLocal()); } 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); - Vector3f n2 = getNormal(leftPoint, rootPoint, bottomPoint); - Vector3f n3 = getNormal(bottomPoint, rootPoint, rightPoint); - Vector3f n4 = getNormal(rightPoint, rootPoint, topPoint); + 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()); } } @@ -719,9 +759,10 @@ public class LODGeomap extends GeoMap { 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(); - 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; } diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainPatch.java b/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainPatch.java index db34bcf10..5aaf466d6 100644 --- a/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainPatch.java +++ b/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainPatch.java @@ -331,8 +331,11 @@ public class TerrainPatch extends Geometry { * recalculate all of this normal vectors in this terrain patch */ protected void updateNormals() { - FloatBuffer newNormalBuffer = geomap.writeNormalArray(null, stepScale); + TangentBinormalGenerator.generate(this); + FloatBuffer newNormalBuffer = geomap.writeNormalArray(null, getWorldScale()); 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 tangent) { + Vector3f scale = getWorldScale(); v[0] = topPoint; v[1] = rootPoint; v[2] = leftPoint; @@ -693,7 +697,7 @@ public class TerrainPatch extends Geometry { Vector3f t1 = Vector3f.ZERO; if (topPoint != null && leftPoint != null) { 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; } v[0] = leftPoint; @@ -706,7 +710,7 @@ public class TerrainPatch extends Geometry { Vector3f t2 = Vector3f.ZERO; if (leftPoint != null && bottomPoint != null) { 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; } v[0] = bottomPoint; @@ -719,7 +723,7 @@ public class TerrainPatch extends Geometry { Vector3f t3 = Vector3f.ZERO; if (rightPoint != null && bottomPoint != null) { 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; } v[0] = rightPoint; @@ -732,7 +736,7 @@ public class TerrainPatch extends Geometry { Vector3f t4 = Vector3f.ZERO; if (rightPoint != null && topPoint != null) { 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; } diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainQuad.java b/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainQuad.java index 7bee962d2..699d580c4 100644 --- a/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainQuad.java +++ b/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainQuad.java @@ -67,6 +67,8 @@ import com.jme3.util.TangentBinormalGenerator; import java.util.ArrayList; import java.util.List; 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. @@ -103,6 +105,7 @@ public class TerrainQuad extends Node implements Terrain { private BoundingBox affectedAreaBBox; // only set in the root quad private TerrainPicker picker; + private Vector3f lastScale = Vector3f.UNIT_XYZ; protected ExecutorService executor; @@ -152,6 +155,9 @@ public class TerrainQuad extends Node implements Terrain { if (!FastMath.isPowerOfTwo(size - 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) heightMap = generateDefaultHeightMap(size); @@ -728,7 +734,7 @@ public class TerrainQuad extends Node implements Terrain { patch1.setModelBound(new BoundingBox()); patch1.updateModelBound(); patch1.setLodCalculator(lodCalculatorFactory); - TangentBinormalGenerator.generate(patch1); + //TangentBinormalGenerator.generate(patch1); // 2 lower left float[] heightBlock2 = createHeightSubBlock(heightMap, 0, split - 1, @@ -750,7 +756,7 @@ public class TerrainQuad extends Node implements Terrain { patch2.setModelBound(new BoundingBox()); patch2.updateModelBound(); patch2.setLodCalculator(lodCalculatorFactory); - TangentBinormalGenerator.generate(patch2); + //TangentBinormalGenerator.generate(patch2); // 3 upper right float[] heightBlock3 = createHeightSubBlock(heightMap, split - 1, 0, @@ -772,7 +778,7 @@ public class TerrainQuad extends Node implements Terrain { patch3.setModelBound(new BoundingBox()); patch3.updateModelBound(); patch3.setLodCalculator(lodCalculatorFactory); - TangentBinormalGenerator.generate(patch3); + //TangentBinormalGenerator.generate(patch3); // 4 lower right float[] heightBlock4 = createHeightSubBlock(heightMap, split - 1, @@ -794,7 +800,7 @@ public class TerrainQuad extends Node implements Terrain { patch4.setModelBound(new BoundingBox()); patch4.updateModelBound(); patch4.setLodCalculator(lodCalculatorFactory); - TangentBinormalGenerator.generate(patch4); + //TangentBinormalGenerator.generate(patch4); } public float[] createHeightSubBlock(float[] heightMap, int x, @@ -870,7 +876,13 @@ public class TerrainQuad extends Node implements Terrain { } 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) {