JmeCloneable related changes to TerrainQuad and TerrainPatch. Fixed

something I missed in NormalRecalcControl.
cleanup_build_scripts
Paul Speed 9 years ago
parent eda92656dd
commit 7b29c58fe0
  1. 19
      jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/NormalRecalcControl.java
  2. 142
      jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/TerrainPatch.java
  3. 101
      jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/TerrainQuad.java

@ -69,12 +69,23 @@ public class NormalRecalcControl extends AbstractControl {
} }
@Override /**
* Called internally by com.jme3.util.clone.Cloner. Do not call directly.
*/
@Override
public Object jmeClone() { public Object jmeClone() {
NormalRecalcControl control = (NormalRecalcControl)super.jmeClone(); NormalRecalcControl control = (NormalRecalcControl)super.jmeClone();
control.setEnabled(true); control.setEnabled(true);
return control; return control;
} }
/**
* Called internally by com.jme3.util.clone.Cloner. Do not call directly.
*/
@Override
public void cloneFields( Cloner cloner, Object original ) {
this.terrain = cloner.clone(terrain);
}
@Override @Override
public Control cloneForSpatial(Spatial spatial) { public Control cloneForSpatial(Spatial spatial) {
@ -83,7 +94,7 @@ public class NormalRecalcControl extends AbstractControl {
control.setEnabled(true); control.setEnabled(true);
return control; return control;
} }
@Override @Override
public void setSpatial(Spatial spatial) { public void setSpatial(Spatial spatial) {
super.setSpatial(spatial); super.setSpatial(spatial);

@ -50,6 +50,7 @@ import com.jme3.scene.mesh.IndexBuffer;
import com.jme3.terrain.geomipmap.TerrainQuad.LocationHeight; import com.jme3.terrain.geomipmap.TerrainQuad.LocationHeight;
import com.jme3.terrain.geomipmap.lodcalc.util.EntropyComputeUtil; import com.jme3.terrain.geomipmap.lodcalc.util.EntropyComputeUtil;
import com.jme3.util.BufferUtils; import com.jme3.util.BufferUtils;
import com.jme3.util.clone.Cloner;
import java.io.IOException; import java.io.IOException;
import java.nio.Buffer; import java.nio.Buffer;
import java.nio.FloatBuffer; import java.nio.FloatBuffer;
@ -65,18 +66,18 @@ import java.util.List;
* That uses a geo-mipmapping algorithm to change the index buffer of the mesh. * That uses a geo-mipmapping algorithm to change the index buffer of the mesh.
* The mesh is a triangle strip. In wireframe mode you might notice some strange lines, these are degenerate * The mesh is a triangle strip. In wireframe mode you might notice some strange lines, these are degenerate
* triangles generated by the geoMipMap algorithm and can be ignored. The video card removes them at almost no cost. * triangles generated by the geoMipMap algorithm and can be ignored. The video card removes them at almost no cost.
* *
* Each patch needs to know its neighbour's LOD so it can seam its edges with them, in case the neighbour has a different * Each patch needs to know its neighbour's LOD so it can seam its edges with them, in case the neighbour has a different
* LOD. If this doesn't happen, you will see gaps. * LOD. If this doesn't happen, you will see gaps.
* *
* The LOD value is most detailed at zero. It gets less detailed the higher the LOD value until you reach maxLod, which * The LOD value is most detailed at zero. It gets less detailed the higher the LOD value until you reach maxLod, which
* is a mathematical limit on the number of times the 'size' of the patch can be divided by two. However there is a -1 to that * is a mathematical limit on the number of times the 'size' of the patch can be divided by two. However there is a -1 to that
* for now until I add in a custom index buffer calculation for that max level, the current algorithm does not go that far. * for now until I add in a custom index buffer calculation for that max level, the current algorithm does not go that far.
* *
* You can supply a LodThresholdCalculator for use in determining when the LOD should change. It's API will no doubt change * You can supply a LodThresholdCalculator for use in determining when the LOD should change. It's API will no doubt change
* in the near future. Right now it defaults to just changing LOD every two patch sizes. So if a patch has a size of 65, * in the near future. Right now it defaults to just changing LOD every two patch sizes. So if a patch has a size of 65,
* then the LOD changes every 130 units away. * then the LOD changes every 130 units away.
* *
* @author Brent Owens * @author Brent Owens
*/ */
public class TerrainPatch extends Geometry { public class TerrainPatch extends Geometry {
@ -118,7 +119,7 @@ public class TerrainPatch extends Geometry {
super("TerrainPatch"); super("TerrainPatch");
setBatchHint(BatchHint.Never); setBatchHint(BatchHint.Never);
} }
public TerrainPatch(String name) { public TerrainPatch(String name) {
super(name); super(name);
setBatchHint(BatchHint.Never); setBatchHint(BatchHint.Never);
@ -221,7 +222,7 @@ public class TerrainPatch extends Geometry {
public FloatBuffer getHeightmap() { public FloatBuffer getHeightmap() {
return BufferUtils.createFloatBuffer(geomap.getHeightArray()); return BufferUtils.createFloatBuffer(geomap.getHeightArray());
} }
public float[] getHeightMap() { public float[] getHeightMap() {
return geomap.getHeightArray(); return geomap.getHeightArray();
} }
@ -256,7 +257,7 @@ public class TerrainPatch extends Geometry {
idxB = geomap.writeIndexArrayLodVariable(pow, (int) Math.pow(2, utp.getRightLod()), (int) Math.pow(2, utp.getTopLod()), (int) Math.pow(2, utp.getLeftLod()), (int) Math.pow(2, utp.getBottomLod()), totalSize); idxB = geomap.writeIndexArrayLodVariable(pow, (int) Math.pow(2, utp.getRightLod()), (int) Math.pow(2, utp.getTopLod()), (int) Math.pow(2, utp.getLeftLod()), (int) Math.pow(2, utp.getBottomLod()), totalSize);
else else
idxB = geomap.writeIndexArrayLodDiff(pow, right, top, left, bottom, totalSize); idxB = geomap.writeIndexArrayLodDiff(pow, right, top, left, bottom, totalSize);
Buffer b; Buffer b;
if (idxB.getBuffer() instanceof IntBuffer) if (idxB.getBuffer() instanceof IntBuffer)
b = (IntBuffer)idxB.getBuffer(); b = (IntBuffer)idxB.getBuffer();
@ -277,14 +278,14 @@ public class TerrainPatch extends Geometry {
return store.set(getMesh().getFloatBuffer(Type.TexCoord).get(idx*2), return store.set(getMesh().getFloatBuffer(Type.TexCoord).get(idx*2),
getMesh().getFloatBuffer(Type.TexCoord).get(idx*2+1) ); getMesh().getFloatBuffer(Type.TexCoord).get(idx*2+1) );
} }
public float getHeightmapHeight(float x, float z) { public float getHeightmapHeight(float x, float z) {
if (x < 0 || z < 0 || x >= size || z >= size) if (x < 0 || z < 0 || x >= size || z >= size)
return 0; return 0;
int idx = (int) (z * size + x); int idx = (int) (z * size + x);
return getMesh().getFloatBuffer(Type.Position).get(idx*3+1); // 3 floats per entry (x,y,z), the +1 is to get the Y return getMesh().getFloatBuffer(Type.Position).get(idx*3+1); // 3 floats per entry (x,y,z), the +1 is to get the Y
} }
/** /**
* Get the triangle of this geometry at the specified local coordinate. * Get the triangle of this geometry at the specified local coordinate.
* @param x local to the terrain patch * @param x local to the terrain patch
@ -306,7 +307,7 @@ public class TerrainPatch extends Geometry {
} }
protected void setHeight(List<LocationHeight> locationHeights, boolean overrideHeight) { protected void setHeight(List<LocationHeight> locationHeights, boolean overrideHeight) {
for (LocationHeight lh : locationHeights) { for (LocationHeight lh : locationHeights) {
if (lh.x < 0 || lh.z < 0 || lh.x >= size || lh.z >= size) if (lh.x < 0 || lh.z < 0 || lh.x >= size || lh.z >= size)
continue; continue;
@ -317,7 +318,7 @@ public class TerrainPatch extends Geometry {
float h = getMesh().getFloatBuffer(Type.Position).get(idx*3+1); float h = getMesh().getFloatBuffer(Type.Position).get(idx*3+1);
geomap.getHeightArray()[idx] = h+lh.h; geomap.getHeightArray()[idx] = h+lh.h;
} }
} }
FloatBuffer newVertexBuffer = geomap.writeVertexArray(null, stepScale, false); FloatBuffer newVertexBuffer = geomap.writeVertexArray(null, stepScale, false);
@ -351,7 +352,7 @@ public class TerrainPatch extends Geometry {
TB.setUpdateNeeded(); TB.setUpdateNeeded();
BB.setUpdateNeeded(); BB.setUpdateNeeded();
} }
/** /**
* Matches the normals along the edge of the patch with the neighbours. * Matches the normals along the edge of the patch with the neighbours.
* Computes the normals for the right, bottom, left, and top edges of the * Computes the normals for the right, bottom, left, and top edges of the
@ -364,7 +365,7 @@ public class TerrainPatch extends Geometry {
* *---x---* * *---x---*
* | * |
* * * *
* It works across the right side of the patch, from the top down to * It works across the right side of the patch, from the top down to
* the bottom. Then it works on the bottom side of the patch, from the * the bottom. Then it works on the bottom side of the patch, from the
* left to the right. * left to the right.
*/ */
@ -388,9 +389,9 @@ public class TerrainPatch extends Geometry {
Vector3f binormal = new Vector3f(); Vector3f binormal = new Vector3f();
Vector3f normal = new Vector3f(); Vector3f normal = new Vector3f();
int s = this.getSize()-1; int s = this.getSize()-1;
if (right != null) { // right side, works its way down if (right != null) { // right side, works its way down
for (int i=0; i<s+1; i++) { for (int i=0; i<s+1; i++) {
rootPoint.set(0, this.getHeightmapHeight(s,i), 0); rootPoint.set(0, this.getHeightmapHeight(s,i), 0);
@ -399,26 +400,26 @@ public class TerrainPatch extends Geometry {
if (i == 0) { // top point if (i == 0) { // top point
bottomPoint.set(0, this.getHeightmapHeight(s,i+1), 1); bottomPoint.set(0, this.getHeightmapHeight(s,i+1), 1);
if (top == null) { if (top == null) {
averageNormalsTangents(null, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal); averageNormalsTangents(null, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal);
setInBuffer(this.getMesh(), s, normal, tangent, binormal); setInBuffer(this.getMesh(), s, normal, tangent, binormal);
setInBuffer(right.getMesh(), 0, normal, tangent, binormal); setInBuffer(right.getMesh(), 0, normal, tangent, binormal);
} else { } else {
topPoint.set(0, top.getHeightmapHeight(s,s-1), -1); topPoint.set(0, top.getHeightmapHeight(s,s-1), -1);
averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint,normal, tangent, binormal); averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint,normal, tangent, binormal);
setInBuffer(this.getMesh(), s, normal, tangent, binormal); setInBuffer(this.getMesh(), s, normal, tangent, binormal);
setInBuffer(right.getMesh(), 0, normal, tangent, binormal); setInBuffer(right.getMesh(), 0, normal, tangent, binormal);
setInBuffer(top.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal); setInBuffer(top.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal);
if (topRight != null) { if (topRight != null) {
// setInBuffer(topRight.getMesh(), (s+1)*s, normal, tangent, binormal); // setInBuffer(topRight.getMesh(), (s+1)*s, normal, tangent, binormal);
} }
} }
} else if (i == s) { // bottom point } else if (i == s) { // bottom point
topPoint.set(0, this.getHeightmapHeight(s,s-1), -1); topPoint.set(0, this.getHeightmapHeight(s,s-1), -1);
if (bottom == null) { if (bottom == null) {
averageNormalsTangents(topPoint, rootPoint, leftPoint, null, rightPoint, normal, tangent, binormal); averageNormalsTangents(topPoint, rootPoint, leftPoint, null, rightPoint, normal, tangent, binormal);
setInBuffer(this.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal); setInBuffer(this.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal);
@ -429,7 +430,7 @@ public class TerrainPatch extends Geometry {
setInBuffer(this.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal); setInBuffer(this.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal);
setInBuffer(right.getMesh(), (s+1)*s, normal, tangent, binormal); setInBuffer(right.getMesh(), (s+1)*s, normal, tangent, binormal);
setInBuffer(bottom.getMesh(), s, normal, tangent, binormal); setInBuffer(bottom.getMesh(), s, normal, tangent, binormal);
if (bottomRight != null) { if (bottomRight != null) {
// setInBuffer(bottomRight.getMesh(), 0, normal, tangent, binormal); // setInBuffer(bottomRight.getMesh(), 0, normal, tangent, binormal);
} }
@ -449,41 +450,41 @@ public class TerrainPatch extends Geometry {
rootPoint.set(0, this.getHeightmapHeight(0,i), 0); rootPoint.set(0, this.getHeightmapHeight(0,i), 0);
leftPoint.set(-1, left.getHeightmapHeight(s-1,i), 0); leftPoint.set(-1, left.getHeightmapHeight(s-1,i), 0);
rightPoint.set(1, this.getHeightmapHeight(1,i), 0); rightPoint.set(1, this.getHeightmapHeight(1,i), 0);
if (i == 0) { // top point if (i == 0) { // top point
bottomPoint.set(0, this.getHeightmapHeight(0,i+1), 1); bottomPoint.set(0, this.getHeightmapHeight(0,i+1), 1);
if (top == null) { if (top == null) {
averageNormalsTangents(null, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal); averageNormalsTangents(null, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal);
setInBuffer(this.getMesh(), 0, normal, tangent, binormal); setInBuffer(this.getMesh(), 0, normal, tangent, binormal);
setInBuffer(left.getMesh(), s, normal, tangent, binormal); setInBuffer(left.getMesh(), s, normal, tangent, binormal);
} else { } else {
topPoint.set(0, top.getHeightmapHeight(0,s-1), -1); topPoint.set(0, top.getHeightmapHeight(0,s-1), -1);
averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal); averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal);
setInBuffer(this.getMesh(), 0, normal, tangent, binormal); setInBuffer(this.getMesh(), 0, normal, tangent, binormal);
setInBuffer(left.getMesh(), s, normal, tangent, binormal); setInBuffer(left.getMesh(), s, normal, tangent, binormal);
setInBuffer(top.getMesh(), (s+1)*s, normal, tangent, binormal); setInBuffer(top.getMesh(), (s+1)*s, normal, tangent, binormal);
if (topLeft != null) { if (topLeft != null) {
// setInBuffer(topLeft.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal); // setInBuffer(topLeft.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal);
} }
} }
} else if (i == s) { // bottom point } else if (i == s) { // bottom point
topPoint.set(0, this.getHeightmapHeight(0,i-1), -1); topPoint.set(0, this.getHeightmapHeight(0,i-1), -1);
if (bottom == null) { if (bottom == null) {
averageNormalsTangents(topPoint, rootPoint, leftPoint, null, rightPoint, normal, tangent, binormal); averageNormalsTangents(topPoint, rootPoint, leftPoint, null, rightPoint, normal, tangent, binormal);
setInBuffer(this.getMesh(), (s+1)*(s), normal, tangent, binormal); setInBuffer(this.getMesh(), (s+1)*(s), normal, tangent, binormal);
setInBuffer(left.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal); setInBuffer(left.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal);
} else { } else {
bottomPoint.set(0, bottom.getHeightmapHeight(0,1), 1); bottomPoint.set(0, bottom.getHeightmapHeight(0,1), 1);
averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal); averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal);
setInBuffer(this.getMesh(), (s+1)*(s), normal, tangent, binormal); setInBuffer(this.getMesh(), (s+1)*(s), normal, tangent, binormal);
setInBuffer(left.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal); setInBuffer(left.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal);
setInBuffer(bottom.getMesh(), 0, normal, tangent, binormal); setInBuffer(bottom.getMesh(), 0, normal, tangent, binormal);
if (bottomLeft != null) { if (bottomLeft != null) {
// setInBuffer(bottomLeft.getMesh(), s, normal, tangent, binormal); // setInBuffer(bottomLeft.getMesh(), s, normal, tangent, binormal);
} }
@ -491,7 +492,7 @@ public class TerrainPatch extends Geometry {
} else { // all in the middle } else { // all in the middle
topPoint.set(0, this.getHeightmapHeight(0,i-1), -1); topPoint.set(0, this.getHeightmapHeight(0,i-1), -1);
bottomPoint.set(0, this.getHeightmapHeight(0,i+1), 1); bottomPoint.set(0, this.getHeightmapHeight(0,i+1), 1);
averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal); averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal);
setInBuffer(this.getMesh(), (s+1)*(i), normal, tangent, binormal); setInBuffer(this.getMesh(), (s+1)*(i), normal, tangent, binormal);
setInBuffer(left.getMesh(), (s+1)*(i+1)-1, normal, tangent, binormal); setInBuffer(left.getMesh(), (s+1)*(i+1)-1, normal, tangent, binormal);
@ -504,14 +505,14 @@ public class TerrainPatch extends Geometry {
rootPoint.set(0, this.getHeightmapHeight(i,0), 0); rootPoint.set(0, this.getHeightmapHeight(i,0), 0);
topPoint.set(0, top.getHeightmapHeight(i,s-1), -1); topPoint.set(0, top.getHeightmapHeight(i,s-1), -1);
bottomPoint.set(0, this.getHeightmapHeight(i,1), 1); bottomPoint.set(0, this.getHeightmapHeight(i,1), 1);
if (i == 0) { // left corner if (i == 0) { // left corner
// handled by left side pass // handled by left side pass
} else if (i == s) { // right corner } else if (i == s) { // right corner
// handled by this patch when it does its right side // handled by this patch when it does its right side
} else { // all in the middle } else { // all in the middle
leftPoint.set(-1, this.getHeightmapHeight(i-1,0), 0); leftPoint.set(-1, this.getHeightmapHeight(i-1,0), 0);
rightPoint.set(1, this.getHeightmapHeight(i+1,0), 0); rightPoint.set(1, this.getHeightmapHeight(i+1,0), 0);
@ -520,9 +521,9 @@ public class TerrainPatch extends Geometry {
setInBuffer(top.getMesh(), (s+1)*(s)+i, normal, tangent, binormal); setInBuffer(top.getMesh(), (s+1)*(s)+i, normal, tangent, binormal);
} }
} }
} }
if (bottom != null) { // bottom side, works its way right if (bottom != null) { // bottom side, works its way right
for (int i=0; i<s+1; i++) { for (int i=0; i<s+1; i++) {
rootPoint.set(0, this.getHeightmapHeight(i,s), 0); rootPoint.set(0, this.getHeightmapHeight(i,s), 0);
@ -531,11 +532,11 @@ public class TerrainPatch extends Geometry {
if (i == 0) { // left if (i == 0) { // left
// handled by the left side pass // handled by the left side pass
} else if (i == s) { // right } else if (i == s) { // right
// handled by the right side pass // handled by the right side pass
} else { // all in the middle } else { // all in the middle
leftPoint.set(-1, this.getHeightmapHeight(i-1,s), 0); leftPoint.set(-1, this.getHeightmapHeight(i-1,s), 0);
rightPoint.set(1, this.getHeightmapHeight(i+1,s), 0); rightPoint.set(1, this.getHeightmapHeight(i+1,s), 0);
@ -544,22 +545,22 @@ public class TerrainPatch extends Geometry {
setInBuffer(bottom.getMesh(), i, normal, tangent, binormal); setInBuffer(bottom.getMesh(), i, normal, tangent, binormal);
} }
} }
} }
} }
protected void averageNormalsTangents( protected void averageNormalsTangents(
Vector3f topPoint, Vector3f topPoint,
Vector3f rootPoint, Vector3f rootPoint,
Vector3f leftPoint, Vector3f leftPoint,
Vector3f bottomPoint, Vector3f bottomPoint,
Vector3f rightPoint, Vector3f rightPoint,
Vector3f normal, Vector3f normal,
Vector3f tangent, Vector3f tangent,
Vector3f binormal) Vector3f binormal)
{ {
Vector3f scale = getWorldScale(); Vector3f scale = getWorldScale();
Vector3f n1 = new Vector3f(0,0,0); Vector3f n1 = new Vector3f(0,0,0);
if (topPoint != null && leftPoint != null) { if (topPoint != null && leftPoint != null) {
n1.set(calculateNormal(topPoint.mult(scale), rootPoint.mult(scale), leftPoint.mult(scale))); n1.set(calculateNormal(topPoint.mult(scale), rootPoint.mult(scale), leftPoint.mult(scale)));
@ -576,12 +577,12 @@ public class TerrainPatch extends Geometry {
if (rightPoint != null && topPoint != null) { if (rightPoint != null && topPoint != null) {
n4.set(calculateNormal(rightPoint.mult(scale), rootPoint.mult(scale), topPoint.mult(scale))); n4.set(calculateNormal(rightPoint.mult(scale), rootPoint.mult(scale), topPoint.mult(scale)));
} }
//if (bottomPoint != null && rightPoint != null && rootTex != null && rightTex != null && bottomTex != null) //if (bottomPoint != null && rightPoint != null && rootTex != null && rightTex != null && bottomTex != null)
// LODGeomap.calculateTangent(new Vector3f[]{rootPoint.mult(scale),rightPoint.mult(scale),bottomPoint.mult(scale)}, new Vector2f[]{rootTex,rightTex,bottomTex}, tangent, binormal); // LODGeomap.calculateTangent(new Vector3f[]{rootPoint.mult(scale),rightPoint.mult(scale),bottomPoint.mult(scale)}, new Vector2f[]{rootTex,rightTex,bottomTex}, tangent, binormal);
normal.set(n1.add(n2).add(n3).add(n4).normalize()); normal.set(n1.add(n2).add(n3).add(n4).normalize());
tangent.set(normal.cross(new Vector3f(0,0,1)).normalize()); tangent.set(normal.cross(new Vector3f(0,0,1)).normalize());
binormal.set(new Vector3f(1,0,0).cross(normal).normalize()); binormal.set(new Vector3f(1,0,0).cross(normal).normalize());
} }
@ -592,11 +593,11 @@ public class TerrainPatch extends Geometry {
.crossLocal(secondPoint.subtract(rootPoint)).normalizeLocal(); .crossLocal(secondPoint.subtract(rootPoint)).normalizeLocal();
return normal; return normal;
} }
protected Vector3f getMeshNormal(int x, int z) { protected Vector3f getMeshNormal(int x, int z) {
if (x >= size || z >= size) if (x >= size || z >= size)
return null; // out of range return null; // out of range
int index = (z*size+x)*3; int index = (z*size+x)*3;
FloatBuffer nb = (FloatBuffer)this.getMesh().getBuffer(Type.Normal).getData(); FloatBuffer nb = (FloatBuffer)this.getMesh().getBuffer(Type.Normal).getData();
Vector3f normal = new Vector3f(); Vector3f normal = new Vector3f();
@ -609,7 +610,7 @@ public class TerrainPatch extends Geometry {
protected float getHeight(int x, int z, float xm, float zm) { protected float getHeight(int x, int z, float xm, float zm) {
return geomap.getHeight(x,z,xm,zm); return geomap.getHeight(x,z,xm,zm);
} }
/** /**
* Locks the mesh (sets it static) to improve performance. * Locks the mesh (sets it static) to improve performance.
* But it it not editable then. Set unlock to make it editable. * But it it not editable then. Set unlock to make it editable.
@ -626,7 +627,7 @@ public class TerrainPatch extends Geometry {
public void unlockMesh() { public void unlockMesh() {
getMesh().setDynamic(); getMesh().setDynamic();
} }
/** /**
* Returns the offset amount this terrain patch uses for textures. * Returns the offset amount this terrain patch uses for textures.
* *
@ -797,7 +798,7 @@ public class TerrainPatch extends Geometry {
protected void setLodBottom(int lodBottom) { protected void setLodBottom(int lodBottom) {
this.lodBottom = lodBottom; this.lodBottom = lodBottom;
} }
/*public void setLodCalculator(LodCalculatorFactory lodCalculatorFactory) { /*public void setLodCalculator(LodCalculatorFactory lodCalculatorFactory) {
this.lodCalculatorFactory = lodCalculatorFactory; this.lodCalculatorFactory = lodCalculatorFactory;
setLodCalculator(lodCalculatorFactory.createCalculator(this)); setLodCalculator(lodCalculatorFactory.createCalculator(this));
@ -812,7 +813,7 @@ public class TerrainPatch extends Geometry {
if (other instanceof BoundingVolume) if (other instanceof BoundingVolume)
if (!getWorldBound().intersects((BoundingVolume)other)) if (!getWorldBound().intersects((BoundingVolume)other))
return 0; return 0;
if(other instanceof Ray) if(other instanceof Ray)
return collideWithRay((Ray)other, results); return collideWithRay((Ray)other, results);
else if (other instanceof BoundingVolume) else if (other instanceof BoundingVolume)
@ -853,7 +854,7 @@ public class TerrainPatch extends Geometry {
* This most definitely is not optimized. * This most definitely is not optimized.
*/ */
private int collideWithBoundingBox(BoundingBox bbox, CollisionResults results) { private int collideWithBoundingBox(BoundingBox bbox, CollisionResults results) {
// test the four corners, for cases where the bbox dimensions are less than the terrain grid size, which is probably most of the time // test the four corners, for cases where the bbox dimensions are less than the terrain grid size, which is probably most of the time
Vector3f topLeft = worldCoordinateToLocal(new Vector3f(bbox.getCenter().x-bbox.getXExtent(), 0, bbox.getCenter().z-bbox.getZExtent())); Vector3f topLeft = worldCoordinateToLocal(new Vector3f(bbox.getCenter().x-bbox.getXExtent(), 0, bbox.getCenter().z-bbox.getZExtent()));
Vector3f topRight = worldCoordinateToLocal(new Vector3f(bbox.getCenter().x+bbox.getXExtent(), 0, bbox.getCenter().z-bbox.getZExtent())); Vector3f topRight = worldCoordinateToLocal(new Vector3f(bbox.getCenter().x+bbox.getXExtent(), 0, bbox.getCenter().z-bbox.getZExtent()));
@ -872,11 +873,11 @@ public class TerrainPatch extends Geometry {
t = getTriangle(bottomRight.x, bottomRight.z); t = getTriangle(bottomRight.x, bottomRight.z);
if (t != null && bbox.collideWith(t, results) > 0) if (t != null && bbox.collideWith(t, results) > 0)
return 1; return 1;
// box is larger than the points on the terrain, so test against the points // box is larger than the points on the terrain, so test against the points
for (float z=topLeft.z; z<bottomLeft.z; z+=1) { for (float z=topLeft.z; z<bottomLeft.z; z+=1) {
for (float x=topLeft.x; x<topRight.x; x+=1) { for (float x=topLeft.x; x<topRight.x; x+=1) {
if (x < 0 || z < 0 || x >= size || z >= size) if (x < 0 || z < 0 || x >= size || z >= size)
continue; continue;
t = getTriangle(x,z); t = getTriangle(x,z);
@ -895,7 +896,7 @@ public class TerrainPatch extends Geometry {
// this reduces the save size to 10% by not saving the mesh // this reduces the save size to 10% by not saving the mesh
Mesh temp = getMesh(); Mesh temp = getMesh();
mesh = null; mesh = null;
super.write(ex); super.write(ex);
OutputCapsule oc = ex.getCapsule(this); OutputCapsule oc = ex.getCapsule(this);
oc.write(size, "size", 16); oc.write(size, "size", 16);
@ -908,7 +909,7 @@ public class TerrainPatch extends Geometry {
//oc.write(lodCalculatorFactory, "lodCalculatorFactory", null); //oc.write(lodCalculatorFactory, "lodCalculatorFactory", null);
oc.write(lodEntropy, "lodEntropy", null); oc.write(lodEntropy, "lodEntropy", null);
oc.write(geomap, "geomap", null); oc.write(geomap, "geomap", null);
setMesh(temp); setMesh(temp);
} }
@ -927,7 +928,7 @@ public class TerrainPatch extends Geometry {
//lodCalculatorFactory = (LodCalculatorFactory) ic.readSavable("lodCalculatorFactory", null); //lodCalculatorFactory = (LodCalculatorFactory) ic.readSavable("lodCalculatorFactory", null);
lodEntropy = ic.readFloatArray("lodEntropy", null); lodEntropy = ic.readFloatArray("lodEntropy", null);
geomap = (LODGeomap) ic.readSavable("geomap", null); geomap = (LODGeomap) ic.readSavable("geomap", null);
Mesh regen = geomap.createMesh(stepScale, new Vector2f(1,1), offset, offsetAmount, totalSize, false); Mesh regen = geomap.createMesh(stepScale, new Vector2f(1,1), offset, offsetAmount, totalSize, false);
setMesh(regen); setMesh(regen);
//TangentBinormalGenerator.generate(this); // note that this will be removed //TangentBinormalGenerator.generate(this); // note that this will be removed
@ -955,6 +956,33 @@ public class TerrainPatch extends Geometry {
return clone; return clone;
} }
/**
* Called internally by com.jme3.util.clone.Cloner. Do not call directly.
*/
@Override
public void cloneFields( Cloner cloner, Object original ) {
this.stepScale = cloner.clone(stepScale);
this.offset = cloner.clone(offset);
this.leftNeighbour = null;
this.topNeighbour = null;
this.rightNeighbour = null;
this.bottomNeighbour = null;
// Don't feel like making geomap cloneable tonight
// so I'll copy the old logic.
this.geomap = new LODGeomap(size, geomap.getHeightArray());
Mesh m = geomap.createMesh(stepScale, Vector2f.UNIT_XY, offset, offsetAmount, totalSize, false);
this.setMesh(m);
// In this case, we always clone material even if the cloner is setup
// not to clone it. Terrain uses mutable textures and stuff so it's important
// to clone it. (At least that's my understanding and is evidenced by the old
// clone code specifically cloning material.) -pspeed
this.material = material.clone();
}
protected void ensurePositiveVolumeBBox() { protected void ensurePositiveVolumeBBox() {
if (getModelBound() instanceof BoundingBox) { if (getModelBound() instanceof BoundingBox) {
if (((BoundingBox)getModelBound()).getYExtent() < 0.001f) { if (((BoundingBox)getModelBound()).getYExtent() < 0.001f) {

@ -55,6 +55,7 @@ import com.jme3.terrain.geomipmap.picking.BresenhamTerrainPicker;
import com.jme3.terrain.geomipmap.picking.TerrainPickData; import com.jme3.terrain.geomipmap.picking.TerrainPickData;
import com.jme3.terrain.geomipmap.picking.TerrainPicker; import com.jme3.terrain.geomipmap.picking.TerrainPicker;
import com.jme3.util.TangentBinormalGenerator; import com.jme3.util.TangentBinormalGenerator;
import com.jme3.util.clone.Cloner;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@ -126,7 +127,7 @@ public class TerrainQuad extends Node implements Terrain {
private Vector3f lastScale = Vector3f.UNIT_XYZ; private Vector3f lastScale = Vector3f.UNIT_XYZ;
protected NeighbourFinder neighbourFinder; protected NeighbourFinder neighbourFinder;
public TerrainQuad() { public TerrainQuad() {
super("Terrain"); super("Terrain");
} }
@ -144,24 +145,24 @@ public class TerrainQuad extends Node implements Terrain {
* </p> * </p>
* @param name the name of the scene element. This is required for * @param name the name of the scene element. This is required for
* identification and comparison purposes. * identification and comparison purposes.
* @param patchSize size of the individual patches (geometry). Power of 2 plus 1, * @param patchSize size of the individual patches (geometry). Power of 2 plus 1,
* must be smaller than totalSize. (eg. 33, 65...) * must be smaller than totalSize. (eg. 33, 65...)
* @param totalSize the size of this entire terrain (on one side). Power of 2 plus 1 * @param totalSize the size of this entire terrain (on one side). Power of 2 plus 1
* (eg. 513, 1025, 2049...) * (eg. 513, 1025, 2049...)
* @param heightMap The height map to generate the terrain from (a flat * @param heightMap The height map to generate the terrain from (a flat
* height map will be generated if this is null). The size of one side of the heightmap * height map will be generated if this is null). The size of one side of the heightmap
* must match the totalSize. So a 513x513 heightmap is needed for a terrain with totalSize of 513. * must match the totalSize. So a 513x513 heightmap is needed for a terrain with totalSize of 513.
*/ */
public TerrainQuad(String name, int patchSize, int totalSize, float[] heightMap) { public TerrainQuad(String name, int patchSize, int totalSize, float[] heightMap) {
this(name, patchSize, totalSize, Vector3f.UNIT_XYZ, heightMap); this(name, patchSize, totalSize, Vector3f.UNIT_XYZ, heightMap);
affectedAreaBBox = new BoundingBox(new Vector3f(0,0,0), size*2, Float.MAX_VALUE, size*2); affectedAreaBBox = new BoundingBox(new Vector3f(0,0,0), size*2, Float.MAX_VALUE, size*2);
fixNormalEdges(affectedAreaBBox); fixNormalEdges(affectedAreaBBox);
addControl(new NormalRecalcControl(this)); addControl(new NormalRecalcControl(this));
} }
/** /**
* *
* @param name the name of the scene element. This is required for * @param name the name of the scene element. This is required for
* identification and comparison purposes. * identification and comparison purposes.
* @param patchSize size of the individual patches * @param patchSize size of the individual patches
@ -176,7 +177,7 @@ public class TerrainQuad extends Node implements Terrain {
} }
/** /**
* *
* @param name the name of the scene element. This is required for * @param name the name of the scene element. This is required for
* identification and comparison purposes. * identification and comparison purposes.
* @param patchSize size of the individual patches * @param patchSize size of the individual patches
@ -192,9 +193,9 @@ public class TerrainQuad extends Node implements Terrain {
//fixNormalEdges(affectedAreaBBox); //fixNormalEdges(affectedAreaBBox);
//addControl(new NormalRecalcControl(this)); //addControl(new NormalRecalcControl(this));
} }
/** /**
* *
* @param name the name of the scene element. This is required for * @param name the name of the scene element. This is required for
* identification and comparison purposes. * identification and comparison purposes.
* @param patchSize size of the individual patches * @param patchSize size of the individual patches
@ -217,17 +218,17 @@ public class TerrainQuad extends Node implements Terrain {
Vector2f offset, float offsetAmount) Vector2f offset, float offsetAmount)
{ {
super(name); super(name);
if (heightMap == null) if (heightMap == null)
heightMap = generateDefaultHeightMap(quadSize); heightMap = generateDefaultHeightMap(quadSize);
if (!FastMath.isPowerOfTwo(quadSize - 1)) { if (!FastMath.isPowerOfTwo(quadSize - 1)) {
throw new RuntimeException("size given: " + quadSize + " Terrain quad sizes may only be (2^N + 1)"); throw new RuntimeException("size given: " + quadSize + " Terrain quad sizes may only be (2^N + 1)");
} }
if (FastMath.sqrt(heightMap.length) > quadSize) { if (FastMath.sqrt(heightMap.length) > quadSize) {
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!"); 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!");
} }
this.offset = offset; this.offset = offset;
this.offsetAmount = offsetAmount; this.offsetAmount = offsetAmount;
this.totalSize = totalSize; this.totalSize = totalSize;
@ -248,7 +249,7 @@ public class TerrainQuad extends Node implements Terrain {
public void recalculateAllNormals() { public void recalculateAllNormals() {
affectedAreaBBox = new BoundingBox(new Vector3f(0,0,0), totalSize*2, Float.MAX_VALUE, totalSize*2); affectedAreaBBox = new BoundingBox(new Vector3f(0,0,0), totalSize*2, Float.MAX_VALUE, totalSize*2);
} }
/** /**
* Create just a flat heightmap * Create just a flat heightmap
*/ */
@ -267,11 +268,11 @@ public class TerrainQuad extends Node implements Terrain {
//TODO background-thread this if it ends up being expensive //TODO background-thread this if it ends up being expensive
fixNormals(affectedAreaBBox); // the affected patches fixNormals(affectedAreaBBox); // the affected patches
fixNormalEdges(affectedAreaBBox); // the edges between the patches fixNormalEdges(affectedAreaBBox); // the edges between the patches
setNormalRecalcNeeded(null); // set to false setNormalRecalcNeeded(null); // set to false
} }
} }
/** /**
* Caches the transforms (except rotation) so the LOD calculator, * Caches the transforms (except rotation) so the LOD calculator,
* which runs on a separate thread, can access them safely. * which runs on a separate thread, can access them safely.
@ -343,7 +344,7 @@ public class TerrainQuad extends Node implements Terrain {
public Material getMaterial() { public Material getMaterial() {
return getMaterial(null); return getMaterial(null);
} }
public Material getMaterial(Vector3f worldLocation) { public Material getMaterial(Vector3f worldLocation) {
// get the material from one of the children. They all share the same material // get the material from one of the children. They all share the same material
if (children != null) { if (children != null) {
@ -362,7 +363,7 @@ public class TerrainQuad extends Node implements Terrain {
public int getNumMajorSubdivisions() { public int getNumMajorSubdivisions() {
return 1; return 1;
} }
protected boolean calculateLod(List<Vector3f> location, HashMap<String,UpdatedTerrainPatch> updates, LodCalculator lodCalculator) { protected boolean calculateLod(List<Vector3f> location, HashMap<String,UpdatedTerrainPatch> updates, LodCalculator lodCalculator) {
@ -434,7 +435,7 @@ public class TerrainQuad extends Node implements Terrain {
utp.setBottomLod(utpD.getNewLod()); utp.setBottomLod(utpD.getNewLod());
utpD.setTopLod(utp.getNewLod()); utpD.setTopLod(utp.getNewLod());
} }
if (left != null) { if (left != null) {
UpdatedTerrainPatch utpL = updated.get(left.getName()); UpdatedTerrainPatch utpL = updated.get(left.getName());
if (utpL == null) { if (utpL == null) {
@ -478,7 +479,7 @@ public class TerrainQuad extends Node implements Terrain {
} }
} }
} }
/** /**
* Find any neighbours that should have their edges seamed because another neighbour * Find any neighbours that should have their edges seamed because another neighbour
* changed its LOD to a greater value (less detailed) * changed its LOD to a greater value (less detailed)
@ -587,10 +588,10 @@ public class TerrainQuad extends Node implements Terrain {
/** /**
* Quadrants, world coordinates, and heightmap coordinates (Y-up): * Quadrants, world coordinates, and heightmap coordinates (Y-up):
* *
* -z * -z
* -u | * -u |
* -v 1|3 * -v 1|3
* -x ----+---- x * -x ----+---- x
* 2|4 u * 2|4 u
* | v * | v
@ -668,7 +669,7 @@ public class TerrainQuad extends Node implements Terrain {
quad3.setLocalTranslation(origin3); quad3.setLocalTranslation(origin3);
quad3.quadrant = 3; quad3.quadrant = 3;
this.attachChild(quad3); this.attachChild(quad3);
// 4 lower right of heightmap, lower right quad // 4 lower right of heightmap, lower right quad
float[] heightBlock4 = createHeightSubBlock(heightMap, split - 1, float[] heightBlock4 = createHeightSubBlock(heightMap, split - 1,
split - 1, split); split - 1, split);
@ -892,7 +893,7 @@ public class TerrainQuad extends Node implements Terrain {
} }
return false; return false;
} }
/** /**
* This will cause all normals for this terrain quad to be recalculated * This will cause all normals for this terrain quad to be recalculated
*/ */
@ -1024,14 +1025,14 @@ public class TerrainQuad extends Node implements Terrain {
int col; int col;
int row; int row;
Spatial child; Spatial child;
QuadrantChild(int col, int row, Spatial child) { QuadrantChild(int col, int row, Spatial child) {
this.col = col; this.col = col;
this.row = row; this.row = row;
this.child = child; this.child = child;
} }
} }
private QuadrantChild findMatchingChild(int x, int z) { private QuadrantChild findMatchingChild(int x, int z) {
int quad = findQuadrant(x, z); int quad = findQuadrant(x, z);
int split = (size + 1) >> 1; int split = (size + 1) >> 1;
@ -1069,7 +1070,7 @@ public class TerrainQuad extends Node implements Terrain {
} }
return null; return null;
} }
/** /**
* Get the interpolated height of the terrain at the specified point. * Get the interpolated height of the terrain at the specified point.
* @param xz the location to get the height for * @param xz the location to get the height for
@ -1090,7 +1091,7 @@ public class TerrainQuad extends Node implements Terrain {
* gets an interpolated value at the specified point * gets an interpolated value at the specified point
*/ */
protected float getHeight(int x, int z, float xm, float zm) { protected float getHeight(int x, int z, float xm, float zm) {
QuadrantChild match = findMatchingChild(x,z); QuadrantChild match = findMatchingChild(x,z);
if (match != null) { if (match != null) {
if (match.child instanceof TerrainQuad) { if (match.child instanceof TerrainQuad) {
@ -1107,10 +1108,10 @@ public class TerrainQuad extends Node implements Terrain {
float x = (float)(((xz.x - getWorldTranslation().x) / getWorldScale().x) + (float)(totalSize-1) / 2f); float x = (float)(((xz.x - getWorldTranslation().x) / getWorldScale().x) + (float)(totalSize-1) / 2f);
float z = (float)(((xz.y - getWorldTranslation().z) / getWorldScale().z) + (float)(totalSize-1) / 2f); float z = (float)(((xz.y - getWorldTranslation().z) / getWorldScale().z) + (float)(totalSize-1) / 2f);
Vector3f normal = getNormal(x, z, xz); Vector3f normal = getNormal(x, z, xz);
return normal; return normal;
} }
protected Vector3f getNormal(float x, float z, Vector2f xz) { protected Vector3f getNormal(float x, float z, Vector2f xz) {
x-=0.5f; x-=0.5f;
z-=0.5f; z-=0.5f;
@ -1125,15 +1126,15 @@ public class TerrainQuad extends Node implements Terrain {
// v3--v4 | Z // v3--v4 | Z
// | // |
// <-------Y // <-------Y
// X // X
Vector3f n1 = getMeshNormal((int) FastMath.ceil(x), (int) FastMath.ceil(z)); Vector3f n1 = getMeshNormal((int) FastMath.ceil(x), (int) FastMath.ceil(z));
Vector3f n2 = getMeshNormal((int) FastMath.floor(x), (int) FastMath.ceil(z)); Vector3f n2 = getMeshNormal((int) FastMath.floor(x), (int) FastMath.ceil(z));
Vector3f n3 = getMeshNormal((int) FastMath.ceil(x), (int) FastMath.floor(z)); Vector3f n3 = getMeshNormal((int) FastMath.ceil(x), (int) FastMath.floor(z));
Vector3f n4 = getMeshNormal((int) FastMath.floor(x), (int) FastMath.floor(z)); Vector3f n4 = getMeshNormal((int) FastMath.floor(x), (int) FastMath.floor(z));
return n1.add(n2).add(n3).add(n4).normalize(); return n1.add(n2).add(n3).add(n4).normalize();
} }
public void setHeight(Vector2f xz, float height) { public void setHeight(Vector2f xz, float height) {
List<Vector2f> coord = new ArrayList<Vector2f>(); List<Vector2f> coord = new ArrayList<Vector2f>();
coord.add(xz); coord.add(xz);
@ -1291,7 +1292,7 @@ public class TerrainQuad extends Node implements Terrain {
return (x >= 0 && x <= totalSize && z >= 0 && z <= totalSize); return (x >= 0 && x <= totalSize && z >= 0 && z <= totalSize);
} }
public int getTerrainSize() { public int getTerrainSize() {
return totalSize; return totalSize;
} }
@ -1750,7 +1751,7 @@ public class TerrainQuad extends Node implements Terrain {
totalSize = c.readInt("totalSize", 0); totalSize = c.readInt("totalSize", 0);
//lodCalculator = (LodCalculator) c.readSavable("lodCalculator", createDefaultLodCalculator()); //lodCalculator = (LodCalculator) c.readSavable("lodCalculator", createDefaultLodCalculator());
//lodCalculatorFactory = (LodCalculatorFactory) c.readSavable("lodCalculatorFactory", null); //lodCalculatorFactory = (LodCalculatorFactory) c.readSavable("lodCalculatorFactory", null);
if ( !(getParent() instanceof TerrainQuad) ) { if ( !(getParent() instanceof TerrainQuad) ) {
BoundingBox all = new BoundingBox(getWorldTranslation(), totalSize, totalSize, totalSize); BoundingBox all = new BoundingBox(getWorldTranslation(), totalSize, totalSize, totalSize);
affectedAreaBBox = all; affectedAreaBBox = all;
@ -1793,10 +1794,10 @@ public class TerrainQuad extends Node implements Terrain {
quadClone.quadrant = quadrant; quadClone.quadrant = quadrant;
//quadClone.lodCalculatorFactory = lodCalculatorFactory.clone(); //quadClone.lodCalculatorFactory = lodCalculatorFactory.clone();
//quadClone.lodCalculator = lodCalculator.clone(); //quadClone.lodCalculator = lodCalculator.clone();
TerrainLodControl lodControlCloned = this.getControl(TerrainLodControl.class); TerrainLodControl lodControlCloned = this.getControl(TerrainLodControl.class);
TerrainLodControl lodControl = quadClone.getControl(TerrainLodControl.class); TerrainLodControl lodControl = quadClone.getControl(TerrainLodControl.class);
if (lodControlCloned != null && !(getParent() instanceof TerrainQuad)) { if (lodControlCloned != null && !(getParent() instanceof TerrainQuad)) {
//lodControlCloned.setLodCalculator(lodControl.getLodCalculator().clone()); //lodControlCloned.setLodCalculator(lodControl.getLodCalculator().clone());
} }
@ -1806,7 +1807,25 @@ public class TerrainQuad extends Node implements Terrain {
return quadClone; return quadClone;
} }
/**
* Called internally by com.jme3.util.clone.Cloner. Do not call directly.
*/
@Override
public void cloneFields( Cloner cloner, Object original ) {
this.stepScale = cloner.clone(stepScale);
this.offset = cloner.clone(offset);
// This was not cloned before... I think that's a mistake.
this.affectedAreaBBox = cloner.clone(affectedAreaBBox);
// picker is not cloneable and not cloned. This also seems like
// a mistake if you ever load the same terrain twice.
// this.picker = cloner.clone(picker);
// neighbourFinder is also not cloned. Maybe that's ok.
}
@Override @Override
protected void setParent(Node parent) { protected void setParent(Node parent) {
super.setParent(parent); super.setParent(parent);
@ -1815,7 +1834,7 @@ public class TerrainQuad extends Node implements Terrain {
clearCaches(); clearCaches();
} }
} }
/** /**
* Removes any cached references this terrain is holding, in particular * Removes any cached references this terrain is holding, in particular
* the TerrainPatch's neighbour references. * the TerrainPatch's neighbour references.
@ -1834,7 +1853,7 @@ public class TerrainQuad extends Node implements Terrain {
} }
} }
} }
public int getMaxLod() { public int getMaxLod() {
if (maxLod < 0) if (maxLod < 0)
maxLod = Math.max(1, (int) (FastMath.log(size-1)/FastMath.log(2)) -1); // -1 forces our minimum of 4 triangles wide maxLod = Math.max(1, (int) (FastMath.log(size-1)/FastMath.log(2)) -1); // -1 forces our minimum of 4 triangles wide

Loading…
Cancel
Save