fix a bug in terrain getHeight for some edge cases

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@9275 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
3.0
bre..ns 13 years ago
parent e3ed779fc9
commit 64b736f2e9
  1. 57
      engine/src/terrain/com/jme3/terrain/geomipmap/LODGeomap.java
  2. 4
      engine/src/terrain/com/jme3/terrain/geomipmap/TerrainPatch.java
  3. 100
      engine/src/terrain/com/jme3/terrain/geomipmap/TerrainQuad.java

@ -929,6 +929,61 @@ public class LODGeomap extends GeoMap {
} }
} }
/**
* Get the two triangles that make up the grid section at the specified point.
*
* For every grid space there are two triangles oriented like this:
* *----*
* |a / |
* | / b|
* *----*
* The corners of the mesh have differently oriented triangles. The two
* corners that we have to special-case are the top left and bottom right
* corners. They are oriented inversely:
* *----*
* | \ b|
* |a \ |
* *----*
*/
protected float getHeight(int x, int z, float xm, float zm) {
int index = findClosestHeightIndex(x, z);
if (index < 0) {
return Float.NaN;
}
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
//float dix = (x % 1f) ;
//float diz = (z % 1f) ;
if ((x == 0 && z == 0) || (x == width - 2 && z == width - 2)) {
// top left or bottom right grid point
/* 1----2
* | \ b|
* |a \ |
* 3----4 */
if (xm<zm)
return h1 + xm*(h4-h3) + zm*(h3-h1);
else
return h1 + xm*(h2-h1) + zm*(h4-h2);
} else {
// all other grid points
/* 1----2
* |a / |
* | / b|
* 3----4 */
if (xm<(1-zm))
return h3 + (xm)*(h2-h1) + (1f-zm)*(h1-h3);
else
return h3 + (xm)*(h4-h3) + (1f-zm)*(h2-h4);
}
}
/** /**
* Get a representation of the underlying triangle at the given point, * Get a representation of the underlying triangle at the given point,
* translated to world coordinates. * translated to world coordinates.
@ -1010,7 +1065,7 @@ public class LODGeomap extends GeoMap {
float h4 = hdata[index + width + 1]; // bottom right float h4 = hdata[index + width + 1]; // bottom right
if ((gridX == 0 && gridY == 0) || (gridX == width - 1 && gridY == width - 1)) { if ((gridX == 0 && gridY == 0) || (gridX == width - 2 && gridY == width - 2)) {
// top left or bottom right grid point // top left or bottom right grid point
t.get(0).x = (gridX); t.get(0).x = (gridX);
t.get(0).y = (h1); t.get(0).y = (h1);

@ -590,6 +590,10 @@ public class TerrainPatch extends Geometry {
return normal; return normal;
} }
protected float getHeight(int x, int z, float xm, float 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.

@ -922,44 +922,88 @@ public class TerrainQuad extends Node implements Terrain {
return null; return null;
} }
/**
* Used for searching for a child and keeping
* track of its quadrant
*/
private class QuadrantChild {
int col;
int row;
Spatial child;
QuadrantChild(int col, int row, Spatial child) {
this.col = col;
this.row = row;
this.child = child;
}
}
private QuadrantChild findMatchingChild(int x, int z) {
int quad = findQuadrant(x, z);
int split = (size + 1) >> 1;
if (children != null) {
for (int i = children.size(); --i >= 0;) {
Spatial spat = children.get(i);
int col = x;
int row = z;
boolean match = false;
// get the childs quadrant
int childQuadrant = 0;
if (spat instanceof TerrainQuad) {
childQuadrant = ((TerrainQuad) spat).getQuadrant();
} else if (spat instanceof TerrainPatch) {
childQuadrant = ((TerrainPatch) spat).getQuadrant();
}
if (childQuadrant == 1 && (quad & 1) != 0) {
match = true;
} else if (childQuadrant == 2 && (quad & 2) != 0) {
row = z - split + 1;
match = true;
} else if (childQuadrant == 3 && (quad & 4) != 0) {
col = x - split + 1;
match = true;
} else if (childQuadrant == 4 && (quad & 8) != 0) {
col = x - split + 1;
row = z - split + 1;
match = true;
}
if (match)
return new QuadrantChild(col, row, spat);
}
}
return null;
}
/**
* Get the interpolated height of the terrain at the specified point.
* @param xz the location to get the height for
* @return Float.NAN if the value does not exist, or the coordinates are outside of the terrain
*/
public float getHeight(Vector2f xz) { public float getHeight(Vector2f xz) {
// offset // offset
float x = (float)(((xz.x - getWorldTranslation().x) / getWorldScale().x) + (float)totalSize / 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 / 2f); float z = (float)(((xz.y - getWorldTranslation().z) / getWorldScale().z) + (float)(totalSize-1) / 2f);
float height = getHeight(x, z); float height = getHeight((int)x, (int)z, (x%1f), (z%1f));
height *= getWorldScale().y; height *= getWorldScale().y;
return height; return height;
} }
/* /*
* gets an interpolated value at the specified point * gets an interpolated value at the specified point
* @param x coordinate translated into actual (positive) terrain grid coordinates
* @param y coordinate translated into actual (positive) terrain grid coordinates
*/ */
protected float getHeight(float x, float z) { protected float getHeight(int x, int z, float xm, float zm) {
x-=0.5f;
z-=0.5f; QuadrantChild match = findMatchingChild(x,z);
float col = FastMath.floor(x); if (match != null) {
float row = FastMath.floor(z); if (match.child instanceof TerrainQuad) {
boolean onX = false; return ((TerrainQuad) match.child).getHeight(match.col, match.row, xm, zm);
if(1 - (x - col)-(z - row) < 0) // what triangle to interpolate on } else if (match.child instanceof TerrainPatch) {
onX = true; return ((TerrainPatch) match.child).getHeight(match.col, match.row, xm, zm);
// v1--v2 ^ }
// | / | |
// | / | |
// v3--v4 | Z
// |
// <-------Y
// X
float v1 = getHeightmapHeight((int) FastMath.ceil(x), (int) FastMath.ceil(z));
float v2 = getHeightmapHeight((int) FastMath.floor(x), (int) FastMath.ceil(z));
float v3 = getHeightmapHeight((int) FastMath.ceil(x), (int) FastMath.floor(z));
float v4 = getHeightmapHeight((int) FastMath.floor(x), (int) FastMath.floor(z));
if (onX) {
return ((x - col) + (z - row) - 1f)*v1 + (1f - (x - col))*v2 + (1f - (z - row))*v3;
} else {
return (1f - (x - col) - (z - row))*v4 + (z - row)*v2 + (x - col)*v3;
} }
return Float.NaN;
} }
public Vector3f getNormal(Vector2f xz) { public Vector3f getNormal(Vector2f xz) {

Loading…
Cancel
Save