ソースを参照

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
bre..ns 13 年 前
コミット
64b736f2e9

+ 56 - 1
engine/src/terrain/com/jme3/terrain/geomipmap/LODGeomap.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);

+ 4 - 0
engine/src/terrain/com/jme3/terrain/geomipmap/TerrainPatch.java

@@ -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.

+ 72 - 28
engine/src/terrain/com/jme3/terrain/geomipmap/TerrainQuad.java

@@ -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 z = (float)(((xz.y - getWorldTranslation().z) / getWorldScale().z) + (float)totalSize / 2f);
-        float height = getHeight(x, z);
+        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 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) {
-        x-=0.5f;
-        z-=0.5f;
-        float col = FastMath.floor(x);
-        float row = FastMath.floor(z);
-        boolean onX = false;
-        if(1 - (x - col)-(z - row) < 0) // what triangle to interpolate on
-            onX = true;
-        // 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;
+    protected float getHeight(int x, int z, float xm, float zm) {
+        
+        QuadrantChild match = findMatchingChild(x,z);
+        if (match != null) {
+            if (match.child instanceof TerrainQuad) {
+                return ((TerrainQuad) match.child).getHeight(match.col, match.row, xm, zm);
+            } else if (match.child instanceof TerrainPatch) {
+                return ((TerrainPatch) match.child).getHeight(match.col, match.row, xm, zm);
+            }
         }
         }
+        return Float.NaN;
     }
     }
 
 
     public Vector3f getNormal(Vector2f xz) {
     public Vector3f getNormal(Vector2f xz) {