OSDN Git Service

Update terrain.
authorkobayasi <kobayasi@pscnet.co.jp>
Thu, 20 Feb 2014 01:35:07 +0000 (10:35 +0900)
committerkobayasi <kobayasi@pscnet.co.jp>
Thu, 20 Feb 2014 01:35:07 +0000 (10:35 +0900)
86 files changed:
LICENSE.txt [new file with mode: 0644]
engine/build.sbt
engine/src/terrain/Common/MatDefs/Terrain/HeightBasedTerrain.j3md
engine/src/terrain/Common/MatDefs/Terrain/Terrain.j3md
engine/src/terrain/Common/MatDefs/Terrain/TerrainLighting.frag
engine/src/terrain/Common/MatDefs/Terrain/TerrainLighting.j3md
engine/src/terrain/com/jme3/terrain/GeoMap.java
engine/src/terrain/com/jme3/terrain/MapUtils.java [deleted file]
engine/src/terrain/com/jme3/terrain/ProgressMonitor.java
engine/src/terrain/com/jme3/terrain/Terrain.java
engine/src/terrain/com/jme3/terrain/geomipmap/LODGeomap.java
engine/src/terrain/com/jme3/terrain/geomipmap/MultiTerrainLodControl.java [new file with mode: 0644]
engine/src/terrain/com/jme3/terrain/geomipmap/NeighbourFinder.java [new file with mode: 0644]
engine/src/terrain/com/jme3/terrain/geomipmap/NormalRecalcControl.java
engine/src/terrain/com/jme3/terrain/geomipmap/TerrainGrid.java
engine/src/terrain/com/jme3/terrain/geomipmap/TerrainGridListener.java
engine/src/terrain/com/jme3/terrain/geomipmap/TerrainGridLodControl.java [new file with mode: 0644]
engine/src/terrain/com/jme3/terrain/geomipmap/TerrainGridTileLoader.java [new file with mode: 0644]
engine/src/terrain/com/jme3/terrain/geomipmap/TerrainLodControl.java
engine/src/terrain/com/jme3/terrain/geomipmap/TerrainPatch.java
engine/src/terrain/com/jme3/terrain/geomipmap/TerrainQuad.java
engine/src/terrain/com/jme3/terrain/geomipmap/UpdatedTerrainPatch.java
engine/src/terrain/com/jme3/terrain/geomipmap/grid/AssetTileLoader.java [new file with mode: 0644]
engine/src/terrain/com/jme3/terrain/geomipmap/grid/FractalTileLoader.java [new file with mode: 0644]
engine/src/terrain/com/jme3/terrain/geomipmap/grid/ImageTileLoader.java [new file with mode: 0644]
engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/DistanceLodCalculator.java
engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/LodCalculator.java
engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/LodCalculatorFactory.java
engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/LodDistanceCalculatorFactory.java
engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/LodPerspectiveCalculatorFactory.java
engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/LodThreshold.java
engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/PerspectiveLodCalculator.java
engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/SimpleLodThreshold.java
engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/util/EntropyComputeUtil.java
engine/src/terrain/com/jme3/terrain/geomipmap/picking/BresenhamTerrainPicker.java
engine/src/terrain/com/jme3/terrain/geomipmap/picking/BresenhamYUpGridTracer.java
engine/src/terrain/com/jme3/terrain/geomipmap/picking/TerrainPickData.java
engine/src/terrain/com/jme3/terrain/geomipmap/picking/TerrainPicker.java
engine/src/terrain/com/jme3/terrain/heightmap/AbstractHeightMap.java
engine/src/terrain/com/jme3/terrain/heightmap/CombinerHeightMap.java
engine/src/terrain/com/jme3/terrain/heightmap/FaultHeightMap.java
engine/src/terrain/com/jme3/terrain/heightmap/FluidSimHeightMap.java
engine/src/terrain/com/jme3/terrain/heightmap/FractalHeightMapGrid.java [deleted file]
engine/src/terrain/com/jme3/terrain/heightmap/Grayscale16BitHeightMap.java [deleted file]
engine/src/terrain/com/jme3/terrain/heightmap/HeightMap.java
engine/src/terrain/com/jme3/terrain/heightmap/HeightMapGrid.java
engine/src/terrain/com/jme3/terrain/heightmap/HillHeightMap.java
engine/src/terrain/com/jme3/terrain/heightmap/ImageBasedHeightMap.java
engine/src/terrain/com/jme3/terrain/heightmap/ImageBasedHeightMapGrid.java
engine/src/terrain/com/jme3/terrain/heightmap/ImageHeightmap.java [new file with mode: 0644]
engine/src/terrain/com/jme3/terrain/heightmap/MidpointDisplacementHeightMap.java
engine/src/terrain/com/jme3/terrain/heightmap/Namer.java
engine/src/terrain/com/jme3/terrain/heightmap/ParticleDepositionHeightMap.java
engine/src/terrain/com/jme3/terrain/heightmap/RawHeightMap.java
engine/src/terrain/com/jme3/terrain/noise/Basis.java [new file with mode: 0644]
engine/src/terrain/com/jme3/terrain/noise/Color.java [new file with mode: 0644]
engine/src/terrain/com/jme3/terrain/noise/Filter.java [new file with mode: 0644]
engine/src/terrain/com/jme3/terrain/noise/ShaderUtils.java [new file with mode: 0644]
engine/src/terrain/com/jme3/terrain/noise/basis/FilteredBasis.java [new file with mode: 0644]
engine/src/terrain/com/jme3/terrain/noise/basis/ImprovedNoise.java [new file with mode: 0644]
engine/src/terrain/com/jme3/terrain/noise/basis/Noise.java [new file with mode: 0644]
engine/src/terrain/com/jme3/terrain/noise/basis/NoiseAggregator.java [new file with mode: 0644]
engine/src/terrain/com/jme3/terrain/noise/filter/AbstractFilter.java [new file with mode: 0644]
engine/src/terrain/com/jme3/terrain/noise/filter/HydraulicErodeFilter.java [new file with mode: 0644]
engine/src/terrain/com/jme3/terrain/noise/filter/IterativeFilter.java [new file with mode: 0644]
engine/src/terrain/com/jme3/terrain/noise/filter/OptimizedErode.java [new file with mode: 0644]
engine/src/terrain/com/jme3/terrain/noise/filter/PerturbFilter.java [new file with mode: 0644]
engine/src/terrain/com/jme3/terrain/noise/filter/SmoothFilter.java [new file with mode: 0644]
engine/src/terrain/com/jme3/terrain/noise/filter/ThermalErodeFilter.java [new file with mode: 0644]
engine/src/terrain/com/jme3/terrain/noise/fractal/Fractal.java [new file with mode: 0644]
engine/src/terrain/com/jme3/terrain/noise/fractal/FractalSum.java [new file with mode: 0644]
engine/src/terrain/com/jme3/terrain/noise/modulator/CatRom2.java [new file with mode: 0644]
engine/src/terrain/com/jme3/terrain/noise/modulator/Modulator.java [new file with mode: 0644]
engine/src/terrain/com/jme3/terrain/noise/modulator/NoiseModulator.java [new file with mode: 0644]
engine/src/test/jme3test/terrain/TerrainFractalGridTest.java
engine/src/test/jme3test/terrain/TerrainGridAlphaMapTest.java
engine/src/test/jme3test/terrain/TerrainGridSerializationTest.java [new file with mode: 0644]
engine/src/test/jme3test/terrain/TerrainGridTest.java
engine/src/test/jme3test/terrain/TerrainGridTileLoaderTest.java [new file with mode: 0644]
engine/src/test/jme3test/terrain/TerrainTest.java
engine/src/test/jme3test/terrain/TerrainTestAdvanced.java
engine/src/test/jme3test/terrain/TerrainTestAndroid.java [new file with mode: 0644]
engine/src/test/jme3test/terrain/TerrainTestCollision.java
engine/src/test/jme3test/terrain/TerrainTestModifyHeight.java
engine/src/test/jme3test/terrain/TerrainTestReadWrite.java
engine/src/test/jme3test/terrain/TerrainTestTile.java [new file with mode: 0644]

diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644 (file)
index 0000000..bb8e87f
--- /dev/null
@@ -0,0 +1,19 @@
+Copyright (c) 2014, Kazuhiko Kobayashi
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
+following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the
+following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
+the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
index f760eae..f91eea9 100644 (file)
@@ -24,12 +24,12 @@ unmanagedSourceDirectories in Compile := Seq(
   , baseDirectory.value / "src/ogre"
   , baseDirectory.value / "src/pack"
   , baseDirectory.value / "src/tools"
-//  , baseDirectory.value / "src/terrain"
+  , baseDirectory.value / "src/terrain"
   , baseDirectory.value / "src/xml"
 )
 
 sources in Compile ~= {
-  dirs => dirs filter(file => (!file.getAbsolutePath.contains("cinematic") && !file.getAbsolutePath.contains("CollisionShapeFactory.java")))
+  dirs => dirs filter(file => (!file.getAbsolutePath.contains("cinematic") /* && !file.getAbsolutePath.contains("CollisionShapeFactory.java")*/))
 }
 
 unmanagedResourceDirectories in Compile <<= unmanagedSourceDirectories in Compile
@@ -48,4 +48,4 @@ libraryDependencies += "xpp3" % "xpp3" % "1.1.4c"
 
 libraryDependencies += "net.sf.sociaal" % "j-ogg-oggd" % "3.0.0.20130526"
 
-libraryDependencies += "net.sf.sociaal" % "j-ogg-vorbisd" % "3.0.0.20130526"
\ No newline at end of file
+libraryDependencies += "net.sf.sociaal" % "j-ogg-vorbisd" % "3.0.0.20130526"
index 4b836f0..b57aaa4 100644 (file)
@@ -35,7 +35,6 @@ MaterialDef Terrain {
                }\r
        }\r
 \r
-       Technique FixedFunc {\r
+    Technique {\r
     }\r
-\r
 }
\ No newline at end of file
index 152f511..9702d65 100644 (file)
@@ -27,7 +27,6 @@ MaterialDef Terrain {
         }\r
        }\r
        \r
-       Technique FixedFunc {\r
+    Technique {\r
     }\r
-\r
 }
\ No newline at end of file
index d8f26e2..8484b45 100644 (file)
@@ -163,9 +163,6 @@ float lightComputeDiffuse(in vec3 norm, in vec3 lightdir, in vec3 viewdir){
 }\r
 \r
 float lightComputeSpecular(in vec3 norm, in vec3 viewdir, in vec3 lightdir, in float shiny){\r
-    if (shiny <= 1.0){\r
-        return 0.0;\r
-    }\r
     #ifdef WARDISO\r
         // Isotropic Ward\r
         vec3 halfVec = normalize(viewdir + lightdir);\r
@@ -185,7 +182,10 @@ float lightComputeSpecular(in vec3 norm, in vec3 viewdir, in vec3 lightdir, in f
 vec2 computeLighting(in vec3 wvPos, in vec3 wvNorm, in vec3 wvViewDir, in vec3 wvLightDir){\r
    float diffuseFactor = lightComputeDiffuse(wvNorm, wvLightDir, wvViewDir);\r
    float specularFactor = lightComputeSpecular(wvNorm, wvViewDir, wvLightDir, m_Shininess);\r
-   specularFactor *= step(1.0, m_Shininess);\r
+\r
+   if (m_Shininess <= 1.0) {\r
+       specularFactor = 0.0; // should be one instruction on most cards ..\r
+   }\r
 \r
    float att = vLightDir.w;\r
 \r
@@ -260,7 +260,6 @@ vec2 computeLighting(in vec3 wvPos, in vec3 wvNorm, in vec3 wvViewDir, in vec3 w
 \r
   vec3 calculateNormal(in vec2 texCoord) {\r
     vec3 normal = vec3(0,0,1);\r
-    vec4 normalHeight = vec4(0,0,0,0);\r
     vec3 n = vec3(0,0,0);\r
 \r
     vec4 alphaBlend = texture2D( m_AlphaMap, texCoord.xy );\r
@@ -273,105 +272,78 @@ vec2 computeLighting(in vec3 wvPos, in vec3 wvNorm, in vec3 wvViewDir, in vec3 w
     #endif\r
 \r
     #ifdef NORMALMAP\r
-      normalHeight = texture2D(m_NormalMap, texCoord * m_DiffuseMap_0_scale);\r
-      n = (normalHeight.xyz * vec3(2.0) - vec3(1.0));\r
-      n.z = sqrt(1.0 - (n.x * n.x) - (n.y * n.y));\r
-      n.y = -n.y;\r
+      n = texture2D(m_NormalMap, texCoord * m_DiffuseMap_0_scale).xyz;\r
       normal += n * alphaBlend.r;\r
+    #else\r
+      normal += vec3(0.5,0.5,1) * alphaBlend.r;\r
     #endif\r
 \r
     #ifdef NORMALMAP_1\r
-      normalHeight = texture2D(m_NormalMap_1, texCoord * m_DiffuseMap_1_scale);\r
-      n = (normalHeight.xyz * vec3(2.0) - vec3(1.0));\r
-      n.z = sqrt(1.0 - (n.x * n.x) - (n.y * n.y));\r
-      n.y = -n.y;\r
+      n = texture2D(m_NormalMap_1, texCoord * m_DiffuseMap_1_scale).xyz;\r
       normal += n * alphaBlend.g;\r
+    #else\r
+      normal += vec3(0.5,0.5,1) * alphaBlend.g;\r
     #endif\r
 \r
     #ifdef NORMALMAP_2\r
-      normalHeight = texture2D(m_NormalMap_2, texCoord * m_DiffuseMap_2_scale);\r
-      n = (normalHeight.xyz * vec3(2.0) - vec3(1.0));\r
-      n.z = sqrt(1.0 - (n.x * n.x) - (n.y * n.y));\r
-      n.y = -n.y;\r
+      n = texture2D(m_NormalMap_2, texCoord * m_DiffuseMap_2_scale).xyz;\r
       normal += n * alphaBlend.b;\r
+    #else\r
+      normal += vec3(0.5,0.5,1) * alphaBlend.b;\r
     #endif\r
 \r
     #ifdef NORMALMAP_3\r
-      normalHeight = texture2D(m_NormalMap_3, texCoord * m_DiffuseMap_3_scale);\r
-      n = (normalHeight.xyz * vec3(2.0) - vec3(1.0));\r
-      n.z = sqrt(1.0 - (n.x * n.x) - (n.y * n.y));\r
-      n.y = -n.y;\r
+      n = texture2D(m_NormalMap_3, texCoord * m_DiffuseMap_3_scale).xyz;\r
       normal += n * alphaBlend.a;\r
+    #else\r
+      normal += vec3(0.5,0.5,1) * alphaBlend.a;\r
     #endif\r
 \r
     #ifdef ALPHAMAP_1\r
         #ifdef NORMALMAP_4\r
-          normalHeight = texture2D(m_NormalMap_4, texCoord * m_DiffuseMap_4_scale);\r
-          n = (normalHeight.xyz * vec3(2.0) - vec3(1.0));\r
-          n.z = sqrt(1.0 - (n.x * n.x) - (n.y * n.y));\r
-          n.y = -n.y;\r
+          n = texture2D(m_NormalMap_4, texCoord * m_DiffuseMap_4_scale).xyz;\r
           normal += n * alphaBlend1.r;\r
         #endif\r
 \r
         #ifdef NORMALMAP_5\r
-          normalHeight = texture2D(m_NormalMap_5, texCoord * m_DiffuseMap_5_scale);\r
-          n = (normalHeight.xyz * vec3(2.0) - vec3(1.0));\r
-          n.z = sqrt(1.0 - (n.x * n.x) - (n.y * n.y));\r
-          n.y = -n.y;\r
+          n = texture2D(m_NormalMap_5, texCoord * m_DiffuseMap_5_scale).xyz;\r
           normal += n * alphaBlend1.g;\r
         #endif\r
 \r
         #ifdef NORMALMAP_6\r
-          normalHeight = texture2D(m_NormalMap_6, texCoord * m_DiffuseMap_6_scale);\r
-          n = (normalHeight.xyz * vec3(2.0) - vec3(1.0));\r
-          n.z = sqrt(1.0 - (n.x * n.x) - (n.y * n.y));\r
-          n.y = -n.y;\r
+          n = texture2D(m_NormalMap_6, texCoord * m_DiffuseMap_6_scale).xyz;\r
           normal += n * alphaBlend1.b;\r
         #endif\r
 \r
         #ifdef NORMALMAP_7\r
-          normalHeight = texture2D(m_NormalMap_7, texCoord * m_DiffuseMap_7_scale);\r
-          n = (normalHeight.xyz * vec3(2.0) - vec3(1.0));\r
-          n.z = sqrt(1.0 - (n.x * n.x) - (n.y * n.y));\r
-          n.y = -n.y;\r
+          n = texture2D(m_NormalMap_7, texCoord * m_DiffuseMap_7_scale).xyz;\r
           normal += n * alphaBlend1.a;\r
         #endif\r
     #endif\r
 \r
     #ifdef ALPHAMAP_2\r
         #ifdef NORMALMAP_8\r
-          normalHeight = texture2D(m_NormalMap_8, texCoord * m_DiffuseMap_8_scale);\r
-          n = (normalHeight.xyz * vec3(2.0) - vec3(1.0));\r
-          n.z = sqrt(1.0 - (n.x * n.x) - (n.y * n.y));\r
-          n.y = -n.y;\r
+          n = texture2D(m_NormalMap_8, texCoord * m_DiffuseMap_8_scale).xyz;\r
           normal += n * alphaBlend2.r;\r
         #endif\r
 \r
         #ifdef NORMALMAP_9\r
-          normalHeight = texture2D(m_NormalMap_9, texCoord * m_DiffuseMap_9_scale);\r
-          n = (normalHeight.xyz * vec3(2.0) - vec3(1.0));\r
-          n.z = sqrt(1.0 - (n.x * n.x) - (n.y * n.y));\r
-          n.y = -n.y;\r
+          n = texture2D(m_NormalMap_9, texCoord * m_DiffuseMap_9_scale);\r
           normal += n * alphaBlend2.g;\r
         #endif\r
 \r
         #ifdef NORMALMAP_10\r
-          normalHeight = texture2D(m_NormalMap_10, texCoord * m_DiffuseMap_10_scale);\r
-          n = (normalHeight.xyz * vec3(2.0) - vec3(1.0));\r
-          n.z = sqrt(1.0 - (n.x * n.x) - (n.y * n.y));\r
-          n.y = -n.y;\r
+          n = texture2D(m_NormalMap_10, texCoord * m_DiffuseMap_10_scale);\r
           normal += n * alphaBlend2.b;\r
         #endif\r
 \r
         #ifdef NORMALMAP_11\r
-          normalHeight = texture2D(m_NormalMap_11, texCoord * m_DiffuseMap_11_scale);\r
-          n = (normalHeight.xyz * vec3(2.0) - vec3(1.0));\r
-          n.z = sqrt(1.0 - (n.x * n.x) - (n.y * n.y));\r
-          n.y = -n.y;\r
+          n = texture2D(m_NormalMap_11, texCoord * m_DiffuseMap_11_scale);\r
           normal += n * alphaBlend2.a;\r
         #endif\r
     #endif\r
 \r
+    normal = (normal.xyz * vec3(2.0) - vec3(1.0));\r
     return normalize(normal);\r
   }\r
 \r
@@ -508,117 +480,105 @@ vec2 computeLighting(in vec3 wvPos, in vec3 wvNorm, in vec3 wvViewDir, in vec3 w
       vec4 coords = wVert;\r
       vec4 alphaBlend = texture2D( m_AlphaMap, texCoord.xy );\r
 \r
-    #ifdef ALPHAMAP_1\r
-      vec4 alphaBlend1 = texture2D( m_AlphaMap_1, texCoord.xy );\r
-    #endif\r
-    #ifdef ALPHAMAP_2\r
-      vec4 alphaBlend2 = texture2D( m_AlphaMap_2, texCoord.xy );\r
-    #endif\r
+      #ifdef ALPHAMAP_1\r
+        vec4 alphaBlend1 = texture2D( m_AlphaMap_1, texCoord.xy );\r
+      #endif\r
+      #ifdef ALPHAMAP_2\r
+        vec4 alphaBlend2 = texture2D( m_AlphaMap_2, texCoord.xy );\r
+      #endif\r
 \r
       vec3 normal = vec3(0,0,1);\r
       vec3 n = vec3(0,0,0);\r
-      vec4 normalHeight = vec4(0,0,0,0);\r
 \r
       #ifdef NORMALMAP\r
-          normalHeight = getTriPlanarBlend(coords, blending, m_NormalMap, m_DiffuseMap_0_scale);\r
-          n = (normalHeight.xyz * vec3(2.0) - vec3(1.0));\r
-          n.z = sqrt(1.0 - (n.x * n.x) - (n.y * n.y));\r
-          n.y = -n.y;\r
+          n = getTriPlanarBlend(coords, blending, m_NormalMap, m_DiffuseMap_0_scale).xyz;\r
           normal += n * alphaBlend.r;\r
+      #else\r
+          normal += vec3(0.5,0.5,1) * alphaBlend.r;\r
       #endif\r
 \r
       #ifdef NORMALMAP_1\r
-          normalHeight = getTriPlanarBlend(coords, blending, m_NormalMap_1, m_DiffuseMap_1_scale);\r
-          n = (normalHeight.xyz * vec3(2.0) - vec3(1.0));\r
-          n.z = sqrt(1.0 - (n.x * n.x) - (n.y * n.y));\r
-          n.y = -n.y;\r
+          n = getTriPlanarBlend(coords, blending, m_NormalMap_1, m_DiffuseMap_1_scale).xyz;\r
           normal += n * alphaBlend.g;\r
+      #else\r
+          normal += vec3(0.5,0.5,1) * alphaBlend.g;\r
       #endif\r
 \r
       #ifdef NORMALMAP_2\r
-          normalHeight = getTriPlanarBlend(coords, blending, m_NormalMap_2, m_DiffuseMap_2_scale);\r
-          n = (normalHeight.xyz * vec3(2.0) - vec3(1.0));\r
-          n.z = sqrt(1.0 - (n.x * n.x) - (n.y * n.y));\r
-          n.y = -n.y;\r
+          n = getTriPlanarBlend(coords, blending, m_NormalMap_2, m_DiffuseMap_2_scale).xyz;\r
           normal += n * alphaBlend.b;\r
+      #else\r
+          normal += vec3(0.5,0.5,1) * alphaBlend.b;\r
       #endif\r
 \r
       #ifdef NORMALMAP_3\r
-          normalHeight = getTriPlanarBlend(coords, blending, m_NormalMap_3, m_DiffuseMap_3_scale);\r
-          n = (normalHeight.xyz * vec3(2.0) - vec3(1.0));\r
-          n.z = sqrt(1.0 - (n.x * n.x) - (n.y * n.y));\r
-          n.y = -n.y;\r
+          n = getTriPlanarBlend(coords, blending, m_NormalMap_3, m_DiffuseMap_3_scale).xyz;\r
           normal += n * alphaBlend.a;\r
+      #else\r
+          normal += vec3(0.5,0.5,1) * alphaBlend.a;\r
       #endif\r
 \r
       #ifdef ALPHAMAP_1\r
           #ifdef NORMALMAP_4\r
-              normalHeight = getTriPlanarBlend(coords, blending, m_NormalMap_4, m_DiffuseMap_4_scale);\r
-              n = (normalHeight.xyz * vec3(2.0) - vec3(1.0));\r
-              n.z = sqrt(1.0 - (n.x * n.x) - (n.y * n.y));\r
-              n.y = -n.y;\r
+              n = getTriPlanarBlend(coords, blending, m_NormalMap_4, m_DiffuseMap_4_scale).xyz;\r
               normal += n * alphaBlend1.r;\r
+          #else\r
+              normal += vec3(0.5,0.5,1) * alphaBlend.r;\r
           #endif\r
 \r
           #ifdef NORMALMAP_5\r
-              normalHeight = getTriPlanarBlend(coords, blending, m_NormalMap_5, m_DiffuseMap_5_scale);\r
-              n = (normalHeight.xyz * vec3(2.0) - vec3(1.0));\r
-              n.z = sqrt(1.0 - (n.x * n.x) - (n.y * n.y));\r
-              n.y = -n.y;\r
+              n = getTriPlanarBlend(coords, blending, m_NormalMap_5, m_DiffuseMap_5_scale).xyz;\r
               normal += n * alphaBlend1.g;\r
+          #else\r
+              normal += vec3(0.5,0.5,1) * alphaBlend.g;\r
           #endif\r
 \r
           #ifdef NORMALMAP_6\r
-              normalHeight = getTriPlanarBlend(coords, blending, m_NormalMap_6, m_DiffuseMap_6_scale);\r
-              n = (normalHeight.xyz * vec3(2.0) - vec3(1.0));\r
-              n.z = sqrt(1.0 - (n.x * n.x) - (n.y * n.y));\r
-              n.y = -n.y;\r
+              n = getTriPlanarBlend(coords, blending, m_NormalMap_6, m_DiffuseMap_6_scale).xyz;\r
               normal += n * alphaBlend1.b;\r
+          #else\r
+              normal += vec3(0.5,0.5,1) * alphaBlend.b;\r
           #endif\r
 \r
           #ifdef NORMALMAP_7\r
-              normalHeight = getTriPlanarBlend(coords, blending, m_NormalMap_7, m_DiffuseMap_7_scale);\r
-              n = (normalHeight.xyz * vec3(2.0) - vec3(1.0));\r
-              n.z = sqrt(1.0 - (n.x * n.x) - (n.y * n.y));\r
-              n.y = -n.y;\r
+              n = getTriPlanarBlend(coords, blending, m_NormalMap_7, m_DiffuseMap_7_scale).xyz;\r
               normal += n * alphaBlend1.a;\r
+          #else\r
+              normal += vec3(0.5,0.5,1) * alphaBlend.a;\r
           #endif\r
       #endif\r
 \r
       #ifdef ALPHAMAP_2\r
           #ifdef NORMALMAP_8\r
-              normalHeight = getTriPlanarBlend(coords, blending, m_NormalMap_8, m_DiffuseMap_8_scale);\r
-              n = (normalHeight.xyz * vec3(2.0) - vec3(1.0));\r
-              n.z = sqrt(1.0 - (n.x * n.x) - (n.y * n.y));\r
-              n.y = -n.y;\r
+              n = getTriPlanarBlend(coords, blending, m_NormalMap_8, m_DiffuseMap_8_scale).xyz;\r
               normal += n * alphaBlend2.r;\r
+          #else\r
+              normal += vec3(0.5,0.5,1) * alphaBlend.r;\r
           #endif\r
 \r
           #ifdef NORMALMAP_9\r
-              normalHeight = getTriPlanarBlend(coords, blending, m_NormalMap_9, m_DiffuseMap_9_scale);\r
-              n = (normalHeight.xyz * vec3(2.0) - vec3(1.0));\r
-              n.z = sqrt(1.0 - (n.x * n.x) - (n.y * n.y));\r
-              n.y = -n.y;\r
+              n = getTriPlanarBlend(coords, blending, m_NormalMap_9, m_DiffuseMap_9_scale).xyz;\r
               normal += n * alphaBlend2.g;\r
+          #else\r
+              normal += vec3(0.5,0.5,1) * alphaBlend.g;\r
           #endif\r
 \r
           #ifdef NORMALMAP_10\r
-              normalHeight = getTriPlanarBlend(coords, blending, m_NormalMap_10, m_DiffuseMap_10_scale);\r
-              n = (normalHeight.xyz * vec3(2.0) - vec3(1.0));\r
-              n.z = sqrt(1.0 - (n.x * n.x) - (n.y * n.y));\r
-              n.y = -n.y;\r
+              n = getTriPlanarBlend(coords, blending, m_NormalMap_10, m_DiffuseMap_10_scale).xyz;\r
               normal += n * alphaBlend2.b;\r
+          #else\r
+              normal += vec3(0.5,0.5,1) * alphaBlend.b;\r
           #endif\r
 \r
           #ifdef NORMALMAP_11\r
-              normalHeight = getTriPlanarBlend(coords, blending, m_NormalMap_11, m_DiffuseMap_11_scale);\r
-              n = (normalHeight.xyz * vec3(2.0) - vec3(1.0));\r
-              n.z = sqrt(1.0 - (n.x * n.x) - (n.y * n.y));\r
-              n.y = -n.y;\r
+              n = getTriPlanarBlend(coords, blending, m_NormalMap_11, m_DiffuseMap_11_scale).xyz;\r
               normal += n * alphaBlend2.a;\r
+          #else\r
+              normal += vec3(0.5,0.5,1) * alphaBlend.a;\r
           #endif\r
       #endif\r
 \r
+      normal = (normal.xyz * vec3(2.0) - vec3(1.0));\r
       return normalize(normal);\r
     }\r
   #endif\r
index 0c48999..bfdbf5b 100644 (file)
@@ -22,7 +22,7 @@ MaterialDef Terrain Lighting {
         Color Specular\r
 \r
         // Specular power/shininess\r
-        Float Shininess : 1\r
+        Float Shininess : 0\r
 \r
         // Texture map #0\r
         Texture2D DiffuseMap\r
@@ -232,13 +232,13 @@ MaterialDef Terrain Lighting {
         }\r
     }\r
 \r
-    Technique FixedFunc {\r
+    Technique {\r
         LightMode FixedPipeline\r
     }\r
 \r
     Technique Glow {\r
 \r
-        VertexShader GLSL100:   Common/MatDefs/Misc/SimpleTextured.vert\r
+        VertexShader GLSL100:   Common/MatDefs/Misc/Unshaded.vert\r
         FragmentShader GLSL100: Common/MatDefs/Light/Glow.frag\r
 \r
         WorldParameters {\r
index 241488d..75a5381 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2010 jMonkeyEngine
+ * Copyright (c) 2009-2012 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  */
 package com.jme3.terrain;
 
-import com.jme3.export.InputCapsule;
-import com.jme3.export.JmeExporter;
-import com.jme3.export.JmeImporter;
-import com.jme3.export.OutputCapsule;
-import com.jme3.export.Savable;
+import com.jme3.export.*;
 import com.jme3.math.Vector2f;
 import com.jme3.math.Vector3f;
 import com.jme3.scene.Mesh;
-import com.jme3.scene.VertexBuffer;
 import com.jme3.scene.VertexBuffer.Type;
 import com.jme3.util.BufferUtils;
 import java.io.IOException;
 import java.nio.BufferUnderflowException;
-import java.nio.ByteBuffer;
 import java.nio.FloatBuffer;
 import java.nio.IntBuffer;
 
@@ -53,36 +47,38 @@ import java.nio.IntBuffer;
  */
 public class GeoMap implements Savable {
     
-    protected FloatBuffer hdata;
-    protected ByteBuffer ndata;
+    protected float[] hdata;
     protected int width, height, maxval;
     
     public GeoMap() {}
     
-    public GeoMap(FloatBuffer heightData, ByteBuffer normalData, int width, int height, int maxval){
-        this.hdata = heightData;
-        this.ndata = normalData;
+    @Deprecated
+    public GeoMap(FloatBuffer heightData, int width, int height, int maxval){
+        hdata = new float[heightData.limit()];
+        heightData.get(hdata);
         this.width = width;
         this.height = height;
         this.maxval = maxval;
     }
-
-    public GeoMap(int width, int height, int maxval) {
-        this(BufferUtils.createFloatBuffer(width*height), null, width,height,maxval);
+    
+    public GeoMap(float[] heightData, int width, int height, int maxval){
+        this.hdata = heightData;
+        this.width = width;
+        this.height = height;
+        this.maxval = maxval;
     }
 
+    @Deprecated
     public FloatBuffer getHeightData(){
         if (!isLoaded())
             return null;
-
-        return hdata;
+        return BufferUtils.createFloatBuffer(hdata);
     }
-
-    public ByteBuffer getNormalData(){
-        if (!isLoaded() || !hasNormalmap())
+    
+    public float[] getHeightArray(){
+        if (!isLoaded())
             return null;
-
-        return ndata;
+        return hdata;
     }
 
     /**
@@ -105,7 +101,7 @@ public class GeoMap implements Savable {
      * @throws NullPointerException If isLoaded() is false
      */
     public float getValue(int x, int y) {
-        return hdata.get(y*width+x);
+        return hdata[y*width+x];
     }
 
     /**
@@ -120,50 +116,9 @@ public class GeoMap implements Savable {
      * @throws NullPointerException If isLoaded() is false
      */
     public float getValue(int i) {
-        return hdata.get(i);
+        return hdata[i];
     }
 
-    /**
-     * Returns the normal at a point
-     *
-     * If store is null, then a new vector is returned,
-     * otherwise, the result is stored in the provided vector
-     * and then returned from this method
-     *
-     * @param x the X coordinate
-     * @param y the Y coordinate
-     * @param store A preallocated vector for storing the normal data, optional
-     * @returns store, or a new vector with the normal data if store is null
-     *
-     * @throws NullPointerException If isLoaded() or hasNormalmap() is false
-     */
-    public Vector3f getNormal(int x, int y, Vector3f store) {
-        return getNormal(y*width+x,store);
-    }
-
-    /**
-     * Returns the normal at an index
-     *
-     * If store is null, then a new vector is returned,
-     * otherwise, the result is stored in the provided vector
-     * and then returned from this method
-     *
-     * See getHeight(int) for information about index lookup
-     *
-     * @param i the index
-     * @param store A preallocated vector for storing the normal data, optional
-     * @returns store, or a new vector with the normal data if store is null
-     *
-     * @throws NullPointerException If isLoaded() or hasNormalmap() is false
-     */
-    public Vector3f getNormal(int i, Vector3f store) {
-        ndata.position( i*3 );
-        if (store==null) store = new Vector3f();
-        store.setX( (((float)(ndata.get() & 0xFF)/255f)-0.5f)*2f );
-        store.setY( (((float)(ndata.get() & 0xFF)/255f)-0.5f)*2f );
-        store.setZ( (((float)(ndata.get() & 0xFF)/255f)-0.5f)*2f );
-        return store;
-    }
 
     /**
      * Returns the width of this Geomap
@@ -184,43 +139,6 @@ public class GeoMap implements Savable {
     }
 
     /**
-     * Copies a section of this geomap as a new geomap
-     */
-    public GeoMap copySubGeomap(int x, int y, int w, int h){
-        FloatBuffer nhdata = BufferUtils.createFloatBuffer(w * h);
-        hdata.position(y*width+x);
-        for (int cy = 0; cy < height; cy++){
-            hdata.limit(hdata.position()+w);
-            nhdata.put(hdata);
-            hdata.limit(hdata.capacity());
-            hdata.position(hdata.position()+width);
-        }
-        nhdata.flip();
-
-        ByteBuffer nndata = null;
-        if (ndata!=null){
-            nndata = BufferUtils.createByteBuffer(w*h*3);
-            ndata.position( (y*width+x)*3 );
-            for (int cy = 0; cy < height; cy++){
-                ndata.limit(ndata.position()+w*3);
-                nndata.put(ndata);
-                ndata.limit(ndata.capacity());
-                ndata.position(ndata.position()+width*3);
-            }
-            nndata.flip();
-        }
-
-        return new GeoMap(nhdata,nndata,w,h,maxval);
-    }
-
-    /**
-     * Returns true if this Geomap has a normalmap associated with it
-     */
-    public boolean hasNormalmap() {
-        return ndata != null;
-    }
-
-    /**
      * Returns true if the Geomap data is loaded in memory
      * If false, then the data is unavailable- must be loaded with load()
      * before the methods getHeight/getNormal can be used
@@ -240,8 +158,6 @@ public class GeoMap implements Savable {
      * @throws NullPointerException If isLoaded() or hasNormalmap() is false
      */
     public FloatBuffer writeNormalArray(FloatBuffer store, Vector3f scale) {
-        if (!isLoaded())
-            throw new NullPointerException();
         
         if (store!=null){
             if (store.remaining() < getWidth()*getHeight()*3)
@@ -250,65 +166,55 @@ public class GeoMap implements Savable {
             store = BufferUtils.createFloatBuffer(getWidth()*getHeight()*3);
         }
         store.rewind();
-        
-        if (!hasNormalmap()){
-            Vector3f oppositePoint = new Vector3f();
-            Vector3f adjacentPoint = new Vector3f();
-            Vector3f rootPoint = new Vector3f();
-            Vector3f tempNorm = new Vector3f();
-            int normalIndex = 0;
-
-            for (int y = 0; y < getHeight(); y++) {
-                for (int x = 0; x < getWidth(); x++) {
-                    rootPoint.set(x, getValue(x,y), y);
-                    if (y == getHeight() - 1) {
-                        if (x == getWidth() - 1) {  // case #4 : last row, last col
-                            // left cross up
+
+        Vector3f oppositePoint = new Vector3f();
+        Vector3f adjacentPoint = new Vector3f();
+        Vector3f rootPoint = new Vector3f();
+        Vector3f tempNorm = new Vector3f();
+        int normalIndex = 0;
+
+        for (int y = 0; y < getHeight(); y++) {
+            for (int x = 0; x < getWidth(); x++) {
+                rootPoint.set(x, getValue(x,y), y);
+                if (y == getHeight() - 1) {
+                    if (x == getWidth() - 1) {  // case #4 : last row, last col
+                        // left cross up
 //                            adj = normalIndex - getWidth();
 //                            opp = normalIndex - 1;
-                            adjacentPoint.set(x, getValue(x,y-1), y-1);
-                            oppositePoint.set(x-1, getValue(x-1, y), y);
-                        } else {                    // case #3 : last row, except for last col
-                            // right cross up
+                        adjacentPoint.set(x, getValue(x,y-1), y-1);
+                        oppositePoint.set(x-1, getValue(x-1, y), y);
+                    } else {                    // case #3 : last row, except for last col
+                        // right cross up
 //                            adj = normalIndex + 1;
 //                            opp = normalIndex - getWidth();
-                            adjacentPoint.set(x+1, getValue(x+1,y), y);
-                            oppositePoint.set(x, getValue(x,y-1), y-1);
-                        }
-                    } else {
-                        if (x == getWidth() - 1) {  // case #2 : last column except for last row
-                            // left cross down
-                            adjacentPoint.set(x-1, getValue(x-1,y), y);
-                            oppositePoint.set(x, getValue(x,y+1), y+1);
+                        adjacentPoint.set(x+1, getValue(x+1,y), y);
+                        oppositePoint.set(x, getValue(x,y-1), y-1);
+                    }
+                } else {
+                    if (x == getWidth() - 1) {  // case #2 : last column except for last row
+                        // left cross down
+                        adjacentPoint.set(x-1, getValue(x-1,y), y);
+                        oppositePoint.set(x, getValue(x,y+1), y+1);
 //                            adj = normalIndex - 1;
 //                            opp = normalIndex + getWidth();
-                        } else {                    // case #1 : most cases
-                            // right cross down
-                            adjacentPoint.set(x, getValue(x,y+1), y+1);
-                            oppositePoint.set(x+1, getValue(x+1,y), y);
+                    } else {                    // case #1 : most cases
+                        // right cross down
+                        adjacentPoint.set(x, getValue(x,y+1), y+1);
+                        oppositePoint.set(x+1, getValue(x+1,y), y);
 //                            adj = normalIndex + getWidth();
 //                            opp = normalIndex + 1;
-                        }
                     }
+                }
 
 
 
-                    tempNorm.set(adjacentPoint).subtractLocal(rootPoint)
-                            .crossLocal(oppositePoint.subtractLocal(rootPoint));
-                    tempNorm.multLocal(scale).normalizeLocal();
+                tempNorm.set(adjacentPoint).subtractLocal(rootPoint)
+                        .crossLocal(oppositePoint.subtractLocal(rootPoint));
+                tempNorm.multLocal(scale).normalizeLocal();
 //                    store.put(tempNorm.x).put(tempNorm.y).put(tempNorm.z);
-                    BufferUtils.setInBuffer(tempNorm, store,
-                            normalIndex);
-                    normalIndex++;
-                }
-            }
-        }else{
-            Vector3f temp = new Vector3f();
-            for (int z = 0; z < getHeight(); z++){
-                for (int x = 0; x < getWidth(); x++){
-                    getNormal(x,z,temp);
-                    store.put(temp.x).put(temp.y).put(temp.z);
-                }
+                BufferUtils.setInBuffer(tempNorm, store,
+                        normalIndex);
+                normalIndex++;
             }
         }
 
@@ -329,7 +235,6 @@ public class GeoMap implements Savable {
      * @throws NullPointerException If isLoaded() is false
      */
     public FloatBuffer writeVertexArray(FloatBuffer store, Vector3f scale, boolean center) {
-        if (!isLoaded()) throw new NullPointerException();
 
         if (store!=null){
             if (store.remaining() < width*height*3)
@@ -337,9 +242,8 @@ public class GeoMap implements Savable {
         }else{
             store = BufferUtils.createFloatBuffer(width*height*3);
         }
-        hdata.rewind();
 
-        assert hdata.limit() == height*width;
+        assert hdata.length == height*width;
 
         Vector3f offset = new Vector3f(-getWidth() * scale.x * 0.5f,
                                        0,
@@ -347,10 +251,11 @@ public class GeoMap implements Savable {
         if (!center)
             offset.zero();
 
+        int i = 0;
         for (int z = 0; z < height; z++){
             for (int x = 0; x < width; x++){
                 store.put( (float)x*scale.x + offset.x );
-                store.put( (float)hdata.get()*scale.y );
+                store.put( (float)hdata[i++]*scale.y );
                 store.put( (float)z*scale.z + offset.z );
             }
         }
@@ -433,27 +338,9 @@ public class GeoMap implements Savable {
         return m;
     }
     
-    /**
-     * Populate the height data from the supplied mesh.
-     * The mesh's dimensions should be the same as width and height
-     * of this geomap
-     */
-    public void populateHdataFromMesh(Mesh mesh) {
-        hdata = BufferUtils.createFloatBuffer(width*height);
-        hdata.rewind();
-        VertexBuffer pb = mesh.getBuffer(Type.Position);
-        FloatBuffer fb = (FloatBuffer) pb.getData();
-        for (int r=0; r<height; r++) {
-            for (int c=0; c<width; c++) {
-                float f = fb.get( (width*r) + c + 1);
-                hdata.put( f );
-            }
-        }
-    }
-
     public void write(JmeExporter ex) throws IOException {
         OutputCapsule oc = ex.getCapsule(this);
-        oc.write(hdata, "hdata", null);
+        oc.write(hdata, "hdataarray", null);
         oc.write(width, "width", 0);
         oc.write(height, "height", 0);
         oc.write(maxval, "maxval", 0);
@@ -461,7 +348,14 @@ public class GeoMap implements Savable {
 
     public void read(JmeImporter im) throws IOException {
         InputCapsule ic = im.getCapsule(this);
-        hdata = ic.readFloatBuffer("hdata", null);
+        hdata = ic.readFloatArray("hdataarray", null);
+        if (hdata == null) {
+            FloatBuffer buf = ic.readFloatBuffer("hdata", null);
+            if (buf != null) {
+                hdata = new float[buf.limit()];
+                buf.get(hdata);
+            }
+        }
         width = ic.readInt("width", 0);
         height = ic.readInt("height", 0);
         maxval = ic.readInt("maxval", 0);
diff --git a/engine/src/terrain/com/jme3/terrain/MapUtils.java b/engine/src/terrain/com/jme3/terrain/MapUtils.java
deleted file mode 100644 (file)
index 676f83d..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-package com.jme3.terrain;\r
-\r
-import java.awt.image.BufferedImage;\r
-import java.io.File;\r
-import java.io.IOException;\r
-import java.nio.FloatBuffer;\r
-import java.util.logging.Level;\r
-import java.util.logging.Logger;\r
-\r
-import javax.imageio.ImageIO;\r
-\r
-import org.novyon.noise.ShaderUtils;\r
-\r
-public class MapUtils {\r
-\r
-    public static FloatBuffer clip(FloatBuffer src, int origSize, int newSize, int offset) {\r
-        FloatBuffer result = FloatBuffer.allocate(newSize * newSize);\r
-\r
-        float[] orig = src.array();\r
-        for (int i = offset; i < offset + newSize; i++) {\r
-            result.put(orig, i * origSize + offset, newSize);\r
-        }\r
-\r
-        return result;\r
-    }\r
-\r
-    public static BufferedImage toGrayscale16Image(FloatBuffer buff, int size) {\r
-        BufferedImage retval = new BufferedImage(size, size, BufferedImage.TYPE_USHORT_GRAY);\r
-        buff.rewind();\r
-        for (int y = 0; y < size; y++) {\r
-            for (int x = 0; x < size; x++) {\r
-                short c = (short) (ShaderUtils.clamp(buff.get(), 0, 1) * 65532);\r
-                retval.getRaster().setDataElements(x, y, new short[]{c});\r
-            }\r
-        }\r
-        return retval;\r
-    }\r
-\r
-    public static BufferedImage toGrayscaleRGBImage(FloatBuffer buff, int size) {\r
-        BufferedImage retval = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);\r
-        buff.rewind();\r
-        for (int y = 0; y < size; y++) {\r
-            for (int x = 0; x < size; x++) {\r
-                int c = (int) (ShaderUtils.clamp(buff.get(), 0, 1) * 255);\r
-                retval.setRGB(x, y, 0xFF000000 | c << 16 | c << 8 | c);\r
-            }\r
-        }\r
-        return retval;\r
-    }\r
-\r
-    public static void saveImage(BufferedImage im, String file) {\r
-        MapUtils.saveImage(im, new File(file));\r
-    }\r
-\r
-    public static void saveImage(BufferedImage im, File file) {\r
-        try {\r
-            ImageIO.write(im, "PNG", file);\r
-            Logger.getLogger(MapUtils.class.getCanonicalName()).log(Level.INFO, "Saved image as : {0}", file.getAbsolutePath());\r
-        } catch (IOException e) {\r
-            e.printStackTrace();\r
-        }\r
-    }\r
-}\r
index 4664231..2d42337 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2010 jMonkeyEngine
+ * Copyright (c) 2009-2012 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index 1fdbd78..d96bb13 100644 (file)
@@ -1,5 +1,5 @@
 /*\r
- * Copyright (c) 2009-2010 jMonkeyEngine\r
+ * Copyright (c) 2009-2012 jMonkeyEngine\r
  * All rights reserved.\r
  *\r
  * Redistribution and use in source and binary forms, with or without\r
@@ -34,7 +34,6 @@ package com.jme3.terrain;
 import com.jme3.material.Material;\r
 import com.jme3.math.Vector2f;\r
 import com.jme3.math.Vector3f;\r
-import com.jme3.terrain.geomipmap.lodcalc.LodCalculator;\r
 import java.util.List;\r
 \r
 /**\r
@@ -126,17 +125,6 @@ public interface Terrain {
     public int getMaxLod();\r
 \r
     /**\r
-     * Called by an LodControl.\r
-     * Calculates the level of detail of the terrain and adjusts its geometry.\r
-     * This is where the Terrain's LOD algorithm will change the detail of\r
-     * the terrain based on how far away this position is from the particular\r
-     * terrain patch.\r
-     * @param location: the Camera's location. A list of one camera location is normal \r
-     *  if you just have one camera in your scene.\r
-     */\r
-    public void update(List<Vector3f> location, LodCalculator lodCalculator);\r
-\r
-    /**\r
      * Lock or unlock the meshes of this terrain.\r
      * Locked meshes are un-editable but have better performance.\r
      * This should call the underlying getMesh().setStatic()/setDynamic() methods.\r
@@ -154,10 +142,26 @@ public interface Terrain {
 \r
     /**\r
      * Returns the material that this terrain uses.\r
-     * This does not necessarily have to guarantee the material\r
-     * return is the only material used in the whole terrain structure.\r
+     * If it uses many materials, just return the one you think is best.\r
+     * For TerrainQuads this is sufficient. For TerrainGrid you want to call\r
+     * getMaterial(Vector3f) instead.\r
      */\r
     public Material getMaterial();\r
+    \r
+    /**\r
+     * Returns the material that this terrain uses.\r
+     * Terrain can have different materials in different locations.\r
+     * In general, the TerrainQuad will only have one material. But \r
+     * TerrainGrid will have a different material per tile.\r
+     * \r
+     * It could be possible to pass in null for the location, some Terrain\r
+     * implementations might just have the one material and not care where\r
+     * you are looking. So implementations must handle null being supplied.\r
+     * \r
+     * @param worldLocation the location, in world coordinates, of where \r
+     * we are interested in the underlying texture.\r
+     */\r
+    public Material getMaterial(Vector3f worldLocation);\r
 \r
     /**\r
      * Used for painting to get the number of vertices along the edge of the\r
@@ -180,6 +184,14 @@ public interface Terrain {
      * 1/512 (or 0.00195) percent of the texture.\r
      * This is used for converting between tri-planar texture scales and regular\r
      * texture scales.\r
+     * \r
+     * not needed\r
+     */\r
+    //public float getTextureCoordinateScale();\r
+    \r
+    /**\r
+     * \r
+     * \r
      */\r
-    public float getTextureCoordinateScale();\r
+    public int getNumMajorSubdivisions();\r
 }\r
index 2322996..fec90d8 100644 (file)
@@ -1,5 +1,5 @@
 /*\r
- * Copyright (c) 2009-2010 jMonkeyEngine\r
+ * Copyright (c) 2009-2012 jMonkeyEngine\r
  * All rights reserved.\r
  *\r
  * Redistribution and use in source and binary forms, with or without\r
  */\r
 package com.jme3.terrain.geomipmap;\r
 \r
-import com.jme3.terrain.GeoMap;\r
 import com.jme3.export.JmeExporter;\r
 import com.jme3.export.JmeImporter;\r
 import com.jme3.math.FastMath;\r
 import com.jme3.math.Triangle;\r
-import java.nio.BufferOverflowException;\r
-import java.nio.BufferUnderflowException;\r
-import java.nio.FloatBuffer;\r
-import java.nio.IntBuffer;\r
-\r
 import com.jme3.math.Vector2f;\r
 import com.jme3.math.Vector3f;\r
 import com.jme3.scene.Mesh;\r
 import com.jme3.scene.Mesh.Mode;\r
-import com.jme3.scene.VertexBuffer;\r
 import com.jme3.scene.VertexBuffer.Type;\r
+import com.jme3.scene.mesh.IndexBuffer;\r
+import com.jme3.terrain.GeoMap;\r
 import com.jme3.util.BufferUtils;\r
-import com.jme3.util.TangentBinormalGenerator;\r
+import com.jme3.util.TempVars;\r
 import java.io.IOException;\r
+import java.nio.Buffer;\r
+import java.nio.BufferOverflowException;\r
+import java.nio.BufferUnderflowException;\r
+import java.nio.FloatBuffer;\r
+import java.nio.IntBuffer;\r
+import java.nio.ShortBuffer;\r
 \r
 /**\r
  * Produces the mesh for the TerrainPatch.\r
@@ -69,8 +70,13 @@ public class LODGeomap extends GeoMap {
     public LODGeomap() {\r
     }\r
 \r
+    @Deprecated\r
     public LODGeomap(int size, FloatBuffer heightMap) {\r
-        super(heightMap, null, size, size, 1);\r
+        super(heightMap, size, size, 1);\r
+    }\r
+    \r
+    public LODGeomap(int size, float[] heightMap) {\r
+        super(heightMap, size, size, 1);\r
     }\r
 \r
     public Mesh createMesh(Vector3f scale, Vector2f tcScale, Vector2f tcOffset, float offsetAmount, int totalSize, boolean center) {\r
@@ -81,10 +87,15 @@ public class LODGeomap extends GeoMap {
         FloatBuffer pb = writeVertexArray(null, scale, center);\r
         FloatBuffer texb = writeTexCoordArray(null, tcOffset, tcScale, offsetAmount, totalSize);\r
         FloatBuffer nb = writeNormalArray(null, scale);\r
-        IntBuffer ib = writeIndexArrayLodDiff(null, lod, rightLod, topLod, leftLod, bottomLod);\r
+        Buffer ib;\r
+        IndexBuffer idxB = writeIndexArrayLodDiff(lod, rightLod, topLod, leftLod, bottomLod, totalSize);\r
+        if (idxB.getBuffer() instanceof IntBuffer)\r
+            ib = (IntBuffer)idxB.getBuffer();\r
+        else\r
+            ib = (ShortBuffer)idxB.getBuffer();\r
         FloatBuffer bb = BufferUtils.createFloatBuffer(getWidth() * getHeight() * 3);\r
         FloatBuffer tanb = BufferUtils.createFloatBuffer(getWidth() * getHeight() * 3);\r
-        writeTangentArray(tanb, bb, texb, scale);\r
+        writeTangentArray(nb, tanb, bb, texb, scale);\r
         Mesh m = new Mesh();\r
         m.setMode(Mode.TriangleStrip);\r
         m.setBuffer(Type.Position, 3, pb);\r
@@ -92,16 +103,15 @@ public class LODGeomap extends GeoMap {
         m.setBuffer(Type.Tangent, 3, tanb);\r
         m.setBuffer(Type.Binormal, 3, bb);\r
         m.setBuffer(Type.TexCoord, 2, texb);\r
-        m.setBuffer(Type.Index, 3, ib);\r
+        if (ib instanceof IntBuffer)\r
+            m.setBuffer(Type.Index, 3, (IntBuffer)ib);\r
+        else if (ib instanceof ShortBuffer)\r
+            m.setBuffer(Type.Index, 3, (ShortBuffer)ib);\r
         m.setStatic();\r
         m.updateBound();\r
         return m;\r
     }\r
 \r
-    protected void removeNormalBuffer() {\r
-        ndata = null;\r
-    }\r
-\r
     public FloatBuffer writeTexCoordArray(FloatBuffer store, Vector2f offset, Vector2f scale, float offsetAmount, int totalSize) {\r
         if (store != null) {\r
             if (store.remaining() < getWidth() * getHeight() * 2) {\r
@@ -152,14 +162,13 @@ public class LODGeomap extends GeoMap {
      * @param bottomLod LOD of the bottom neighbour\r
      * @return the LOD-ified index buffer\r
      */\r
-    public IntBuffer writeIndexArrayLodDiff(IntBuffer store, int lod, boolean rightLod, boolean topLod, boolean leftLod, boolean bottomLod) {\r
+    public IndexBuffer writeIndexArrayLodDiff(int lod, boolean rightLod, boolean topLod, boolean leftLod, boolean bottomLod, int totalSize) {\r
 \r
-        IntBuffer buffer2 = store;\r
+        \r
         int numIndexes = calculateNumIndexesLodDiff(lod);\r
-        if (store == null) {\r
-            buffer2 = BufferUtils.createIntBuffer(numIndexes);\r
-        }\r
-        VerboseIntBuffer buffer = new VerboseIntBuffer(buffer2);\r
+        \r
+        IndexBuffer ib = IndexBuffer.createIndexBuffer(numIndexes, numIndexes);\r
+        VerboseBuffer buffer = new VerboseBuffer(ib);\r
 \r
 \r
         // generate center squares minus the edges\r
@@ -275,7 +284,7 @@ public class LODGeomap extends GeoMap {
                 buffer.put(idx);\r
                 idx = (row + 2 * lod) * getWidth();\r
                 buffer.put(idx);\r
-                if (row < getWidth() - lod - 2 - 1) { //if not the last one\r
+                if (row < getWidth() - 1 - 2 * lod) { //if not the last one\r
                     idx = (row + 2 * lod) * getWidth() + lod;\r
                     buffer.put(idx);\r
                     idx = (row + 2 * lod) * getWidth();\r
@@ -355,14 +364,12 @@ public class LODGeomap extends GeoMap {
         return buffer.delegate;\r
     }\r
 \r
-    public IntBuffer writeIndexArrayLodVariable(IntBuffer store, int lod, int rightLod, int topLod, int leftLod, int bottomLod) {\r
+    public IndexBuffer writeIndexArrayLodVariable(int lod, int rightLod, int topLod, int leftLod, int bottomLod, int totalSize) {\r
 \r
-        IntBuffer buffer2 = store;\r
         int numIndexes = calculateNumIndexesLodDiff(lod);\r
-        if (store == null) {\r
-            buffer2 = BufferUtils.createIntBuffer(numIndexes);\r
-        }\r
-        VerboseIntBuffer buffer = new VerboseIntBuffer(buffer2);\r
+        \r
+        IndexBuffer ib = IndexBuffer.createIndexBuffer(numIndexes, numIndexes);\r
+        VerboseBuffer buffer = new VerboseBuffer(ib);\r
 \r
 \r
         // generate center squares minus the edges\r
@@ -628,7 +635,7 @@ public class LODGeomap extends GeoMap {
         return num;\r
     }\r
 \r
-    public FloatBuffer[] writeTangentArray(FloatBuffer tangentStore, FloatBuffer binormalStore, FloatBuffer textureBuffer, Vector3f scale) {\r
+    public FloatBuffer[] writeTangentArray(FloatBuffer normalBuffer, FloatBuffer tangentStore, FloatBuffer binormalStore, FloatBuffer textureBuffer, Vector3f scale) {\r
         if (!isLoaded()) {\r
             throw new NullPointerException();\r
         }\r
@@ -651,38 +658,60 @@ public class LODGeomap extends GeoMap {
         }\r
         binormalStore.rewind();\r
 \r
+        Vector3f normal = new Vector3f();\r
         Vector3f tangent = new Vector3f();\r
         Vector3f binormal = new Vector3f();\r
-        Vector3f v1 = new Vector3f();\r
+        /*Vector3f v1 = new Vector3f();\r
         Vector3f v2 = new Vector3f();\r
         Vector3f v3 = new Vector3f();\r
         Vector2f t1 = new Vector2f();\r
         Vector2f t2 = new Vector2f();\r
-        Vector2f t3 = new Vector2f();\r
-\r
-        scale = Vector3f.UNIT_XYZ;\r
+        Vector2f t3 = new Vector2f();*/\r
 \r
         for (int r = 0; r < getHeight(); r++) {\r
             for (int c = 0; c < getWidth(); c++) {\r
+                \r
+                int idx = (r * getWidth() + c) * 3;\r
+                normal.set(normalBuffer.get(idx), normalBuffer.get(idx+1), normalBuffer.get(idx+2));\r
+                tangent.set(normal.cross(new Vector3f(0,0,1)));\r
+                binormal.set(new Vector3f(1,0,0).cross(normal));\r
+                \r
+                BufferUtils.setInBuffer(tangent.normalizeLocal(), tangentStore, (r * getWidth() + c)); // save the tangent\r
+                BufferUtils.setInBuffer(binormal.normalizeLocal(), binormalStore, (r * getWidth() + c)); // save the binormal\r
+            }\r
+        }\r
+\r
+/*        for (int r = 0; r < getHeight(); r++) {\r
+            for (int c = 0; c < getWidth(); c++) {\r
 \r
                 int texIdx = ((getHeight() - 1 - r) * getWidth() + c) * 2; // pull from the end\r
-                int texIdxPrev = ((getHeight() - 1 - (r - 1)) * getWidth() + c) * 2; // pull from the end\r
+                int texIdxAbove = ((getHeight() - 1 - (r - 1)) * getWidth() + c) * 2; // pull from the end\r
                 int texIdxNext = ((getHeight() - 1 - (r + 1)) * getWidth() + c) * 2; // pull from the end\r
 \r
                 v1.set(c, getValue(c, r), r);\r
                 t1.set(textureBuffer.get(texIdx), textureBuffer.get(texIdx + 1));\r
 \r
-                if (r == 0) { // first row\r
-                    v3.set(c, getValue(c, r), r); // ???\r
-                    t3.set(textureBuffer.get(texIdxNext), textureBuffer.get(texIdxNext + 1)); // ???\r
+                // below\r
+                if (r == getHeight()-1) { // last row\r
+                    v3.set(c, getValue(c, r), r + 1);\r
+                    float u = textureBuffer.get(texIdx) - textureBuffer.get(texIdxAbove);\r
+                    u += textureBuffer.get(texIdx);\r
+                    float v = textureBuffer.get(texIdx + 1) - textureBuffer.get(texIdxAbove + 1);\r
+                    v += textureBuffer.get(texIdx + 1);\r
+                    t3.set(u, v);\r
                 } else {\r
-                    v3.set(c, getValue(c, r - 1), r - 1);\r
-                    t3.set(textureBuffer.get(texIdxPrev), textureBuffer.get(texIdxPrev + 1));\r
+                    v3.set(c, getValue(c, r + 1), r + 1);\r
+                    t3.set(textureBuffer.get(texIdxNext), textureBuffer.get(texIdxNext + 1));\r
                 }\r
-\r
-                if (c == getWidth() - 1) { // last column\r
-                    v2.set(c + 1, getValue(c, r), r); // use same height\r
-                    t2.set(textureBuffer.get(texIdx), textureBuffer.get(texIdx + 1));\r
+                \r
+                //right\r
+                if (c == getWidth()-1) { // last column\r
+                    v2.set(c + 1, getValue(c, r), r);\r
+                    float u = textureBuffer.get(texIdx) - textureBuffer.get(texIdx - 2);\r
+                    u += textureBuffer.get(texIdx);\r
+                    float v = textureBuffer.get(texIdx + 1) - textureBuffer.get(texIdx - 1);\r
+                    v += textureBuffer.get(texIdx - 1);\r
+                    t2.set(u, v);\r
                 } else {\r
                     v2.set(c + 1, getValue(c + 1, r), r); // one to the right\r
                     t2.set(textureBuffer.get(texIdx + 2), textureBuffer.get(texIdx + 3));\r
@@ -693,14 +722,14 @@ public class LODGeomap extends GeoMap {
                 BufferUtils.setInBuffer(binormal, binormalStore, (r * getWidth() + c)); // save the binormal\r
             }\r
         }\r
-\r
+        */\r
         return new FloatBuffer[]{tangentStore, binormalStore};\r
     }\r
 \r
     /**\r
      * \r
-     * @param v Takes 3 vertexes: root, right, top\r
-     * @param t Takes 3 tex coords: root, right, top\r
+     * @param v Takes 3 vertices: root, right, bottom\r
+     * @param t Takes 3 tex coords: root, right, bottom\r
      * @param tangent that will store the result\r
      * @return the tangent store\r
      */\r
@@ -762,116 +791,169 @@ public class LODGeomap extends GeoMap {
         }\r
         store.rewind();\r
 \r
-        Vector3f rootPoint = new Vector3f();\r
-        Vector3f rightPoint = new Vector3f();\r
-        Vector3f leftPoint = new Vector3f();\r
-        Vector3f topPoint = new Vector3f();\r
-        Vector3f bottomPoint = new Vector3f();\r
+        TempVars vars = TempVars.get();\r
+        \r
+        Vector3f rootPoint = vars.vect1;\r
+        Vector3f rightPoint = vars.vect2;\r
+        Vector3f leftPoint = vars.vect3;\r
+        Vector3f topPoint = vars.vect4;\r
+        Vector3f bottomPoint = vars.vect5;\r
+        \r
+        Vector3f tmp1 = vars.vect6;\r
 \r
         // calculate normals for each polygon\r
         for (int r = 0; r < getHeight(); r++) {\r
             for (int c = 0; c < getWidth(); c++) {\r
 \r
-                rootPoint.set(c, getValue(c, r), r);\r
-                Vector3f normal = new Vector3f();\r
+                rootPoint.set(0, getValue(c, r), 0);\r
+                Vector3f normal = vars.vect8;\r
 \r
                 if (r == 0) { // first row\r
                     if (c == 0) { // first column\r
-                        rightPoint.set(c + 1, getValue(c + 1, r), r);\r
-                        bottomPoint.set(c, getValue(c, r + 1), r + 1);\r
-                        normal.set(getNormal(bottomPoint, rootPoint, rightPoint, scale));\r
+                        rightPoint.set(1, getValue(c + 1, r), 0);\r
+                        bottomPoint.set(0, getValue(c, r + 1), 1);\r
+                        getNormal(bottomPoint, rootPoint, rightPoint, scale, normal);\r
                     } else if (c == getWidth() - 1) { // last column\r
-                        leftPoint.set(c - 1, getValue(c - 1, r), r);\r
-                        bottomPoint.set(c, getValue(c, r + 1), r + 1);\r
-                        normal.set(getNormal(leftPoint, rootPoint, bottomPoint, scale));\r
+                        leftPoint.set(-1, getValue(c - 1, r), 0);\r
+                        bottomPoint.set(0, getValue(c, r + 1), 1);\r
+                        getNormal(leftPoint, rootPoint, bottomPoint, scale, normal);\r
                     } else { // all middle columns\r
-                        leftPoint.set(c - 1, getValue(c - 1, r), r);\r
-                        rightPoint.set(c + 1, getValue(c + 1, r), r);\r
-                        bottomPoint.set(c, getValue(c, r + 1), r + 1);\r
-                        Vector3f n1 = getNormal(leftPoint, rootPoint, bottomPoint, scale);\r
-                        Vector3f n2 = getNormal(bottomPoint, rootPoint, rightPoint, scale);\r
-                        normal.set(n1.add(n2).normalizeLocal());\r
+                        leftPoint.set(-1, getValue(c - 1, r), 0);\r
+                        rightPoint.set(1, getValue(c + 1, r), 0);\r
+                        bottomPoint.set(0, getValue(c, r + 1), 1);\r
+                        \r
+                        normal.set( getNormal(leftPoint, rootPoint, bottomPoint, scale, tmp1) );\r
+                        normal.addLocal( getNormal(bottomPoint, rootPoint, rightPoint, scale, tmp1) );\r
                     }\r
                 } else if (r == getHeight() - 1) { // last row\r
                     if (c == 0) { // first column\r
-                        topPoint.set(c, getValue(c, r - 1), r - 1);\r
-                        rightPoint.set(c + 1, getValue(c + 1, r), r);\r
-                        normal.set(getNormal(rightPoint, rootPoint, topPoint, scale));\r
+                        topPoint.set(0, getValue(c, r - 1), -1);\r
+                        rightPoint.set(1, getValue(c + 1, r), 0);\r
+                        getNormal(rightPoint, rootPoint, topPoint, scale, normal);\r
                     } else if (c == getWidth() - 1) { // last column\r
-                        topPoint.set(c, getValue(c, r - 1), r - 1);\r
-                        leftPoint.set(c - 1, getValue(c - 1, r), r);\r
-                        normal.set(getNormal(topPoint, rootPoint, leftPoint, scale));\r
+                        topPoint.set(0, getValue(c, r - 1), -1);\r
+                        leftPoint.set(-1, getValue(c - 1, r), 0);\r
+                        getNormal(topPoint, rootPoint, leftPoint, scale, normal);\r
                     } else { // all middle columns\r
-                        topPoint.set(c, getValue(c, r - 1), r - 1);\r
-                        leftPoint.set(c - 1, getValue(c - 1, r), r);\r
-                        rightPoint.set(c + 1, getValue(c + 1, r), r);\r
-                        Vector3f n1 = getNormal(topPoint, rootPoint, leftPoint, scale);\r
-                        Vector3f n2 = getNormal(rightPoint, rootPoint, topPoint, scale);\r
-                        normal.set(n1.add(n2).normalizeLocal());\r
+                        topPoint.set(0, getValue(c, r - 1), -1);\r
+                        leftPoint.set(-1, getValue(c - 1, r), 0);\r
+                        rightPoint.set(1, getValue(c + 1, r), 0);\r
+                        \r
+                        normal.set( getNormal(topPoint, rootPoint, leftPoint, scale, tmp1) );\r
+                        normal.addLocal( getNormal(rightPoint, rootPoint, topPoint, scale, tmp1) );\r
                     }\r
                 } else { // all middle rows\r
                     if (c == 0) { // first column\r
-                        topPoint.set(c, getValue(c, r - 1), r - 1);\r
-                        rightPoint.set(c + 1, getValue(c + 1, r), r);\r
-                        bottomPoint.set(c, getValue(c, r + 1), r + 1);\r
-                        Vector3f n1 = getNormal(rightPoint, rootPoint, topPoint, scale);\r
-                        Vector3f n2 = getNormal(bottomPoint, rootPoint, rightPoint, scale);\r
-                        normal.set(n1.add(n2).normalizeLocal());\r
+                        topPoint.set(0, getValue(c, r - 1), -1);\r
+                        rightPoint.set(1, getValue(c + 1, r), 0);\r
+                        bottomPoint.set(0, getValue(c, r + 1), 1);\r
+                        \r
+                        normal.set( getNormal(rightPoint, rootPoint, topPoint, scale, tmp1) );\r
+                        normal.addLocal( getNormal(bottomPoint, rootPoint, rightPoint, scale, tmp1) );\r
                     } else if (c == getWidth() - 1) { // last column\r
-                        topPoint.set(c, getValue(c, r - 1), r - 1);\r
-                        leftPoint.set(c - 1, getValue(c - 1, r), r);\r
-                        bottomPoint.set(c, getValue(c, r + 1), r + 1);\r
-                        Vector3f n1 = getNormal(topPoint, rootPoint, leftPoint, scale);\r
-                        Vector3f n2 = getNormal(leftPoint, rootPoint, bottomPoint, scale);\r
-                        normal.set(n1.add(n2).normalizeLocal());\r
+                        topPoint.set(0, getValue(c, r - 1), -1);\r
+                        leftPoint.set(-1, getValue(c - 1, r), 0);\r
+                        bottomPoint.set(0, getValue(c, r + 1), 1);\r
+\r
+                        normal.set( getNormal(topPoint, rootPoint, leftPoint, scale, tmp1) );\r
+                        normal.addLocal( getNormal(leftPoint, rootPoint, bottomPoint, scale, tmp1) );\r
                     } else { // all middle columns\r
-                        topPoint.set(c, getValue(c, r - 1), r - 1);\r
-                        leftPoint.set(c - 1, getValue(c - 1, r), r);\r
-                        rightPoint.set(c + 1, getValue(c + 1, r), r);\r
-                        bottomPoint.set(c, getValue(c, r + 1), r + 1);\r
-                        Vector3f n1 = getNormal(topPoint, rootPoint, leftPoint, scale);\r
-                        Vector3f n2 = getNormal(leftPoint, rootPoint, bottomPoint, scale);\r
-                        Vector3f n3 = getNormal(bottomPoint, rootPoint, rightPoint, scale);\r
-                        Vector3f n4 = getNormal(rightPoint, rootPoint, topPoint, scale);\r
-                        normal.set(n1.add(n2).add(n3).add(n4).normalizeLocal());\r
+                        topPoint.set(0, getValue(c, r - 1), -1);\r
+                        leftPoint.set(-1, getValue(c - 1, r), 0);\r
+                        rightPoint.set(1, getValue(c + 1, r), 0);\r
+                        bottomPoint.set(0, getValue(c, r + 1), 1);\r
+                        \r
+                        normal.set( getNormal(topPoint,  rootPoint, leftPoint, scale, tmp1 ) );\r
+                        normal.addLocal( getNormal(leftPoint, rootPoint, bottomPoint, scale, tmp1) );\r
+                        normal.addLocal( getNormal(bottomPoint, rootPoint, rightPoint, scale, tmp1) );\r
+                        normal.addLocal( getNormal(rightPoint, rootPoint, topPoint, scale, tmp1) );\r
                     }\r
                 }\r
-\r
+                normal.normalizeLocal();\r
                 BufferUtils.setInBuffer(normal, store, (r * getWidth() + c)); // save the normal\r
-\r
             }\r
         }\r
-\r
+        vars.release();\r
+        \r
         return store;\r
     }\r
 \r
-    private Vector3f getNormal(Vector3f firstPoint, Vector3f rootPoint, Vector3f secondPoint, Vector3f scale) {\r
-        Vector3f normal = new Vector3f();\r
-        //scale = Vector3f.UNIT_XYZ;\r
-        normal.set(firstPoint.mult(scale)).subtractLocal(rootPoint.mult(scale)).crossLocal(secondPoint.mult(scale).subtract(rootPoint.mult(scale))).normalizeLocal();\r
-        return normal;\r
+    private Vector3f getNormal(Vector3f firstPoint, Vector3f rootPoint, Vector3f secondPoint, Vector3f scale, Vector3f store) {\r
+        float x1 = firstPoint.x - rootPoint.x;\r
+        float y1 = firstPoint.y - rootPoint.y;\r
+        float z1 = firstPoint.z - rootPoint.z;\r
+        x1 *= scale.x;\r
+        y1 *= scale.y;\r
+        z1 *= scale.z;\r
+        float x2 = secondPoint.x - rootPoint.x;\r
+        float y2 = secondPoint.y - rootPoint.y;\r
+        float z2 = secondPoint.z - rootPoint.z;\r
+        x2 *= scale.x;\r
+        y2 *= scale.y;\r
+        z2 *= scale.z;\r
+        float x3 = (y1 * z2) - (z1 * y2);\r
+        float y3 = (z1 * x2) - (x1 * z2);\r
+        float z3 = (x1 * y2) - (y1 * x2);\r
+        \r
+        float inv = 1.0f / FastMath.sqrt(x3 * x3 + y3 * y3 + z3 * z3);\r
+        store.x = x3 * inv;\r
+        store.y = y3 * inv;\r
+        store.z = z3 * inv;\r
+        \r
+        \r
+        /*firstPoint.multLocal(scale);\r
+        rootPoint.multLocal(scale);\r
+        secondPoint.multLocal(scale);\r
+        firstPoint.subtractLocal(rootPoint);\r
+        secondPoint.subtractLocal(rootPoint);\r
+        firstPoint.cross(secondPoint, store);*/\r
+        return store;\r
     }\r
 \r
     /**\r
      * Keeps a count of the number of indexes, good for debugging\r
      */\r
-    public class VerboseIntBuffer {\r
+    public class VerboseBuffer {\r
 \r
-        private IntBuffer delegate;\r
+        private IndexBuffer delegate;\r
+        //private IntBuffer delegateInt;\r
+        //private ShortBuffer delegateShort;\r
         int count = 0;\r
+        //private boolean intb = true;\r
 \r
-        public VerboseIntBuffer(IntBuffer d) {\r
-            delegate = d;\r
+        public VerboseBuffer(IndexBuffer d) {\r
+            this.delegate = d;\r
         }\r
+        \r
+        /*public VerboseBuffer(Buffer d) {\r
+            if (d instanceof IntBuffer)\r
+                delegateInt = (IntBuffer)d;\r
+            else if (d instanceof ShortBuffer) {\r
+                delegateShort = (ShortBuffer)d;\r
+                intb = false;\r
+            }\r
+        }*/\r
 \r
         public void put(int value) {\r
+            delegate.put(count, value);\r
+            count++;\r
+        }\r
+        /*public void put(int value) {\r
             try {\r
-                delegate.put(value);\r
                 count++;\r
+                int limit = intb? delegateInt.limit() : delegateShort.limit();\r
+                if (count > limit)\r
+                    throw new BufferOverflowException();\r
+                if (intb)\r
+                    delegateInt.put(value);\r
+                else {\r
+                    System.out.println(Integer.toString(value)+" "+Short.toString((short)value));\r
+                    delegateShort.put((short)value);\r
+                }\r
             } catch (BufferOverflowException e) {\r
-                //System.out.println("err buffer size: "+delegate.capacity());\r
+                Logger.getLogger(this.getClass().getName()).log(Logger.Level.ERROR, "err buffer size: "+delegateInt.capacity());\r
             }\r
-        }\r
+        }*/\r
 \r
         public int getCount() {\r
             return count;\r
@@ -879,6 +961,61 @@ public class LODGeomap extends GeoMap {
     }\r
 \r
     /**\r
+     * Get the two triangles that make up the grid section at the specified point.\r
+     *\r
+     * For every grid space there are two triangles oriented like this:\r
+     *  *----*\r
+     *  |a / |\r
+     *  | / b|\r
+     *  *----*\r
+     * The corners of the mesh have differently oriented triangles. The two\r
+     * corners that we have to special-case are the top left and bottom right\r
+     * corners. They are oriented inversely:\r
+     *  *----*\r
+     *  | \ b|\r
+     *  |a \ |\r
+     *  *----*\r
+     */\r
+    protected float getHeight(int x, int z, float xm, float zm) {\r
+        \r
+        int index = findClosestHeightIndex(x, z);\r
+        if (index < 0) {\r
+            return Float.NaN;\r
+        }\r
+        \r
+        float h1 = hdata[index];                // top left\r
+        float h2 = hdata[index + 1];            // top right\r
+        float h3 = hdata[index + width];        // bottom left\r
+        float h4 = hdata[index + width + 1];    // bottom right\r
+\r
+        //float dix = (x % 1f) ;\r
+        //float diz = (z % 1f) ;\r
+            \r
+        if ((x == 0 && z == 0) || (x == width - 2 && z == width - 2)) {\r
+            // top left or bottom right grid point\r
+            /*  1----2\r
+             *  | \ b|\r
+             *  |a \ |\r
+             *  3----4 */\r
+            if (xm<zm)\r
+                return h1 + xm*(h4-h3) + zm*(h3-h1);\r
+            else\r
+                return h1 + xm*(h2-h1) + zm*(h4-h2);\r
+            \r
+        } else {\r
+            // all other grid points\r
+            /*  1----2\r
+             *  |a / |\r
+             *  | / b|\r
+             *  3----4 */\r
+            if (xm<(1-zm))\r
+                return h3 + (xm)*(h2-h1) + (1f-zm)*(h1-h3);\r
+            else\r
+                return h3 + (xm)*(h4-h3) + (1f-zm)*(h2-h4);\r
+        }\r
+    }\r
+    \r
+    /**\r
      * Get a representation of the underlying triangle at the given point,\r
      * translated to world coordinates.\r
      * \r
@@ -937,8 +1074,6 @@ public class LODGeomap extends GeoMap {
      *\r
      * @param x local x coordinate\r
      * @param z local z coordinate\r
-     * @param scale\r
-     * @param translation\r
      * @return\r
      */\r
     protected Triangle[] getGridTrianglesAtPoint(float x, float z) {\r
@@ -953,13 +1088,13 @@ public class LODGeomap extends GeoMap {
         Triangle t = new Triangle(new Vector3f(), new Vector3f(), new Vector3f());\r
         Triangle t2 = new Triangle(new Vector3f(), new Vector3f(), new Vector3f());\r
 \r
-        float h1 = hdata.get(index);                // top left\r
-        float h2 = hdata.get(index + 1);            // top right\r
-        float h3 = hdata.get(index + width);        // bottom left\r
-        float h4 = hdata.get(index + width + 1);    // bottom right\r
+        float h1 = hdata[index];                // top left\r
+        float h2 = hdata[index + 1];            // top right\r
+        float h3 = hdata[index + width];        // bottom left\r
+        float h4 = hdata[index + width + 1];    // bottom right\r
 \r
 \r
-        if ((gridX == 0 && gridY == 0) || (gridX == width - 1 && gridY == width - 1)) {\r
+        if ((gridX == 0 && gridY == 0) || (gridX == width - 2 && gridY == width - 2)) {\r
             // top left or bottom right grid point\r
             t.get(0).x = (gridX);\r
             t.get(0).y = (h1);\r
@@ -1024,7 +1159,7 @@ public class LODGeomap extends GeoMap {
     protected Triangle getTriangleAtPoint(float x, float z) {\r
         Triangle[] triangles = getGridTrianglesAtPoint(x, z);\r
         if (triangles == null) {\r
-            System.out.println("x,z: " + x + "," + z);\r
+            //System.out.println("x,z: " + x + "," + z);\r
             return null;\r
         }\r
         Vector2f point = new Vector2f(x, z);\r
diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/MultiTerrainLodControl.java b/engine/src/terrain/com/jme3/terrain/geomipmap/MultiTerrainLodControl.java
new file mode 100644 (file)
index 0000000..995bc8f
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.terrain.geomipmap;
+
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.Camera;
+import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
+import com.jme3.terrain.geomipmap.lodcalc.LodCalculator;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * An extension of the TerrainLodControl that handles
+ * multiple terrains at once. This is to be used if you 
+ * have your own tiling/paging terrain system, such as
+ * TerrainGrid.
+ * 
+ * @author Brent Owens
+ */
+public class MultiTerrainLodControl extends TerrainLodControl {
+    
+    List<TerrainQuad> terrains = new ArrayList<TerrainQuad>();
+    private List<TerrainQuad> addedTerrains = new ArrayList<TerrainQuad>();
+    private List<TerrainQuad> removedTerrains = new ArrayList<TerrainQuad>();
+
+    public MultiTerrainLodControl(List<Camera> cameras) {
+        this.cameras = cameras;
+        lodCalculator = new DistanceLodCalculator(65, 2.7f);
+    }
+
+    public MultiTerrainLodControl(Camera camera) {
+        List<Camera> cams = new ArrayList<Camera>();
+        cams.add(camera);
+        this.cameras = cams;
+        lodCalculator = new DistanceLodCalculator(65, 2.7f);
+    }
+    
+    /**
+     * Add a terrain that will have its LOD handled by this control.
+     * It will be added next update run. You should only call this from
+     * the render thread.
+     */
+    public void addTerrain(TerrainQuad tq) {
+        addedTerrains.add(tq);
+    }
+    
+    /**
+     * Add a terrain that will no longer have its LOD handled by this control.
+     * It will be removed next update run. You should only call this from
+     * the render thread.
+     */
+    public void removeTerrain(TerrainQuad tq) {
+        removedTerrains.add(tq);
+    }
+    
+    @Override
+    protected UpdateLOD getLodThread(List<Vector3f> locations, LodCalculator lodCalculator) {
+        return new UpdateMultiLOD(locations, lodCalculator);
+    }
+    
+    @Override
+    protected void prepareTerrain() {
+        if (!addedTerrains.isEmpty()) {
+            for (TerrainQuad t : addedTerrains) {
+                if (!terrains.contains(t))
+                    terrains.add(t);
+            }
+            addedTerrains.clear();
+        }
+        
+        if (!removedTerrains.isEmpty()) {
+            terrains.removeAll(removedTerrains);
+            removedTerrains.clear();
+        }
+        
+        for (TerrainQuad terrain : terrains)
+            terrain.cacheTerrainTransforms();// cache the terrain's world transforms so they can be accessed on the separate thread safely
+    }
+    
+    /**
+     * Overrides the parent UpdateLOD runnable to process
+     * multiple terrains.
+     */
+    protected class UpdateMultiLOD extends UpdateLOD {
+        
+        
+        protected UpdateMultiLOD(List<Vector3f> camLocations, LodCalculator lodCalculator) {
+            super(camLocations, lodCalculator);
+        }
+        
+        @Override
+        public HashMap<String, UpdatedTerrainPatch> call() throws Exception {
+            
+            setLodCalcRunning(true);
+            
+            HashMap<String,UpdatedTerrainPatch> updated = new HashMap<String,UpdatedTerrainPatch>();
+            
+            for (TerrainQuad terrainQuad : terrains) {
+                // go through each patch and calculate its LOD based on camera distance
+                terrainQuad.calculateLod(camLocations, updated, lodCalculator); // 'updated' gets populated here
+            }
+            
+            for (TerrainQuad terrainQuad : terrains) {
+                // then calculate the neighbour LOD values for seaming
+                terrainQuad.findNeighboursLod(updated);
+            }
+            
+            for (TerrainQuad terrainQuad : terrains) {
+                // check neighbour quads that need their edges seamed
+                terrainQuad.fixEdges(updated);
+            }
+            
+            for (TerrainQuad terrainQuad : terrains) {
+                // perform the edge seaming, if it requires it
+                terrainQuad.reIndexPages(updated, lodCalculator.usesVariableLod());
+            }
+            
+            //setUpdateQuadLODs(updated); // set back to main ogl thread
+            setLodCalcRunning(false);
+            
+            return updated;
+        }
+    }
+}
diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/NeighbourFinder.java b/engine/src/terrain/com/jme3/terrain/geomipmap/NeighbourFinder.java
new file mode 100644 (file)
index 0000000..ccaf8d1
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.terrain.geomipmap;
+
+/**
+ * Used for TerrainQuad to find neighbours that are not part of the
+ * same quad tree. Normally TerrainQuads function in a quad tree and
+ * use the neighbour methods getRightQuad, getLeftQuad etc. to update
+ * LOD values of the terrain (and for some other routines).
+ * 
+ * With this you can have a parent, control or spatial, that manages a group of
+ * TerrainQuads by linking them together through these four methods.
+ * 
+ * The general orientation of TerrainQuads and their sub-quads is as such:
+ * 
+ * 
+ *  +-- x+ ---->
+ *  |
+ *  |    1 | 3 (quadrants)
+ *  z+   --+--
+ *  |    2 | 4
+ *  |
+ * \/
+ * 
+ * Your implementation will still have to manage getHeight, getNormal, and 
+ * most other Terrain.java interface methods; often by offsetting the XZ
+ * coordinate parameters.
+ * 
+ * @author sploreg
+ */
+public interface NeighbourFinder {
+    
+    /**
+     * Get the TerrainQuad to the right of the supplied 'center' quad.
+     */
+    public TerrainQuad getRightQuad(TerrainQuad center);
+    
+    /**
+     * Get the TerrainQuad to the left of the supplied 'center' quad.
+     */
+    public TerrainQuad getLeftQuad(TerrainQuad center);
+    
+    /**
+     * Get the TerrainQuad above the supplied 'center' quad.
+     */
+    public TerrainQuad getTopQuad(TerrainQuad center);
+    
+    /**
+     * Get the TerrainQuad below the supplied 'center' quad.
+     */
+    public TerrainQuad getDownQuad(TerrainQuad center);
+}
index ba13e52..4c41e73 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2010 jMonkeyEngine
+ * Copyright (c) 2009-2012 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index 9357140..50e1f9f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2010 jMonkeyEngine
+ * Copyright (c) 2009-2012 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  */
 package com.jme3.terrain.geomipmap;
 
-import com.jme3.scene.control.UpdateControl;
-import com.jme3.bullet.PhysicsSpace;
-import com.jme3.bullet.collision.shapes.HeightfieldCollisionShape;
-import com.jme3.bullet.control.RigidBodyControl;
-import com.jme3.terrain.heightmap.HeightMap;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
+import com.jme3.bounding.BoundingBox;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
 import com.jme3.material.Material;
 import com.jme3.math.FastMath;
 import com.jme3.math.Vector2f;
 import com.jme3.math.Vector3f;
-import com.jme3.terrain.geomipmap.lodcalc.LodCalculator;
-import com.jme3.terrain.geomipmap.lodcalc.LodCalculatorFactory;
-import com.jme3.terrain.geomipmap.lodcalc.LodDistanceCalculatorFactory;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.control.UpdateControl;
+import com.jme3.terrain.Terrain;
+import com.jme3.terrain.heightmap.HeightMap;
 import com.jme3.terrain.heightmap.HeightMapGrid;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
 import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 /**
+ * <p>
+ * TerrainGrid itself is an actual TerrainQuad. Its four children are the visible four tiles.</p>
+ * </p><p>
+ * The grid is indexed by cells. Each cell has an integer XZ coordinate originating at 0,0.
+ * TerrainGrid will piggyback on the TerrainLodControl so it can use the camera for its
+ * updates as well. It does this in the overwritten update() method.
+ * </p><p>
+ * It uses an LRU (Least Recently Used) cache of 16 terrain tiles (full TerrainQuadTrees). The
+ * center 4 are the ones that are visible. As the camera moves, it checks what camera cell it is in
+ * and will attach the now visible tiles.
+ * </p><p>
+ * The 'quadIndex' variable is a 4x4 array that represents the tiles. The center
+ * four (index numbers: 5, 6, 9, 10) are what is visible. Each quadIndex value is an
+ * offset vector. The vector contains whole numbers and represents how many tiles in offset
+ * this location is from the center of the map. So for example the index 11 [Vector3f(2, 0, 1)]
+ * is located 2*terrainSize in X axis and 1*terrainSize in Z axis.
+ * </p><p>
+ * As the camera moves, it tests what cameraCell it is in. Each camera cell covers four quad tiles
+ * and is half way inside each one.
+ * </p><pre>
+ * +-------+-------+
+ * | 1     |     3 |    Four terrainQuads that make up the grid
+ * |    *..|..*    |    with the cameraCell in the middle, covering
+ * |----|--|--|----|    all four quads.
+ * |    *..|..*    |
+ * | 2     |     4 |
+ * +-------+-------+
+ * </pre><p>
+ * This results in the effect of when the camera gets half way across one of the sides of a quad to
+ * an empty (non-loaded) area, it will trigger the system to load in the next tiles.
+ * </p><p>
+ * The tile loading is done on a background thread, and once the tile is loaded, then it is
+ * attached to the qrid quad tree, back on the OGL thread. It will grab the terrain quad from
+ * the LRU cache if it exists. If it does not exist, it will load in the new TerrainQuad tile.
+ * </p><p>
+ * The loading of new tiles triggers events for any TerrainGridListeners. The events are:
+ * <ul>
+ *  <li>tile Attached
+ *  <li>tile Detached
+ *  <li>grid moved.
+ * </ul>
+ * <p>
+ * These allow physics to update, and other operation (often needed for loading the terrain) to occur
+ * at the right time.
+ * </p>
  * @author Anthyon
  */
 public class TerrainGrid extends TerrainQuad {
-
     protected static final Logger log = Logger.getLogger(TerrainGrid.class.getCanonicalName());
-    protected Vector3f currentCell;
-    protected int quarterSize;
+    protected Vector3f currentCamCell = Vector3f.ZERO;
+    protected int quarterSize; // half of quadSize
     protected int quadSize;
     protected HeightMapGrid heightMapGrid;
+    private TerrainGridTileLoader gridTileLoader;
     protected Vector3f[] quadIndex;
-    protected Map<String, TerrainGridListener> listeners = new HashMap<String, TerrainGridListener>();
+    protected Set<TerrainGridListener> listeners = new HashSet<TerrainGridListener>();
     protected Material material;
-    protected LRUCache<Vector3f, TerrainQuad> cache = new LRUCache<Vector3f, TerrainQuad>(16);
-    protected RigidBodyControl[] quadControls;
-    protected PhysicsSpace space;
-    private int cellsLoaded = 0;
-    private int[] gridOffset;
+    //cache  needs to be 1 row (4 cells) larger than what we care is cached
+    protected LRUCache<Vector3f, TerrainQuad> cache = new LRUCache<Vector3f, TerrainQuad>(20);
+    protected int cellsLoaded = 0;
+    protected int[] gridOffset;
+    protected boolean runOnce = false;
+    protected ExecutorService cacheExecutor;
 
     protected class UpdateQuadCache implements Runnable {
 
@@ -79,31 +134,59 @@ public class TerrainGrid extends TerrainQuad {
             this.location = location;
         }
 
+        /**
+         * This is executed if the camera has moved into a new CameraCell and will load in
+         * the new TerrainQuad tiles to be children of this TerrainGrid parent.
+         * It will first check the LRU cache to see if the terrain tile is already there,
+         * if it is not there, it will load it in and then cache that tile.
+         * The terrain tiles get added to the quad tree back on the OGL thread using the
+         * attachQuadAt() method. It also resets any cached values in TerrainQuad (such as
+         * neighbours).
+         */
         public void run() {
-
             for (int i = 0; i < 4; i++) {
                 for (int j = 0; j < 4; j++) {
                     int quadIdx = i * 4 + j;
-                    final Vector3f temp = location.add(quadIndex[quadIdx]);
-                    TerrainQuad q = cache.get(temp);
+                    final Vector3f quadCell = location.add(quadIndex[quadIdx]);
+                    TerrainQuad q = cache.get(quadCell);
                     if (q == null) {
-                        // create the new Quad since it doesn't exist
-                        HeightMap heightMapAt = heightMapGrid.getHeightMapAt(temp);
-                        q = new TerrainQuad(getName() + "Quad" + temp, patchSize, quadSize, totalSize, heightMapAt == null ? null : heightMapAt.getHeightMap());
-                        q.setMaterial(material.clone());
-                        log.log(Level.FINE, "Loaded TerrainQuad {0}", q.getName());
+                        if (heightMapGrid != null) {
+                            // create the new Quad since it doesn't exist
+                            HeightMap heightMapAt = heightMapGrid.getHeightMapAt(quadCell);
+                            q = new TerrainQuad(getName() + "Quad" + quadCell, patchSize, quadSize, heightMapAt == null ? null : heightMapAt.getHeightMap());
+                            q.setMaterial(material.clone());
+                            log.log(Level.FINE, "Loaded TerrainQuad {0} from HeightMapGrid", q.getName());
+                        } else if (gridTileLoader != null) {
+                            q = gridTileLoader.getTerrainQuadAt(quadCell);
+                            // only clone the material to the quad if it doesn't have a material of its own
+                            if(q.getMaterial()==null) q.setMaterial(material.clone());
+                            log.log(Level.FINE, "Loaded TerrainQuad {0} from TerrainQuadGrid", q.getName());
+                        }
                     }
-                    cache.put(temp, q);
+                    cache.put(quadCell, q);
 
+                    
+                    final int quadrant = getQuadrant(quadIdx);
+                    final TerrainQuad newQuad = q;
+                    
                     if (isCenter(quadIdx)) {
                         // if it should be attached as a child right now, attach it
-                        final int quadrant = getQuadrant(quadIdx);
-                        final TerrainQuad newQuad = q;
-                        // back on the OpenGL thread:
                         getControl(UpdateControl.class).enqueue(new Callable() {
-
+                            // back on the OpenGL thread:
                             public Object call() throws Exception {
-                                attachQuadAt(newQuad, quadrant, temp);
+                                if (newQuad.getParent() != null) {
+                                    attachQuadAt(newQuad, quadrant, quadCell, true);
+                                }
+                                else {
+                                    attachQuadAt(newQuad, quadrant, quadCell, false);
+                                }
+                                return null;
+                            }
+                        });
+                    } else {
+                        getControl(UpdateControl.class).enqueue(new Callable() {
+                            public Object call() throws Exception {
+                                removeQuad(newQuad);
                                 return null;
                             }
                         });
@@ -111,6 +194,20 @@ public class TerrainGrid extends TerrainQuad {
                 }
             }
 
+            getControl(UpdateControl.class).enqueue(new Callable() {
+                    // back on the OpenGL thread:
+                    public Object call() throws Exception {
+                        for (Spatial s : getChildren()) {
+                            if (s instanceof TerrainQuad) {
+                                TerrainQuad tq = (TerrainQuad)s;
+                                tq.resetCachedNeighbours();
+                            }
+                        }
+                        System.out.println("fixed normals "+location.clone().mult(size));
+                        setNeedToRecalculateNormals();
+                        return null;
+                    }
+            });
         }
     }
 
@@ -119,182 +216,240 @@ public class TerrainGrid extends TerrainQuad {
     }
 
     protected int getQuadrant(int quadIndex) {
-        if (quadIndex == 9) {
+        if (quadIndex == 5) {
             return 1;
-        } else if (quadIndex == 5) {
+        } else if (quadIndex == 9) {
             return 2;
-        } else if (quadIndex == 10) {
-            return 3;
         } else if (quadIndex == 6) {
+            return 3;
+        } else if (quadIndex == 10) {
             return 4;
         }
         return 0; // error
     }
 
-    public TerrainGrid(String name, int patchSize, int maxVisibleSize, Vector3f scale, HeightMapGrid heightMapGrid,
+    public TerrainGrid(String name, int patchSize, int maxVisibleSize, Vector3f scale, TerrainGridTileLoader terrainQuadGrid,
             Vector2f offset, float offsetAmount) {
         this.name = name;
         this.patchSize = patchSize;
         this.size = maxVisibleSize;
-        this.quarterSize = maxVisibleSize >> 2;
-        this.quadSize = (maxVisibleSize + 1) >> 1;
         this.stepScale = scale;
-        this.heightMapGrid = heightMapGrid;
-        heightMapGrid.setSize(this.quadSize);
-        this.totalSize = maxVisibleSize;
         this.offset = offset;
         this.offsetAmount = offsetAmount;
-        //this.lodCalculatorFactory = lodCalculatorFactory;
-        this.gridOffset = new int[]{0,0};
-        //if (lodCalculatorFactory == null) {
-        //    lodCalculatorFactory = new LodDistanceCalculatorFactory();
-        //}
-        this.quadIndex = new Vector3f[]{
-        new Vector3f(-1, 0, 2), new Vector3f(0, 0, 2), new Vector3f(1, 0, 2), new Vector3f(2, 0, 2),
-        new Vector3f(-1, 0, 1), new Vector3f(0, 0, 1), new Vector3f(1, 0, 1), new Vector3f(2, 0, 1),
-        new Vector3f(-1, 0, 0), new Vector3f(0, 0, 0), new Vector3f(1, 0, 0), new Vector3f(2, 0, 0),
-        new Vector3f(-1, 0, -1), new Vector3f(0, 0, -1), new Vector3f(1, 0, -1), new Vector3f(-2, 0, -1)};
-
+        initData();
+        this.gridTileLoader = terrainQuadGrid;
+        terrainQuadGrid.setPatchSize(this.patchSize);
+        terrainQuadGrid.setQuadSize(this.quadSize);
         addControl(new UpdateControl());
+        
+        fixNormalEdges(new BoundingBox(new Vector3f(0,0,0), size*2, Float.MAX_VALUE, size*2));
+        addControl(new NormalRecalcControl(this));
     }
 
-    public TerrainGrid(String name, int patchSize, int maxVisibleSize, Vector3f scale, HeightMapGrid heightMapGrid) {
-        this(name, patchSize, maxVisibleSize, scale, heightMapGrid, new Vector2f(), 0);
+    public TerrainGrid(String name, int patchSize, int maxVisibleSize, Vector3f scale, TerrainGridTileLoader terrainQuadGrid) {
+        this(name, patchSize, maxVisibleSize, scale, terrainQuadGrid, new Vector2f(), 0);
     }
 
-    public TerrainGrid(String name, int patchSize, int maxVisibleSize, HeightMapGrid heightMapGrid) {
-        this(name, patchSize, maxVisibleSize, Vector3f.UNIT_XYZ, heightMapGrid);
+    public TerrainGrid(String name, int patchSize, int maxVisibleSize, TerrainGridTileLoader terrainQuadGrid) {
+        this(name, patchSize, maxVisibleSize, Vector3f.UNIT_XYZ, terrainQuadGrid);
     }
 
     public TerrainGrid() {
     }
 
-    public void initialize(Vector3f location) {
-        if (this.material == null) {
-            throw new RuntimeException("Material must be set prior to call of initialize");
-        }
-        Vector3f camCell = this.getCell(location);
-        this.updateChildrens(camCell);
-        for (TerrainGridListener l : this.listeners.values()) {
-            l.gridMoved(camCell);
-        }
+    private void initData() {
+        int maxVisibleSize = size;
+        this.quarterSize = maxVisibleSize >> 2;
+        this.quadSize = (maxVisibleSize + 1) >> 1;
+        this.totalSize = maxVisibleSize;
+        this.gridOffset = new int[]{0, 0};
+
+        /*
+         *        -z
+         *         | 
+         *        1|3 
+         *  -x ----+---- x
+         *        2|4
+         *         |
+         *         z
+         */
+        this.quadIndex = new Vector3f[]{
+            new Vector3f(-1, 0, -1), new Vector3f(0, 0, -1), new Vector3f(1, 0, -1), new Vector3f(2, 0, -1),
+            new Vector3f(-1, 0, 0), new Vector3f(0, 0, 0), new Vector3f(1, 0, 0), new Vector3f(2, 0, 0),
+            new Vector3f(-1, 0, 1), new Vector3f(0, 0, 1), new Vector3f(1, 0, 1), new Vector3f(2, 0, 1),
+            new Vector3f(-1, 0, 2), new Vector3f(0, 0, 2), new Vector3f(1, 0, 2), new Vector3f(2, 0, 2)};
+
     }
 
-    @Override
-    public void update(List<Vector3f> locations, LodCalculator lodCalculator) {
-        // for now, only the first camera is handled.
-        // to accept more, there are two ways:
-        // 1: every camera has an associated grid, then the location is not enough to identify which camera location has changed
-        // 2: grids are associated with locations, and no incremental update is done, we load new grids for new locations, and unload those that are not needed anymore
-        Vector3f cam = locations.get(0);
-        Vector3f camCell = this.getCell(cam);
-        if(cellsLoaded>1){                  // Check if cells are updated before updating gridoffset.
-            gridOffset[0] = Math.round(camCell.x*(size/2));
-            gridOffset[1] = Math.round(camCell.z*(size/2));
-            cellsLoaded=0;
-        }
-        if (camCell.x != this.currentCell.x || camCell.z != currentCell.z) {
-            this.updateChildrens(camCell);
-            for (TerrainGridListener l : this.listeners.values()) {
-                l.gridMoved(camCell);
-            }
-        }
-        super.update(locations, lodCalculator);
+    /**
+     * Get the location in cell-coordinates of the specified location.
+     * Cell coordinates are integer corrdinates, usually with y=0, each 
+     * representing a cell in the world.
+     * For example, moving right in the +X direction:
+     * (0,0,0) (1,0,0) (2,0,0), (3,0,0)
+     * and then down the -Z direction:
+     * (3,0,-1) (3,0,-2) (3,0,-3)
+     */
+    public Vector3f getCamCell(Vector3f location) {
+        Vector3f tile = getTileCell(location);
+        Vector3f offsetHalf = new Vector3f(-0.5f, 0, -0.5f);
+        Vector3f shifted = tile.subtract(offsetHalf);
+        return new Vector3f(FastMath.floor(shifted.x), 0, FastMath.floor(shifted.z));
     }
 
-    public Vector3f getCell(Vector3f location) {
-        final Vector3f v = location.clone().divideLocal(this.getLocalScale().mult(this.quadSize - 1)).add(0.5f, 0, 0.5f);
-        return new Vector3f(FastMath.floor(v.x), 0, FastMath.floor(v.z));
+    /**
+     * Centered at 0,0.
+     * Get the tile index location in integer form:
+     * @param location world coordinate
+     */
+    public Vector3f getTileCell(Vector3f location) {
+        Vector3f tileLoc = location.divide(this.getWorldScale().mult(this.quadSize));
+        return tileLoc;
     }
 
-    protected void removeQuad(int idx) {
-        if (this.getQuad(idx) != null) {
-            if (quadControls != null) {
-                this.getQuad(idx).removeControl(RigidBodyControl.class);
-            }
-            for (TerrainGridListener l : listeners.values()) {
-                l.tileDetached(getCell(this.getQuad(idx).getWorldTranslation()), this.getQuad(idx));
+    public TerrainGridTileLoader getGridTileLoader() {
+        return gridTileLoader;
+    }
+    
+    /**
+     * Get the terrain tile at the specified world location, in XZ coordinates.
+     */
+    public Terrain getTerrainAt(Vector3f worldLocation) {
+        if (worldLocation == null)
+            return null;
+        Vector3f tileCell = getTileCell(worldLocation.setY(0));
+        tileCell = new Vector3f(Math.round(tileCell.x), tileCell.y, Math.round(tileCell.z));
+        return cache.get(tileCell);
+    }
+    
+    /**
+     * Get the terrain tile at the specified XZ cell coordinate (not world coordinate).
+     * @param cellCoordinate integer cell coordinates
+     * @return the terrain tile at that location
+     */
+    public Terrain getTerrainAtCell(Vector3f cellCoordinate) {
+        return cache.get(cellCoordinate);
+    }
+    
+    /**
+     * Convert the world location into a cell location (integer coordinates)
+     */
+    public Vector3f toCellSpace(Vector3f worldLocation) {
+        Vector3f tileCell = getTileCell(worldLocation);
+        tileCell = new Vector3f(Math.round(tileCell.x), tileCell.y, Math.round(tileCell.z));
+        return tileCell;
+    }
+    
+    /**
+     * Convert the cell coordinate (integer coordinates) into world coordinates.
+     */
+    public Vector3f toWorldSpace(Vector3f cellLocation) {
+        return cellLocation.mult(getLocalScale()).multLocal(quadSize - 1);
+    }
+    
+    protected void removeQuad(TerrainQuad q) {
+        if (q != null && ( (q.getQuadrant() > 0 && q.getQuadrant()<5) || q.getParent() != null) ) {
+            for (TerrainGridListener l : listeners) {
+                l.tileDetached(getTileCell(q.getWorldTranslation()), q);
             }
-            this.detachChild(this.getQuad(idx));
+            q.setQuadrant((short)0);
+            this.detachChild(q);
             cellsLoaded++; // For gridoffset calc., maybe the run() method is a better location for this.
         }
     }
 
     /**
      * Runs on the rendering thread
+     * @param shifted quads are still attached to the parent and don't need to re-load
      */
-    protected void attachQuadAt(TerrainQuad q, int quadrant, Vector3f cam) {
-        this.removeQuad(quadrant);
-        //q.setMaterial(this.material);
-        //q.setLocalTranslation(quadOrigins[quadrant - 1]);
+    protected void attachQuadAt(TerrainQuad q, int quadrant, Vector3f quadCell, boolean shifted) {
+        
         q.setQuadrant((short) quadrant);
-        this.attachChild(q);
+        if (!shifted)
+            this.attachChild(q);
 
-        Vector3f loc = cam.mult(this.quadSize - 1).subtract(quarterSize, 0, quarterSize);// quadrant location handled TerrainQuad automatically now
+        Vector3f loc = quadCell.mult(this.quadSize - 1).subtract(quarterSize, 0, quarterSize);// quadrant location handled TerrainQuad automatically now
         q.setLocalTranslation(loc);
 
-        for (TerrainGridListener l : listeners.values()) {
-            l.tileAttached(cam, q);
+        if (!shifted) {
+            for (TerrainGridListener l : listeners) {
+                l.tileAttached(quadCell, q);
+            }
         }
-
         updateModelBound();
+        
     }
 
-    protected void updateChildrens(Vector3f cam) {
-        // ---------------------------------------------------
-        // LRU cache is used, so elements that need to remain
-        // should be touched.
-        // ---------------------------------------------------
+    
+    /**
+     * Called when the camera has moved into a new cell. We need to
+     * update what quads are in the scene now.
+     * 
+     * Step 1: touch cache
+     * LRU cache is used, so elements that need to remain
+     * should be touched.
+     *
+     * Step 2: load new quads in background thread
+     * if the camera has moved into a new cell, we load in new quads
+     * @param camCell the cell the camera is in
+     */
+    protected void updateChildren(Vector3f camCell) {
+
         int dx = 0;
         int dy = 0;
-        if (currentCell != null) {
-            dx = (int) (cam.x - currentCell.x);
-            dy = (int) (cam.z - currentCell.z);
+        if (currentCamCell != null) {
+            dx = (int) (camCell.x - currentCamCell.x);
+            dy = (int) (camCell.z - currentCamCell.z);
         }
 
-        int kxm = 0;
-        int kxM = 4;
-        int kym = 0;
-        int kyM = 4;
-        if (dx == -1) {
-            kxM = 3;
-        } else if (dx == 1) {
-            kxm = 1;
+        int xMin = 0;
+        int xMax = 4;
+        int yMin = 0;
+        int yMax = 4;
+        if (dx == -1) { // camera moved to -X direction
+            xMax = 3;
+        } else if (dx == 1) { // camera moved to +X direction
+            xMin = 1;
         }
 
-        if (dy == -1) {
-            kyM = 3;
-        } else if (dy == 1) {
-            kym = 1;
+        if (dy == -1) { // camera moved to -Y direction
+            yMax = 3;
+        } else if (dy == 1) { // camera moved to +Y direction
+            yMin = 1;
         }
 
-        for (int i = kym; i < kyM; i++) {
-            for (int j = kxm; j < kxM; j++) {
-                cache.get(cam.add(quadIndex[i * 4 + j]));
+        // Touch the items in the cache that we are and will be interested in.
+        // We activate cells in the direction we are moving. If we didn't move 
+        // either way in one of the axes (say X or Y axis) then they are all touched.
+        for (int i = yMin; i < yMax; i++) {
+            for (int j = xMin; j < xMax; j++) {
+                cache.get(camCell.add(quadIndex[i * 4 + j]));
             }
         }
+        
         // ---------------------------------------------------
         // ---------------------------------------------------
 
-        if (executor == null) {
-            executor = createExecutorService();
+        if (cacheExecutor == null) {
+            // use the same executor as the LODControl
+            cacheExecutor = createExecutorService();
         }
 
-        executor.submit(new UpdateQuadCache(cam));
+        cacheExecutor.submit(new UpdateQuadCache(camCell));
 
-        this.currentCell = cam;
+        this.currentCamCell = camCell;
     }
 
-    public void addListener(String id, TerrainGridListener listener) {
-        this.listeners.put(id, listener);
+    public void addListener(TerrainGridListener listener) {
+        this.listeners.add(listener);
     }
 
     public Vector3f getCurrentCell() {
-        return this.currentCell;
+        return this.currentCamCell;
     }
 
-    public void removeListener(String id) {
-        this.listeners.remove(id);
+    public void removeListener(TerrainGridListener listener) {
+        this.listeners.remove(listener);
     }
 
     @Override
@@ -316,49 +471,95 @@ public class TerrainGrid extends TerrainQuad {
         }
         super.adjustHeight(xz, height);
     }
-    
+
     @Override
     protected float getHeightmapHeight(int x, int z) {
-        return super.getHeightmapHeight(x-gridOffset[0], z-gridOffset[1]);
+        return super.getHeightmapHeight(x - gridOffset[0], z - gridOffset[1]);
     }
-
+    
     @Override
-    protected TerrainQuad findDownQuad() {
-        if (quadrant == 1) {
-            return cache.get(currentCell.add(quadIndex[13]));
-        } else if (quadrant == 3) {
-            return cache.get(currentCell.add(quadIndex[14]));
-        }
-        return null;
+    public int getNumMajorSubdivisions() {
+        return 2;
     }
-
+    
     @Override
-    protected TerrainQuad findLeftQuad() {
-        if (quadrant == 1) {
-            return cache.get(currentCell.add(quadIndex[8]));
-        } else if (quadrant == 2) {
-            return cache.get(currentCell.add(quadIndex[4]));
-        }
-        return null;
+    public Material getMaterial(Vector3f worldLocation) {
+        if (worldLocation == null)
+            return null;
+        Vector3f tileCell = getTileCell(worldLocation);
+        Terrain terrain = cache.get(tileCell);
+        if (terrain == null)
+            return null; // terrain not loaded for that cell yet!
+        return terrain.getMaterial(worldLocation);
     }
 
+    /**
+     * This will print out any exceptions from the thread
+     */
+    protected ExecutorService createExecutorService() {
+        final ThreadFactory threadFactory = new ThreadFactory() {
+            public Thread newThread(Runnable r) {
+                Thread th = new Thread(r);
+                th.setName("jME TerrainGrid Thread");
+                th.setDaemon(true);
+                return th;
+            }
+        };
+        ThreadPoolExecutor ex = new ThreadPoolExecutor(1, 1,
+                                    0L, TimeUnit.MILLISECONDS,
+                                    new LinkedBlockingQueue<Runnable>(), 
+                                    threadFactory) {
+            protected void afterExecute(Runnable r, Throwable t) {
+                super.afterExecute(r, t);
+                if (t == null && r instanceof Future<?>) {
+                    try {
+                        Future<?> future = (Future<?>) r;
+                        if (future.isDone())
+                            future.get();
+                    } catch (CancellationException ce) {
+                        t = ce;
+                    } catch (ExecutionException ee) {
+                        t = ee.getCause();
+                    } catch (InterruptedException ie) {
+                        Thread.currentThread().interrupt(); // ignore/reset
+                    }
+                }
+                if (t != null)
+                    t.printStackTrace();
+            }
+        };
+        return ex;
+    }
+    
     @Override
-    protected TerrainQuad findRightQuad() {
-        if (quadrant == 3) {
-            return cache.get(currentCell.add(quadIndex[11]));
-        } else if (quadrant == 4) {
-            return cache.get(currentCell.add(quadIndex[7]));
+    public void read(JmeImporter im) throws IOException {
+        super.read(im);
+        InputCapsule c = im.getCapsule(this);
+        name = c.readString("name", null);
+        size = c.readInt("size", 0);
+        patchSize = c.readInt("patchSize", 0);
+        stepScale = (Vector3f) c.readSavable("stepScale", null);
+        offset = (Vector2f) c.readSavable("offset", null);
+        offsetAmount = c.readFloat("offsetAmount", 0);
+        gridTileLoader = (TerrainGridTileLoader) c.readSavable("terrainQuadGrid", null);
+        material = (Material) c.readSavable("material", null);
+        initData();
+        if (gridTileLoader != null) {
+            gridTileLoader.setPatchSize(this.patchSize);
+            gridTileLoader.setQuadSize(this.quadSize);
         }
-        return null;
     }
 
     @Override
-    protected TerrainQuad findTopQuad() {
-        if (quadrant == 2) {
-            return cache.get(currentCell.add(quadIndex[1]));
-        } else if (quadrant == 4) {
-            return cache.get(currentCell.add(quadIndex[2]));
-        }
-        return null;
+    public void write(JmeExporter ex) throws IOException {
+        super.write(ex);
+        OutputCapsule c = ex.getCapsule(this);
+        c.write(gridTileLoader, "terrainQuadGrid", null);
+        c.write(size, "size", 0);
+        c.write(patchSize, "patchSize", 0);
+        c.write(stepScale, "stepScale", null);
+        c.write(offset, "offset", null);
+        c.write(offsetAmount, "offsetAmount", 0);
+        c.write(material, "material", null);
     }
 }
index 38e0001..cb25cae 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2010 jMonkeyEngine
+ * Copyright (c) 2009-2012 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  */
 package com.jme3.terrain.geomipmap;
 
-import com.jme3.material.Material;
 import com.jme3.math.Vector3f;
 
 /**
- *
+ * Notifies the user of grid change events, such as moving to new grid cells.
  * @author Anthyon
  */
 public interface TerrainGridListener {
 
+    /**
+     * Called whenever the camera has moved full grid cells. This triggers new tiles to load.
+     * @param newCenter 
+     */
     public void gridMoved(Vector3f newCenter);
 
+    /**
+     * Called when a TerrainQuad is attached to the scene and is visible (attached to the root TerrainGrid)
+     * @param cell the cell that is moved into
+     * @param quad the quad that was just attached
+     */
     public void tileAttached( Vector3f cell, TerrainQuad quad );
 
+    /**
+     * Called when a TerrainQuad is detached from its TerrainGrid parent: it is no longer on the scene graph.
+     * @param cell the cell that is moved into
+     * @param quad the quad that was just detached
+     */
     public void tileDetached( Vector3f cell, TerrainQuad quad );
 }
diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainGridLodControl.java b/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainGridLodControl.java
new file mode 100644 (file)
index 0000000..65a209b
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.terrain.geomipmap;
+
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.Camera;
+import com.jme3.terrain.Terrain;
+import com.jme3.terrain.geomipmap.lodcalc.LodCalculator;
+import java.util.List;
+
+/**
+ * Updates grid offsets and cell positions.
+ * As well as updating LOD.
+ * 
+ * @author sploreg
+ */
+public class TerrainGridLodControl extends TerrainLodControl {
+    
+    public TerrainGridLodControl(Terrain terrain, Camera camera) {
+        super(terrain, camera);
+    }
+    
+    @Override
+    protected void updateLOD(List<Vector3f> locations, LodCalculator lodCalculator) {
+        TerrainGrid terrainGrid = (TerrainGrid)getSpatial();
+        
+        // for now, only the first camera is handled.
+        // to accept more, there are two ways:
+        // 1: every camera has an associated grid, then the location is not enough to identify which camera location has changed
+        // 2: grids are associated with locations, and no incremental update is done, we load new grids for new locations, and unload those that are not needed anymore
+        Vector3f cam = locations.isEmpty() ? Vector3f.ZERO.clone() : locations.get(0);
+        Vector3f camCell = terrainGrid.getCamCell(cam); // get the grid index value of where the camera is (ie. 2,1)
+        if (terrainGrid.cellsLoaded > 1) {                  // Check if cells are updated before updating gridoffset.
+            terrainGrid.gridOffset[0] = Math.round(camCell.x * (terrainGrid.size / 2));
+            terrainGrid.gridOffset[1] = Math.round(camCell.z * (terrainGrid.size / 2));
+            terrainGrid.cellsLoaded = 0;
+        }
+        if (camCell.x != terrainGrid.currentCamCell.x || camCell.z != terrainGrid.currentCamCell.z || !terrainGrid.runOnce) {
+            // if the camera has moved into a new cell, load new terrain into the visible 4 center quads
+            terrainGrid.updateChildren(camCell);
+            for (TerrainGridListener l : terrainGrid.listeners) {
+                l.gridMoved(camCell);
+            }
+        }
+        terrainGrid.runOnce = true;
+        super.updateLOD(locations, lodCalculator);
+    }
+}
diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainGridTileLoader.java b/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainGridTileLoader.java
new file mode 100644 (file)
index 0000000..7ff1ce0
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.terrain.geomipmap;
+
+import com.jme3.export.Savable;
+import com.jme3.math.Vector3f;
+
+/**
+ *
+ * @author normenhansen
+ */
+public interface TerrainGridTileLoader extends Savable {
+
+    public TerrainQuad getTerrainQuadAt(Vector3f location);
+
+    public void setPatchSize(int patchSize);
+
+    public void setQuadSize(int quadSize);
+}
index 23bb458..96bedc2 100644 (file)
@@ -1,5 +1,5 @@
 /*\r
- * Copyright (c) 2009-2010 jMonkeyEngine\r
+ * Copyright (c) 2009-2012 jMonkeyEngine\r
  * All rights reserved.\r
  *\r
  * Redistribution and use in source and binary forms, with or without\r
@@ -36,8 +36,6 @@ import com.jme3.export.JmeExporter;
 import com.jme3.export.JmeImporter;\r
 import com.jme3.export.OutputCapsule;\r
 import com.jme3.math.Vector3f;\r
-import java.util.List;\r
-\r
 import com.jme3.renderer.Camera;\r
 import com.jme3.renderer.RenderManager;\r
 import com.jme3.renderer.ViewPort;\r
@@ -50,6 +48,17 @@ import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
 import com.jme3.terrain.geomipmap.lodcalc.LodCalculator;\r
 import java.io.IOException;\r
 import java.util.ArrayList;\r
+import java.util.HashMap;\r
+import java.util.List;\r
+import java.util.concurrent.Callable;\r
+import java.util.concurrent.ExecutionException;\r
+import java.util.concurrent.ExecutorService;\r
+import java.util.concurrent.Executors;\r
+import java.util.concurrent.Future;\r
+import java.util.concurrent.ThreadFactory;\r
+import java.util.concurrent.atomic.AtomicBoolean;\r
+import java.util.logging.Level;\r
+import java.util.logging.Logger;\r
 \r
 /**\r
  * Tells the terrain to update its Level of Detail.\r
@@ -60,16 +69,36 @@ import java.util.ArrayList;
  * in the future it will use all of them to determine what\r
  * LOD to set.\r
  *\r
+ * This control serializes, but it does not save the Camera reference.\r
+ * This camera reference has to be manually added in when you load the\r
+ * terrain to the scene!\r
+ * \r
+ * When the control or the terrain are removed from the scene, you should call\r
+ * TerrainLodControl.detachAndCleanUpControl() to remove any threads it created\r
+ * to handle the LOD processing. If you supply your own executor service, then\r
+ * you have to handle its thread termination yourself.\r
+ * \r
  * @author Brent Owens\r
  */\r
 public class TerrainLodControl extends AbstractControl {\r
 \r
     private Terrain terrain;\r
-    private List<Camera> cameras;\r
+    protected List<Camera> cameras;\r
     private List<Vector3f> cameraLocations = new ArrayList<Vector3f>();\r
-    private LodCalculator lodCalculator;\r
+    protected LodCalculator lodCalculator;\r
     private boolean hasResetLod = false; // used when enabled is set to false\r
 \r
+    private HashMap<String,UpdatedTerrainPatch> updatedPatches;\r
+    private final Object updatePatchesLock = new Object();\r
+    \r
+    protected List<Vector3f> lastCameraLocations; // used for LOD calc\r
+    private AtomicBoolean lodCalcRunning = new AtomicBoolean(false);\r
+    private int lodOffCount = 0;\r
+    \r
+    protected ExecutorService executor;\r
+    protected Future<HashMap<String, UpdatedTerrainPatch>> indexer;\r
+    private boolean forceUpdate = true;\r
+    \r
     public TerrainLodControl() {\r
     }\r
 \r
@@ -89,15 +118,30 @@ public class TerrainLodControl extends AbstractControl {
     public TerrainLodControl(Terrain terrain, List<Camera> cameras) {\r
         this.terrain = terrain;\r
         this.cameras = cameras;\r
+        lodCalculator = new DistanceLodCalculator(65, 2.7f); // a default calculator\r
     }\r
 \r
     @Override\r
     protected void controlRender(RenderManager rm, ViewPort vp) {\r
     }\r
 \r
-    @Override\r
-    public void update(float tpf) {\r
-        controlUpdate(tpf);\r
+    /**\r
+     * Set your own custom executor to be used. The control will use\r
+     * this instead of creating its own.\r
+     */\r
+    public void setExecutor(ExecutorService executor) {\r
+        this.executor = executor;\r
+    }\r
+    \r
+    protected ExecutorService createExecutorService() {\r
+        return Executors.newSingleThreadExecutor(new ThreadFactory() {\r
+            public Thread newThread(Runnable r) {\r
+                Thread th = new Thread(r);\r
+                th.setName("jME Terrain Thread");\r
+                th.setDaemon(true);\r
+                return th;\r
+            }\r
+        });\r
     }\r
     \r
     @Override\r
@@ -122,10 +166,142 @@ public class TerrainLodControl extends AbstractControl {
                     cameraLocations.add(c.getLocation());\r
                 }\r
             }\r
-            terrain.update(cameraLocations, lodCalculator);\r
+            updateLOD(cameraLocations, lodCalculator);\r
+        }\r
+    }\r
+    \r
+    /**\r
+     * Call this when you remove the terrain or this control from the scene.\r
+     * It will clear up any threads it had.\r
+     */\r
+    public void detachAndCleanUpControl() {\r
+        if (executor != null)\r
+            executor.shutdownNow();\r
+        getSpatial().removeControl(this);\r
+    }\r
+\r
+    // do all of the LOD calculations\r
+    protected void updateLOD(List<Vector3f> locations, LodCalculator lodCalculator) {\r
+        if(getSpatial() == null){\r
+            return;\r
         }\r
+        \r
+        // update any existing ones that need updating\r
+        updateQuadLODs();\r
+\r
+        if (lodCalculator.isLodOff()) {\r
+            // we want to calculate the base lod at least once\r
+            if (lodOffCount == 1)\r
+                return;\r
+            else\r
+                lodOffCount++;\r
+        } else \r
+            lodOffCount = 0;\r
+        \r
+        if (lastCameraLocations != null) {\r
+            if (!forceUpdate && lastCameraLocationsTheSame(locations) && !lodCalculator.isLodOff())\r
+                return; // don't update if in same spot\r
+            else\r
+                lastCameraLocations = cloneVectorList(locations);\r
+            forceUpdate = false;\r
+        }\r
+        else {\r
+            lastCameraLocations = cloneVectorList(locations);\r
+            return;\r
+        }\r
+\r
+        if (isLodCalcRunning()) {\r
+            return;\r
+        }\r
+        setLodCalcRunning(true);\r
+\r
+        if (executor == null)\r
+            executor = createExecutorService();\r
+        \r
+        prepareTerrain();\r
+        \r
+        UpdateLOD updateLodThread = getLodThread(locations, lodCalculator);\r
+        indexer = executor.submit(updateLodThread);\r
+    }\r
+\r
+    /**\r
+     * Force the LOD to update instantly, does not wait for the camera to move.\r
+     * It will reset once it has updated.\r
+     */\r
+    public void forceUpdate() {\r
+        this.forceUpdate = true;\r
+    }\r
+    \r
+    protected void prepareTerrain() {\r
+        TerrainQuad terrain = (TerrainQuad)getSpatial();\r
+        terrain.cacheTerrainTransforms();// cache the terrain's world transforms so they can be accessed on the separate thread safely\r
+    }\r
+    \r
+    protected UpdateLOD getLodThread(List<Vector3f> locations, LodCalculator lodCalculator) {\r
+        return new UpdateLOD(locations, lodCalculator);\r
     }\r
 \r
+    /**\r
+     * Back on the ogl thread: update the terrain patch geometries\r
+     */\r
+    private void updateQuadLODs() {\r
+        if (indexer != null) {\r
+            if (indexer.isDone()) {\r
+                try {\r
+                    \r
+                    HashMap<String, UpdatedTerrainPatch> updated = indexer.get();\r
+                    if (updated != null) {\r
+                        // do the actual geometry update here\r
+                        for (UpdatedTerrainPatch utp : updated.values()) {\r
+                            utp.updateAll();\r
+                        }\r
+                    }\r
+                    \r
+                } catch (InterruptedException ex) {\r
+                    Logger.getLogger(TerrainLodControl.class.getName()).log(Level.SEVERE, null, ex);\r
+                } catch (ExecutionException ex) {\r
+                    Logger.getLogger(TerrainLodControl.class.getName()).log(Level.SEVERE, null, ex);\r
+                } finally {\r
+                    indexer = null;\r
+                }\r
+            }\r
+        }\r
+    }\r
+    \r
+    private boolean lastCameraLocationsTheSame(List<Vector3f> locations) {\r
+        boolean theSame = true;\r
+        for (Vector3f l : locations) {\r
+            for (Vector3f v : lastCameraLocations) {\r
+                if (!v.equals(l) ) {\r
+                    theSame = false;\r
+                    return false;\r
+                }\r
+            }\r
+        }\r
+        return theSame;\r
+    }\r
+    \r
+    protected synchronized boolean isLodCalcRunning() {\r
+        return lodCalcRunning.get();\r
+    }\r
+\r
+    protected synchronized void setLodCalcRunning(boolean running) {\r
+        lodCalcRunning.set(running);\r
+    }\r
+\r
+    private List<Vector3f> cloneVectorList(List<Vector3f> locations) {\r
+        List<Vector3f> cloned = new ArrayList<Vector3f>();\r
+        for(Vector3f l : locations)\r
+            cloned.add(l.clone());\r
+        return cloned;\r
+    }\r
+\r
+    \r
+    \r
+    \r
+    \r
+    \r
+    \r
     public Control cloneForSpatial(Spatial spatial) {\r
         if (spatial instanceof Terrain) {\r
             List<Camera> cameraClone = new ArrayList<Camera>();\r
@@ -186,6 +362,54 @@ public class TerrainLodControl extends AbstractControl {
             lodCalculator.turnOnLod();\r
         }\r
     }\r
+    \r
+    \r
+    /**\r
+     * Calculates the LOD of all child terrain patches.\r
+     */\r
+    protected class UpdateLOD implements Callable<HashMap<String,UpdatedTerrainPatch>> {\r
+        protected List<Vector3f> camLocations;\r
+        protected LodCalculator lodCalculator;\r
+\r
+        protected UpdateLOD(List<Vector3f> camLocations, LodCalculator lodCalculator) {\r
+            this.camLocations = camLocations;\r
+            this.lodCalculator = lodCalculator;\r
+        }\r
+\r
+        public HashMap<String, UpdatedTerrainPatch> call() throws Exception {\r
+            //long start = System.currentTimeMillis();\r
+            //if (isLodCalcRunning()) {\r
+            //    return null;\r
+            //}\r
+            setLodCalcRunning(true);\r
+\r
+            TerrainQuad terrainQuad = (TerrainQuad)getSpatial();\r
+            \r
+            // go through each patch and calculate its LOD based on camera distance\r
+            HashMap<String,UpdatedTerrainPatch> updated = new HashMap<String,UpdatedTerrainPatch>();\r
+            boolean lodChanged = terrainQuad.calculateLod(camLocations, updated, lodCalculator); // 'updated' gets populated here\r
+\r
+            if (!lodChanged) {\r
+                // not worth updating anything else since no one's LOD changed\r
+                setLodCalcRunning(false);\r
+                return null;\r
+            }\r
+            \r
+            \r
+            // then calculate its neighbour LOD values for seaming in the shader\r
+            terrainQuad.findNeighboursLod(updated);\r
+\r
+            terrainQuad.fixEdges(updated); // 'updated' can get added to here\r
+\r
+            terrainQuad.reIndexPages(updated, lodCalculator.usesVariableLod());\r
+\r
+            //setUpdateQuadLODs(updated); // set back to main ogl thread\r
+\r
+            setLodCalcRunning(false);\r
+            \r
+            return updated;\r
+        }\r
+    }\r
 \r
     @Override\r
     public void write(JmeExporter ex) throws IOException {\r
index bd35fcc..4ac811e 100644 (file)
@@ -1,5 +1,5 @@
 /*\r
- * Copyright (c) 2009-2010 jMonkeyEngine\r
+ * Copyright (c) 2009-2012 jMonkeyEngine\r
  * All rights reserved.\r
  *\r
  * Redistribution and use in source and binary forms, with or without\r
@@ -29,7 +29,6 @@
  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\r
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
  */\r
-\r
 package com.jme3.terrain.geomipmap;\r
 \r
 import com.jme3.bounding.BoundingBox;\r
@@ -42,27 +41,21 @@ import com.jme3.export.InputCapsule;
 import com.jme3.export.JmeExporter;\r
 import com.jme3.export.JmeImporter;\r
 import com.jme3.export.OutputCapsule;\r
-import java.nio.FloatBuffer;\r
-import java.nio.IntBuffer;\r
-import java.util.HashMap;\r
-\r
-import com.jme3.math.FastMath;\r
-import com.jme3.math.Ray;\r
-import com.jme3.math.Triangle;\r
-import com.jme3.math.Vector2f;\r
-import com.jme3.math.Vector3f;\r
+import com.jme3.math.*;\r
 import com.jme3.scene.Geometry;\r
 import com.jme3.scene.Mesh;\r
 import com.jme3.scene.VertexBuffer;\r
 import com.jme3.scene.VertexBuffer.Type;\r
+import com.jme3.scene.mesh.IndexBuffer;\r
 import com.jme3.terrain.geomipmap.TerrainQuad.LocationHeight;\r
-import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;\r
-import com.jme3.terrain.geomipmap.lodcalc.LodCalculator;\r
-import com.jme3.terrain.geomipmap.lodcalc.LodCalculatorFactory;\r
 import com.jme3.terrain.geomipmap.lodcalc.util.EntropyComputeUtil;\r
 import com.jme3.util.BufferUtils;\r
-import com.jme3.util.TangentBinormalGenerator;\r
 import java.io.IOException;\r
+import java.nio.Buffer;\r
+import java.nio.FloatBuffer;\r
+import java.nio.IntBuffer;\r
+import java.nio.ShortBuffer;\r
+import java.util.HashMap;\r
 import java.util.List;\r
 \r
 \r
@@ -89,7 +82,7 @@ import java.util.List;
 public class TerrainPatch extends Geometry {\r
 \r
     protected LODGeomap geomap;\r
-    protected int lod = -1; // this terrain patch's LOD\r
+    protected int lod = 0; // this terrain patch's LOD\r
     private int maxLod = -1;\r
     protected int previousLod = -1;\r
     protected int lodLeft, lodTop, lodRight, lodBottom; // it's neighbour's LODs\r
@@ -115,15 +108,20 @@ public class TerrainPatch extends Geometry {
     protected TerrainPatch leftNeighbour, topNeighbour, rightNeighbour, bottomNeighbour;\r
     protected boolean searchedForNeighboursAlready = false;\r
 \r
+    // these two vectors are calculated on the GL thread, but used in the outside LOD thread\r
+    protected Vector3f worldTranslationCached;\r
+    protected Vector3f worldScaleCached;\r
 \r
     protected float[] lodEntropy;\r
 \r
     public TerrainPatch() {\r
         super("TerrainPatch");\r
+        setBatchHint(BatchHint.Never);\r
     }\r
     \r
     public TerrainPatch(String name) {\r
         super(name);\r
+        setBatchHint(BatchHint.Never);\r
     }\r
 \r
     public TerrainPatch(String name, int size) {\r
@@ -178,6 +176,7 @@ public class TerrainPatch extends Geometry {
                     float[] heightMap, Vector3f origin, int totalSize,\r
                     Vector2f offset, float offsetAmount) {\r
         super(name);\r
+        setBatchHint(BatchHint.Never);\r
         this.size = size;\r
         this.stepScale = stepScale;\r
         this.totalSize = totalSize;\r
@@ -186,10 +185,7 @@ public class TerrainPatch extends Geometry {
 \r
         setLocalTranslation(origin);\r
 \r
-        FloatBuffer heightBuffer = BufferUtils.createFloatBuffer(size*size);\r
-        heightBuffer.put(heightMap);\r
-\r
-        geomap = new LODGeomap(size, heightBuffer);\r
+        geomap = new LODGeomap(size, heightMap);\r
         Mesh m = geomap.createMesh(stepScale, new Vector2f(1,1), offset, offsetAmount, totalSize, false);\r
         setMesh(m);\r
 \r
@@ -202,8 +198,13 @@ public class TerrainPatch extends Geometry {
         float[] entropies = new float[getMaxLod()+1];\r
         for (int i = 0; i <= getMaxLod(); i++){\r
             int curLod = (int) Math.pow(2, i);\r
-            IntBuffer buf = geomap.writeIndexArrayLodDiff(null, curLod, false, false, false, false);\r
-            entropies[i] = EntropyComputeUtil.computeLodEntropy(mesh, buf);\r
+            IndexBuffer idxB = geomap.writeIndexArrayLodDiff(curLod, false, false, false, false, totalSize);\r
+            Buffer ib;\r
+            if (idxB.getBuffer() instanceof IntBuffer)\r
+                ib = (IntBuffer)idxB.getBuffer();\r
+            else\r
+                ib = (ShortBuffer)idxB.getBuffer();\r
+            entropies[i] = EntropyComputeUtil.computeLodEntropy(mesh, ib);\r
         }\r
 \r
         lodEntropy = entropies;\r
@@ -216,8 +217,13 @@ public class TerrainPatch extends Geometry {
         return lodEntropy;\r
     }\r
 \r
+    @Deprecated\r
     public FloatBuffer getHeightmap() {\r
-        return geomap.getHeightData();\r
+        return BufferUtils.createFloatBuffer(geomap.getHeightArray());\r
+    }\r
+    \r
+    public float[] getHeightMap() {\r
+        return geomap.getHeightArray();\r
     }\r
 \r
     /**\r
@@ -225,7 +231,7 @@ public class TerrainPatch extends Geometry {
      * If the patch size is 32 then the returned value would be log2(32)-2 = 3\r
      * You can then use that value, 3, to see how many times you can divide 32 by 2\r
      * before the terrain gets too un-detailed (can't stitch it any further).\r
-     * @return\r
+     * @return the maximum LOD\r
      */\r
     public int getMaxLod() {\r
         if (maxLod < 0)\r
@@ -238,19 +244,25 @@ public class TerrainPatch extends Geometry {
 \r
         UpdatedTerrainPatch utp = updated.get(getName());\r
 \r
-        if (utp != null && (utp.isReIndexNeeded() || utp.isFixEdges()) ) {\r
+        if (utp != null && utp.isReIndexNeeded() ) {\r
             int pow = (int) Math.pow(2, utp.getNewLod());\r
             boolean left = utp.getLeftLod() > utp.getNewLod();\r
             boolean top = utp.getTopLod() > utp.getNewLod();\r
             boolean right = utp.getRightLod() > utp.getNewLod();\r
             boolean bottom = utp.getBottomLod() > utp.getNewLod();\r
 \r
-            IntBuffer ib = null;\r
+            IndexBuffer idxB;\r
             if (useVariableLod)\r
-                ib = geomap.writeIndexArrayLodVariable(null, 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()));\r
+                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);\r
             else\r
-                ib = geomap.writeIndexArrayLodDiff(null, pow, right, top, left, bottom);\r
-            utp.setNewIndexBuffer(ib);\r
+                idxB = geomap.writeIndexArrayLodDiff(pow, right, top, left, bottom, totalSize);\r
+            \r
+            Buffer b;\r
+            if (idxB.getBuffer() instanceof IntBuffer)\r
+                b = (IntBuffer)idxB.getBuffer();\r
+            else\r
+                b = (ShortBuffer)idxB.getBuffer();\r
+            utp.setNewIndexBuffer(b);\r
         }\r
 \r
     }\r
@@ -275,8 +287,8 @@ public class TerrainPatch extends Geometry {
     \r
     /**\r
      * Get the triangle of this geometry at the specified local coordinate.\r
-     * @param gridX local to the terrain patch\r
-     * @param gridY local to the terrain patch\r
+     * @param x local to the terrain patch\r
+     * @param z local to the terrain patch\r
      * @return the triangle in world coordinates, or null if the point does intersect this patch on the XZ axis\r
      */\r
     public Triangle getTriangle(float x, float z) {\r
@@ -285,8 +297,8 @@ public class TerrainPatch extends Geometry {
 \r
     /**\r
      * Get the triangles at the specified grid point. Probably only 2 triangles\r
-     * @param gridX local to the terrain patch\r
-     * @param gridY local to the terrain patch\r
+     * @param x local to the terrain patch\r
+     * @param z local to the terrain patch\r
      * @return the triangles in world coordinates, or null if the point does intersect this patch on the XZ axis\r
      */\r
     public Triangle[] getGridTriangles(float x, float z) {\r
@@ -300,10 +312,10 @@ public class TerrainPatch extends Geometry {
                 continue;\r
             int idx = lh.z * size + lh.x;\r
             if (overrideHeight) {\r
-                geomap.getHeightData().put(idx, lh.h);\r
+                geomap.getHeightArray()[idx] = lh.h;\r
             } else {\r
                 float h = getMesh().getFloatBuffer(Type.Position).get(idx*3+1);\r
-                geomap.getHeightData().put(idx, h+lh.h);\r
+                geomap.getHeightArray()[idx] = h+lh.h;\r
             }\r
             \r
         }\r
@@ -314,20 +326,32 @@ public class TerrainPatch extends Geometry {
     }\r
 \r
     /**\r
-     * recalculate all of this normal vectors in this terrain patch\r
+     * recalculate all of the normal vectors in this terrain patch\r
      */\r
     protected void updateNormals() {\r
         FloatBuffer newNormalBuffer = geomap.writeNormalArray(null, getWorldScale());\r
         getMesh().getBuffer(Type.Normal).updateData(newNormalBuffer);\r
         FloatBuffer newTangentBuffer = null;\r
         FloatBuffer newBinormalBuffer = null;\r
-        FloatBuffer[] tb = geomap.writeTangentArray(newTangentBuffer, newBinormalBuffer, (FloatBuffer)getMesh().getBuffer(Type.TexCoord).getData(), getWorldScale());\r
+        FloatBuffer[] tb = geomap.writeTangentArray(newNormalBuffer, newTangentBuffer, newBinormalBuffer, (FloatBuffer)getMesh().getBuffer(Type.TexCoord).getData(), getWorldScale());\r
         newTangentBuffer = tb[0];\r
         newBinormalBuffer = tb[1];\r
         getMesh().getBuffer(Type.Tangent).updateData(newTangentBuffer);\r
         getMesh().getBuffer(Type.Binormal).updateData(newBinormalBuffer);\r
     }\r
 \r
+    private void setInBuffer(Mesh mesh, int index, Vector3f normal, Vector3f tangent, Vector3f binormal) {\r
+        VertexBuffer NB = mesh.getBuffer(Type.Normal);\r
+        VertexBuffer TB = mesh.getBuffer(Type.Tangent);\r
+        VertexBuffer BB = mesh.getBuffer(Type.Binormal);\r
+        BufferUtils.setInBuffer(normal, (FloatBuffer)NB.getData(), index);\r
+        BufferUtils.setInBuffer(tangent, (FloatBuffer)TB.getData(), index);\r
+        BufferUtils.setInBuffer(binormal, (FloatBuffer)BB.getData(), index);\r
+        NB.setUpdateNeeded();\r
+        TB.setUpdateNeeded();\r
+        BB.setUpdateNeeded();\r
+    }\r
+    \r
     /**\r
      * Matches the normals along the edge of the patch with the neighbours.\r
      * Computes the normals for the right, bottom, left, and top edges of the\r
@@ -340,6 +364,9 @@ public class TerrainPatch extends Geometry {
      *          *---x---*\r
      *              |\r
      *              *\r
+     * It works across the right side of the patch, from the top down to \r
+     * the bottom. Then it works on the bottom side of the patch, from the\r
+     * left to the right.\r
      */\r
     protected void fixNormalEdges(TerrainPatch right,\r
                                 TerrainPatch bottom,\r
@@ -354,310 +381,171 @@ public class TerrainPatch extends Geometry {
         Vector3f rightPoint = new Vector3f();\r
         Vector3f leftPoint = new Vector3f();\r
         Vector3f topPoint = new Vector3f();\r
+\r
         Vector3f bottomPoint = new Vector3f();\r
-        Vector2f rootTex = new Vector2f();\r
-        Vector2f rightTex = new Vector2f();\r
-        Vector2f leftTex = new Vector2f();\r
-        Vector2f topTex = new Vector2f();\r
-        Vector2f bottomTex = new Vector2f();\r
-        Vector3f normal = new Vector3f();\r
+\r
         Vector3f tangent = new Vector3f();\r
+        Vector3f binormal = new Vector3f();\r
+        Vector3f normal = new Vector3f();\r
 \r
+        \r
         int s = this.getSize()-1;\r
         \r
-        if (right != null) { // right side\r
+        if (right != null) { // right side,    works its way down\r
             for (int i=0; i<s+1; i++) {\r
-                rootPoint.set(s, this.getHeightmapHeight(s,i), i);\r
-                leftPoint.set(s-1, this.getHeightmapHeight(s-1,i), i);\r
-                rightPoint.set(s+1, right.getHeightmapHeight(1,i), i);\r
-                this.getTex(s,i, rootTex);\r
-                this.getTex(s-1,i, leftTex);\r
-                right.getTex(1,i, rightTex);\r
-                if (i == 0) { // top\r
+                rootPoint.set(0, this.getHeightmapHeight(s,i), 0);\r
+                leftPoint.set(-1, this.getHeightmapHeight(s-1,i), 0);\r
+                rightPoint.set(1, right.getHeightmapHeight(1,i), 0);\r
+\r
+                if (i == 0) { // top point\r
+                    bottomPoint.set(0, this.getHeightmapHeight(s,i+1), 1);\r
+                    \r
                     if (top == null) {\r
-                        bottomPoint.set(s, this.getHeightmapHeight(s,i+1), i+1);\r
-                        this.getTex(s,i+1, bottomTex);\r
-                        averageNormalsTangents(null, rootPoint, leftPoint, bottomPoint, rightPoint, null, rootTex, leftTex, bottomTex, rightTex, normal, tangent);\r
-                        VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);\r
-                        VertexBuffer tpTB = this.getMesh().getBuffer(Type.Tangent);\r
-                        VertexBuffer rightNB = right.getMesh().getBuffer(Type.Normal);\r
-                        VertexBuffer rightTB = right.getMesh().getBuffer(Type.Tangent);\r
-                        BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), s);\r
-                        BufferUtils.setInBuffer(tangent, (FloatBuffer)tpTB.getData(), s);\r
-                        BufferUtils.setInBuffer(normal, (FloatBuffer)rightNB.getData(), 0);\r
-                        BufferUtils.setInBuffer(tangent, (FloatBuffer)rightTB.getData(), 0);\r
+                        averageNormalsTangents(null, rootPoint, leftPoint, bottomPoint, rightPoint,  normal, tangent, binormal);\r
+                        setInBuffer(this.getMesh(), s, normal, tangent, binormal);\r
+                        setInBuffer(right.getMesh(), 0, normal, tangent, binormal);\r
                     } else {\r
-                        topPoint.set(s, top.getHeightmapHeight(s,s-1), i-1);\r
-                        bottomPoint.set(s, this.getHeightmapHeight(s,i+1), i+1);\r
-                        top.getTex(s,s-1, topTex);\r
-                        this.getTex(s,i+1, bottomTex);\r
-                        averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, topTex, rootTex, leftTex, bottomTex, rightTex, normal, tangent);\r
-                        VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);\r
-                        VertexBuffer tpTB = this.getMesh().getBuffer(Type.Tangent);\r
-                        VertexBuffer rightNB = right.getMesh().getBuffer(Type.Normal);\r
-                        VertexBuffer rightTB = right.getMesh().getBuffer(Type.Tangent);\r
-                        VertexBuffer topNB = top.getMesh().getBuffer(Type.Normal);\r
-                        VertexBuffer topTB = top.getMesh().getBuffer(Type.Tangent);\r
-                        BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), s);\r
-                        BufferUtils.setInBuffer(tangent, (FloatBuffer)tpTB.getData(), s);\r
-                        BufferUtils.setInBuffer(normal, (FloatBuffer)rightNB.getData(), 0);\r
-                        BufferUtils.setInBuffer(tangent, (FloatBuffer)rightTB.getData(), 0);\r
-                        BufferUtils.setInBuffer(normal, (FloatBuffer)topNB.getData(), (s+1)*(s+1)-1);\r
-                        BufferUtils.setInBuffer(tangent, (FloatBuffer)topTB.getData(), (s+1)*(s+1)-1);\r
-                        // update top right corner\r
-                        /*if (topRight != null) {\r
-                            VertexBuffer topRightNB = topRight.getMesh().getBuffer(Type.Normal);\r
-                            VertexBuffer topRightTB = topRight.getMesh().getBuffer(Type.Tangent);\r
-                            BufferUtils.setInBuffer(normal, (FloatBuffer)topRightNB.getData(), (s+1)*s);\r
-                            BufferUtils.setInBuffer(tangent, (FloatBuffer)topRightTB.getData(), (s+1)*s);\r
-                            topRightNB.setUpdateNeeded();\r
-                            topRightTB.setUpdateNeeded();\r
-                        }*/\r
+                        topPoint.set(0, top.getHeightmapHeight(s,s-1), -1);\r
+                        \r
+                        averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint,normal, tangent, binormal);\r
+                        setInBuffer(this.getMesh(), s, normal, tangent, binormal);\r
+                        setInBuffer(right.getMesh(), 0, normal, tangent, binormal);\r
+                        setInBuffer(top.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal);\r
+                        \r
+                        if (topRight != null) {\r
+                    //        setInBuffer(topRight.getMesh(), (s+1)*s, normal, tangent, binormal);\r
+                        }\r
                     }\r
-                } else if (i == s) { // bottom\r
+                } else if (i == s) { // bottom point\r
+                    topPoint.set(0, this.getHeightmapHeight(s,s-1), -1);\r
+                    \r
                     if (bottom == null) {\r
-                        topPoint.set(s, this.getHeightmapHeight(s,i-1), i-1);\r
-                        this.getTex(s,i-1, topTex);\r
-                        averageNormalsTangents(topPoint, rootPoint, leftPoint, null, rightPoint, topTex, rootTex, leftTex, null, rightTex, normal, tangent);\r
-                        VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);\r
-                        VertexBuffer tpTB = this.getMesh().getBuffer(Type.Tangent);\r
-                        VertexBuffer rightNB = right.getMesh().getBuffer(Type.Normal);\r
-                        VertexBuffer rightTB = right.getMesh().getBuffer(Type.Tangent);\r
-                        BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), (s+1)*(i+1)-1);\r
-                        BufferUtils.setInBuffer(tangent, (FloatBuffer)tpTB.getData(), (s+1)*(i+1)-1);\r
-                        BufferUtils.setInBuffer(normal, (FloatBuffer)rightNB.getData(), (s+1)*(i));\r
-                        BufferUtils.setInBuffer(tangent, (FloatBuffer)rightTB.getData(), (s+1)*(i));\r
+                        averageNormalsTangents(topPoint, rootPoint, leftPoint, null, rightPoint, normal, tangent, binormal);\r
+                        setInBuffer(this.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal);\r
+                        setInBuffer(right.getMesh(), (s+1)*(s), normal, tangent, binormal);\r
                     } else {\r
-                        topPoint.set(s, this.getHeightmapHeight(s,i-1), i-1);\r
-                        bottomPoint.set(s, bottom.getHeightmapHeight(s,1), i+1);\r
-                        this.getTex(s,i-1, topTex);\r
-                        bottom.getTex(s,1, bottomTex);\r
-                        averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, topTex, rootTex, leftTex, bottomTex, rightTex, normal, tangent);\r
-                        VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);\r
-                        VertexBuffer tpTB = this.getMesh().getBuffer(Type.Tangent);\r
-                        VertexBuffer rightNB = right.getMesh().getBuffer(Type.Normal);\r
-                        VertexBuffer rightTB = right.getMesh().getBuffer(Type.Tangent);\r
-                        VertexBuffer downNB = bottom.getMesh().getBuffer(Type.Normal);\r
-                        VertexBuffer downTB = bottom.getMesh().getBuffer(Type.Tangent);\r
-                        BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), (s+1)*(s+1)-1);\r
-                        BufferUtils.setInBuffer(tangent, (FloatBuffer)tpTB.getData(), (s+1)*(s+1)-1);\r
-                        BufferUtils.setInBuffer(normal, (FloatBuffer)rightNB.getData(), (s+1)*s);\r
-                        BufferUtils.setInBuffer(tangent, (FloatBuffer)rightTB.getData(), (s+1)*s);\r
-                        BufferUtils.setInBuffer(normal, (FloatBuffer)downNB.getData(), s);\r
-                        BufferUtils.setInBuffer(tangent, (FloatBuffer)downTB.getData(), s);\r
-                        /*if (bottomRight != null) {\r
-                            VertexBuffer bottomRightNB = bottomRight.getMesh().getBuffer(Type.Normal);\r
-                            VertexBuffer bottomRightTB = bottomRight.getMesh().getBuffer(Type.Tangent);\r
-                            BufferUtils.setInBuffer(normal, (FloatBuffer)bottomRightNB.getData(), 0);\r
-                            BufferUtils.setInBuffer(tangent, (FloatBuffer)bottomRightTB.getData(), 0);\r
-                            bottomRightNB.setUpdateNeeded();\r
-                            bottomRightTB.setUpdateNeeded();\r
-                        }*/\r
-                        downNB.setUpdateNeeded();\r
-                        downTB.setUpdateNeeded();\r
+                        bottomPoint.set(0, bottom.getHeightmapHeight(s,1), 1);\r
+                        averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal);\r
+                        setInBuffer(this.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal);\r
+                        setInBuffer(right.getMesh(), (s+1)*s, normal, tangent, binormal);\r
+                        setInBuffer(bottom.getMesh(), s, normal, tangent, binormal);\r
+                        \r
+                        if (bottomRight != null) {\r
+                   //         setInBuffer(bottomRight.getMesh(), 0, normal, tangent, binormal);\r
+                        }\r
                     }\r
                 } else { // all in the middle\r
-                    topPoint.set(s, this.getHeightmapHeight(s,i-1), i-1);\r
-                    bottomPoint.set(s, this.getHeightmapHeight(s,i+1), i+1);\r
-                    this.getTex(s,i-1, topTex);\r
-                    this.getTex(s,i+1, bottomTex);\r
-                    averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, topTex, rootTex, leftTex, bottomTex, rightTex, normal, tangent);\r
-                    VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);\r
-                    VertexBuffer tpTB = this.getMesh().getBuffer(Type.Tangent);\r
-                    VertexBuffer rightNB = right.getMesh().getBuffer(Type.Normal);\r
-                    VertexBuffer rightTB = right.getMesh().getBuffer(Type.Tangent);\r
-                    BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), (s+1)*(i+1)-1);\r
-                    BufferUtils.setInBuffer(tangent, (FloatBuffer)tpTB.getData(), (s+1)*(i+1)-1);\r
-                    BufferUtils.setInBuffer(normal, (FloatBuffer)rightNB.getData(), (s+1)*(i));\r
-                    BufferUtils.setInBuffer(tangent, (FloatBuffer)rightTB.getData(), (s+1)*(i));\r
+                    topPoint.set(0, this.getHeightmapHeight(s,i-1), -1);\r
+                    bottomPoint.set(0, this.getHeightmapHeight(s,i+1), 1);\r
+                    averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal);\r
+                    setInBuffer(this.getMesh(), (s+1)*(i+1)-1, normal, tangent, binormal);\r
+                    setInBuffer(right.getMesh(), (s+1)*(i), normal, tangent, binormal);\r
                 }\r
             }\r
-            right.getMesh().getBuffer(Type.Normal).setUpdateNeeded();\r
-            right.getMesh().getBuffer(Type.Tangent).setUpdateNeeded();\r
         }\r
 \r
-        if (bottom != null) {\r
+        if (left != null) { // left side,    works its way down\r
             for (int i=0; i<s+1; i++) {\r
-                rootPoint.set(i, this.getHeightmapHeight(i,s), s);\r
-                topPoint.set(i, this.getHeightmapHeight(i,s-1), s-1);\r
-                bottomPoint.set(i, bottom.getHeightmapHeight(i,1), s+1);\r
-                this.getTex(i,s, rootTex);\r
-                this.getTex(i,s-1, topTex);\r
-                bottom.getTex(i,1, bottomTex);\r
-                if (i == 0) { // left\r
-                    /*if (left == null) {\r
-                        rightPoint.set(i+1, this.getHeight(i+1,s), s);\r
-                        averageNormalsTangents(v, t, indexes, topPoint, rootPoint, null, bottomPoint, rightPoint, topTex, rootTex, null, bottomTex, rightTex, normal, tangent);\r
-                        VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);\r
-                        VertexBuffer tpTB = this.getMesh().getBuffer(Type.Tangent);\r
-                        VertexBuffer downNB = bottom.getMesh().getBuffer(Type.Normal);\r
-                        VertexBuffer downTB = bottom.getMesh().getBuffer(Type.Tangent);\r
-                        BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), (s+1)*s);\r
-                        BufferUtils.setInBuffer(tangent, (FloatBuffer)tpTB.getData(), (s+1)*s);\r
-                        BufferUtils.setInBuffer(normal, (FloatBuffer)downNB.getData(), 0);\r
-                        BufferUtils.setInBuffer(tangent, (FloatBuffer)downTB.getData(), 0);\r
-                    } else {\r
-                        leftPoint.set(i-1, left.getHeight(s-1,s), s);\r
-                        rightPoint.set(i+1, this.getHeight(i+1,s), s);\r
-                        left.getTex(i-1,s, leftTex);\r
-                        this.getTex(i+1,s, rightTex);\r
-                        averageNormalsTangents(v, t, indexes, topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, topTex, rootTex, leftTex, bottomTex, rightTex, normal, tangent);\r
-                        VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);\r
-                        VertexBuffer tpTB = this.getMesh().getBuffer(Type.Tangent);\r
-                        VertexBuffer leftNB = left.getMesh().getBuffer(Type.Normal);\r
-                        VertexBuffer leftTB = left.getMesh().getBuffer(Type.Tangent);\r
-                        VertexBuffer downNB = bottom.getMesh().getBuffer(Type.Normal);\r
-                        VertexBuffer downTB = bottom.getMesh().getBuffer(Type.Tangent);\r
-                        BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), (s+1)*s);\r
-                        BufferUtils.setInBuffer(tangent, (FloatBuffer)tpTB.getData(), (s+1)*s);\r
-                        BufferUtils.setInBuffer(normal, (FloatBuffer)leftNB.getData(), (s+1)*(s+1)-1);\r
-                        BufferUtils.setInBuffer(tangent, (FloatBuffer)leftTB.getData(), (s+1)*(s+1)-1);\r
-                        BufferUtils.setInBuffer(normal, (FloatBuffer)downNB.getData(), 0);\r
-                        BufferUtils.setInBuffer(tangent, (FloatBuffer)downTB.getData(), 0);\r
-                    }*/\r
-                } else if (i == s) { // right\r
+                rootPoint.set(0, this.getHeightmapHeight(0,i), 0);\r
+                leftPoint.set(-1, left.getHeightmapHeight(s-1,i), 0);\r
+                rightPoint.set(1, this.getHeightmapHeight(1,i), 0);\r
+                \r
+                if (i == 0) { // top point\r
+                    bottomPoint.set(0, this.getHeightmapHeight(0,i+1), 1);\r
                     \r
-                    // handled by right side\r
+                    if (top == null) {\r
+                        averageNormalsTangents(null, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal);\r
+                        setInBuffer(this.getMesh(), 0, normal, tangent, binormal);\r
+                        setInBuffer(left.getMesh(), s, normal, tangent, binormal);\r
+                    } else {\r
+                        topPoint.set(0, top.getHeightmapHeight(0,s-1), -1);\r
+                        \r
+                        averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal);\r
+                        setInBuffer(this.getMesh(), 0, normal, tangent, binormal);\r
+                        setInBuffer(left.getMesh(), s, normal, tangent, binormal);\r
+                        setInBuffer(top.getMesh(), (s+1)*s, normal, tangent, binormal);\r
+                        \r
+                        if (topLeft != null) {\r
+                     //       setInBuffer(topLeft.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal);\r
+                        }\r
+                    }\r
+                } else if (i == s) { // bottom point\r
+                    topPoint.set(0, this.getHeightmapHeight(0,i-1), -1);\r
                     \r
+                    if (bottom == null) {\r
+                        averageNormalsTangents(topPoint, rootPoint, leftPoint, null, rightPoint, normal, tangent, binormal);\r
+                        setInBuffer(this.getMesh(), (s+1)*(s), normal, tangent, binormal);\r
+                        setInBuffer(left.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal);\r
+                    } else {\r
+                        bottomPoint.set(0, bottom.getHeightmapHeight(0,1), 1);\r
+                        \r
+                        averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal);\r
+                        setInBuffer(this.getMesh(), (s+1)*(s), normal, tangent, binormal);\r
+                        setInBuffer(left.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal);\r
+                        setInBuffer(bottom.getMesh(), 0, normal, tangent, binormal);\r
+                        \r
+                        if (bottomLeft != null) {\r
+                     //       setInBuffer(bottomLeft.getMesh(), s, normal, tangent, binormal);\r
+                        }\r
+                    }\r
                 } else { // all in the middle\r
-                    leftPoint.set(i-1, this.getHeightmapHeight(i-1,s), s);\r
-                    rightPoint.set(i+1, this.getHeightmapHeight(i+1,s), s);\r
-                    this.getTex(i-1,s, leftTex);\r
-                    this.getTex(i+1,s, rightTex);\r
-                    averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, topTex, rootTex, leftTex, bottomTex, rightTex, normal, tangent);\r
-                    VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);\r
-                    VertexBuffer tpTB = this.getMesh().getBuffer(Type.Tangent);\r
-                    VertexBuffer downNB = bottom.getMesh().getBuffer(Type.Normal);\r
-                    VertexBuffer downTB = bottom.getMesh().getBuffer(Type.Tangent);\r
-                    BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), (s+1)*(s)+i);\r
-                    BufferUtils.setInBuffer(tangent, (FloatBuffer)tpTB.getData(), (s+1)*(s)+i);\r
-                    BufferUtils.setInBuffer(normal, (FloatBuffer)downNB.getData(), i);\r
-                    BufferUtils.setInBuffer(tangent, (FloatBuffer)downTB.getData(), i);\r
+                    topPoint.set(0, this.getHeightmapHeight(0,i-1), -1);\r
+                    bottomPoint.set(0, this.getHeightmapHeight(0,i+1), 1);\r
+                    \r
+                    averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal);\r
+                    setInBuffer(this.getMesh(), (s+1)*(i), normal, tangent, binormal);\r
+                    setInBuffer(left.getMesh(), (s+1)*(i+1)-1, normal, tangent, binormal);\r
                 }\r
             }\r
-            bottom.getMesh().getBuffer(Type.Normal).setUpdateNeeded();\r
-            bottom.getMesh().getBuffer(Type.Tangent).setUpdateNeeded();\r
         }\r
 \r
-        if (left != null) { // left side\r
+        if (top != null) { // top side,    works its way right\r
             for (int i=0; i<s+1; i++) {\r
-                rootPoint.set(0, this.getHeightmapHeight(0,i), i);\r
-                leftPoint.set(-1, left.getHeightmapHeight(s-1,i), i);\r
-                rightPoint.set(1, this.getHeightmapHeight(1,i), i);\r
-                this.getTex(0,i, rootTex);\r
-                left.getTex(s-1,i, leftTex);\r
-                this.getTex(1,i, rightTex);\r
-                if (i == 0) { // top\r
-                    if (top == null) {\r
-                        bottomPoint.set(0, this.getHeightmapHeight(0,i+1), i+1);\r
-                        this.getTex(0,i+1, bottomTex);\r
-                        averageNormalsTangents(null, rootPoint, leftPoint, bottomPoint, rightPoint, null, rootTex, leftTex, bottomTex, rightTex, normal, tangent);\r
-                        VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);\r
-                        VertexBuffer tpTB = this.getMesh().getBuffer(Type.Tangent);\r
-                        VertexBuffer leftNB = left.getMesh().getBuffer(Type.Normal);\r
-                        VertexBuffer leftTB = left.getMesh().getBuffer(Type.Tangent);\r
-                        BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), 0);\r
-                        BufferUtils.setInBuffer(tangent, (FloatBuffer)tpTB.getData(), 0);\r
-                        BufferUtils.setInBuffer(normal, (FloatBuffer)leftNB.getData(), s+1);\r
-                        BufferUtils.setInBuffer(tangent, (FloatBuffer)leftTB.getData(), s+1);\r
-                    } else {\r
-                        topPoint.set(0, top.getHeightmapHeight(0,s-1), i-1);\r
-                        bottomPoint.set(0, this.getHeightmapHeight(0,i+1), i+1);\r
-                        top.getTex(0,i-1, topTex);\r
-                        this.getTex(0,i+1, bottomTex);\r
-                        averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, topTex, rootTex, leftTex, bottomTex, rightTex, normal, tangent);\r
-                        VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);\r
-                        VertexBuffer tpTB = this.getMesh().getBuffer(Type.Tangent);\r
-                        VertexBuffer leftNB = left.getMesh().getBuffer(Type.Normal);\r
-                        VertexBuffer leftTB = left.getMesh().getBuffer(Type.Tangent);\r
-                        VertexBuffer topNB = top.getMesh().getBuffer(Type.Normal);\r
-                        VertexBuffer topTB = top.getMesh().getBuffer(Type.Tangent);\r
-                        BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), 0);\r
-                        BufferUtils.setInBuffer(tangent, (FloatBuffer)tpTB.getData(), 0);\r
-                        BufferUtils.setInBuffer(normal, (FloatBuffer)leftNB.getData(), s);\r
-                        BufferUtils.setInBuffer(tangent, (FloatBuffer)leftTB.getData(), s);\r
-                        BufferUtils.setInBuffer(normal, (FloatBuffer)topNB.getData(), (s+1)*s);\r
-                        BufferUtils.setInBuffer(tangent, (FloatBuffer)topTB.getData(), (s+1)*s);\r
-                        /*if (topLeft != null) {\r
-                            VertexBuffer topLeftNB = topLeft.getMesh().getBuffer(Type.Normal);\r
-                            VertexBuffer topLeftTB = topLeft.getMesh().getBuffer(Type.Tangent);\r
-                            BufferUtils.setInBuffer(normal, (FloatBuffer)topLeftNB.getData(), (s+1)*(s+1)-1);\r
-                            BufferUtils.setInBuffer(tangent, (FloatBuffer)topLeftTB.getData(), (s+1)*(s+1)-1);\r
-                            topLeftNB.setUpdateNeeded();\r
-                            topLeftTB.setUpdateNeeded();\r
-                        }*/\r
-                        topNB.setUpdateNeeded();\r
-                    }\r
-                } else if (i == s) { // bottom\r
-\r
-                    // handled by bottom\r
-\r
-                    /*if (bottomLeft != null) {\r
-                        VertexBuffer bottomLeftNB = bottomLeft.getMesh().getBuffer(Type.Normal);\r
-                        VertexBuffer bottomLeftTB = bottomLeft.getMesh().getBuffer(Type.Tangent);\r
-                        BufferUtils.setInBuffer(normal, (FloatBuffer)bottomLeftNB.getData(), s);\r
-                        BufferUtils.setInBuffer(tangent, (FloatBuffer)bottomLeftTB.getData(), s);\r
-                        bottomLeftNB.setUpdateNeeded();\r
-                        bottomLeftTB.setUpdateNeeded();\r
-                    }*/\r
-\r
+                rootPoint.set(0, this.getHeightmapHeight(i,0), 0);\r
+                topPoint.set(0, top.getHeightmapHeight(i,s-1), -1);\r
+                bottomPoint.set(0, this.getHeightmapHeight(i,1), 1);\r
+                \r
+                if (i == 0) { // left corner\r
+                    // handled by left side pass\r
+                    \r
+                } else if (i == s) { // right corner\r
+                    \r
+                    // handled by this patch when it does its right side\r
+                    \r
                 } else { // all in the middle\r
-                    topPoint.set(0, this.getHeightmapHeight(0,i-1), i-1);\r
-                    bottomPoint.set(0, this.getHeightmapHeight(0,i+1), i+1);\r
-                    this.getTex(0,i-1, topTex);\r
-                    this.getTex(0,i+1, bottomTex);\r
-                    averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, topTex, rootTex, leftTex, bottomTex, rightTex, normal, tangent);\r
-                    VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);\r
-                    VertexBuffer tpTB = this.getMesh().getBuffer(Type.Tangent);\r
-                    VertexBuffer leftNB = left.getMesh().getBuffer(Type.Normal);\r
-                    VertexBuffer leftTB = left.getMesh().getBuffer(Type.Tangent);\r
-                    BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), (s+1)*(i));\r
-                    BufferUtils.setInBuffer(tangent, (FloatBuffer)tpTB.getData(), (s+1)*(i));\r
-                    BufferUtils.setInBuffer(normal, (FloatBuffer)leftNB.getData(), (s+1)*(i+1)-1);\r
-                    BufferUtils.setInBuffer(tangent, (FloatBuffer)leftTB.getData(), (s+1)*(i+1)-1);\r
+                    leftPoint.set(-1, this.getHeightmapHeight(i-1,0), 0);\r
+                    rightPoint.set(1, this.getHeightmapHeight(i+1,0), 0);\r
+                    averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal);\r
+                    setInBuffer(this.getMesh(), i, normal, tangent, binormal);\r
+                    setInBuffer(top.getMesh(), (s+1)*(s)+i, normal, tangent, binormal);\r
                 }\r
             }\r
-            left.getMesh().getBuffer(Type.Normal).setUpdateNeeded();\r
-            left.getMesh().getBuffer(Type.Tangent).setUpdateNeeded();\r
+            \r
         }\r
-\r
-        if (top != null) { // top side\r
+        \r
+        if (bottom != null) { // bottom side,    works its way right\r
             for (int i=0; i<s+1; i++) {\r
-                rootPoint.set(i, this.getHeightmapHeight(i,0), 0);\r
-                topPoint.set(i, top.getHeightmapHeight(i,s-1), -1);\r
-                bottomPoint.set(i, this.getHeightmapHeight(i,1), 1);\r
-                this.getTex(i,s, rootTex);\r
-                top.getTex(i,s-1, topTex);\r
-                this.getTex(i,1, bottomTex);\r
-                if (i == 0) { // left\r
-\r
-                    // handled by left side\r
+                rootPoint.set(0, this.getHeightmapHeight(i,s), 0);\r
+                topPoint.set(0, this.getHeightmapHeight(i,s-1), -1);\r
+                bottomPoint.set(0, bottom.getHeightmapHeight(i,1), 1);\r
 \r
+                if (i == 0) { // left\r
+                    // handled by the left side pass\r
+                    \r
                 } else if (i == s) { // right\r
-\r
-                    // handled by right side\r
-\r
+                    \r
+                    // handled by the right side pass\r
+                    \r
                 } else { // all in the middle\r
-                    leftPoint.set(i-1, this.getHeightmapHeight(i-1,0), 0);\r
-                    rightPoint.set(i+1, this.getHeightmapHeight(i+1,0), 0);\r
-                    this.getTex(i-1,0, leftTex);\r
-                    this.getTex(i+1,0, rightTex);\r
-                    averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, topTex, rootTex, leftTex, bottomTex, rightTex, normal, tangent);\r
-                    VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal);\r
-                    VertexBuffer tpTB = this.getMesh().getBuffer(Type.Tangent);\r
-                    VertexBuffer topNB = top.getMesh().getBuffer(Type.Normal);\r
-                    VertexBuffer topTB = top.getMesh().getBuffer(Type.Tangent);\r
-                    BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), i);\r
-                    BufferUtils.setInBuffer(tangent, (FloatBuffer)tpTB.getData(), i);\r
-                    BufferUtils.setInBuffer(normal, (FloatBuffer)topNB.getData(), (s+1)*(s)+i);\r
-                    BufferUtils.setInBuffer(tangent, (FloatBuffer)topTB.getData(), (s+1)*(s)+i);\r
+                    leftPoint.set(-1, this.getHeightmapHeight(i-1,s), 0);\r
+                    rightPoint.set(1, this.getHeightmapHeight(i+1,s), 0);\r
+                    averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal);\r
+                    setInBuffer(this.getMesh(), (s+1)*(s)+i, normal, tangent, binormal);\r
+                    setInBuffer(bottom.getMesh(), i, normal, tangent, binormal);\r
                 }\r
             }\r
-            top.getMesh().getBuffer(Type.Normal).setUpdateNeeded();\r
+            \r
         }\r
-\r
-        this.getMesh().getBuffer(Type.Normal).setUpdateNeeded();\r
-        this.getMesh().getBuffer(Type.Tangent).setUpdateNeeded();\r
     }\r
 \r
     protected void averageNormalsTangents(\r
@@ -666,38 +554,36 @@ public class TerrainPatch extends Geometry {
             Vector3f leftPoint, \r
             Vector3f bottomPoint, \r
             Vector3f rightPoint,\r
-            Vector2f topTex,\r
-            Vector2f rootTex,\r
-            Vector2f leftTex,\r
-            Vector2f bottomTex,\r
-            Vector2f rightTex,\r
             Vector3f normal,\r
-            Vector3f tangent)\r
+            Vector3f tangent,\r
+            Vector3f binormal)\r
     {\r
         Vector3f scale = getWorldScale();\r
         \r
-        Vector3f n1 = Vector3f.ZERO;\r
+        Vector3f n1 = new Vector3f(0,0,0);\r
         if (topPoint != null && leftPoint != null) {\r
-            n1 = calculateNormal(topPoint.mult(scale), rootPoint.mult(scale), leftPoint.mult(scale));\r
+            n1.set(calculateNormal(topPoint.mult(scale), rootPoint.mult(scale), leftPoint.mult(scale)));\r
         }\r
-        Vector3f n2 = Vector3f.ZERO;\r
+        Vector3f n2 = new Vector3f(0,0,0);\r
         if (leftPoint != null && bottomPoint != null) {\r
-            n2 = calculateNormal(leftPoint.mult(scale), rootPoint.mult(scale), bottomPoint.mult(scale));\r
+            n2.set(calculateNormal(leftPoint.mult(scale), rootPoint.mult(scale), bottomPoint.mult(scale)));\r
         }\r
-        Vector3f n3 = Vector3f.ZERO;\r
+        Vector3f n3 = new Vector3f(0,0,0);\r
         if (rightPoint != null && bottomPoint != null) {\r
-            n3 = calculateNormal(bottomPoint.mult(scale), rootPoint.mult(scale), rightPoint.mult(scale));\r
+            n3.set(calculateNormal(bottomPoint.mult(scale), rootPoint.mult(scale), rightPoint.mult(scale)));\r
         }\r
-        Vector3f n4 = Vector3f.ZERO;\r
+        Vector3f n4 = new Vector3f(0,0,0);\r
         if (rightPoint != null && topPoint != null) {\r
-            n4 = calculateNormal(rightPoint.mult(scale), rootPoint.mult(scale), topPoint.mult(scale));\r
+            n4.set(calculateNormal(rightPoint.mult(scale), rootPoint.mult(scale), topPoint.mult(scale)));\r
         }\r
         \r
-        Vector3f binormal = new Vector3f();\r
-        if (topPoint != null && rightPoint != null)\r
-            LODGeomap.calculateTangent(new Vector3f[]{rootPoint.mult(scale),rightPoint.mult(scale),topPoint.mult(scale)}, new Vector2f[]{rootTex,rightTex,topTex}, tangent, binormal);\r
+        //if (bottomPoint != null && rightPoint != null && rootTex != null && rightTex != null && bottomTex != null)\r
+        //    LODGeomap.calculateTangent(new Vector3f[]{rootPoint.mult(scale),rightPoint.mult(scale),bottomPoint.mult(scale)}, new Vector2f[]{rootTex,rightTex,bottomTex}, tangent, binormal);\r
 \r
-        normal.set(n1.add(n2).add(n3).add(n4).normalizeLocal());\r
+        normal.set(n1.add(n2).add(n3).add(n4).normalize());\r
+        \r
+        tangent.set(normal.cross(new Vector3f(0,0,1)).normalize());\r
+        binormal.set(new Vector3f(1,0,0).cross(normal).normalize());\r
     }\r
 \r
     private Vector3f calculateNormal(Vector3f firstPoint, Vector3f rootPoint, Vector3f secondPoint) {\r
@@ -720,6 +606,10 @@ public class TerrainPatch extends Geometry {
         return normal;\r
     }\r
 \r
+    protected float getHeight(int x, int z, float xm, float zm) {\r
+        return geomap.getHeight(x,z,xm,zm);\r
+    }\r
+    \r
     /**\r
      * Locks the mesh (sets it static) to improve performance.\r
      * But it it not editable then. Set unlock to make it editable.\r
@@ -1057,7 +947,7 @@ public class TerrainPatch extends Geometry {
         //clone.lodCalculator = lodCalculator.clone();\r
         //clone.lodCalculator.setTerrainPatch(clone);\r
         //clone.setLodCalculator(lodCalculatorFactory.clone());\r
-        clone.geomap = new LODGeomap(size, geomap.getHeightData());\r
+        clone.geomap = new LODGeomap(size, geomap.getHeightArray());\r
         clone.setLocalTranslation(getLocalTranslation().clone());\r
         Mesh m = clone.geomap.createMesh(clone.stepScale, Vector2f.UNIT_XY, clone.offset, clone.offsetAmount, clone.totalSize, false);\r
         clone.setMesh(m);\r
@@ -1075,6 +965,44 @@ public class TerrainPatch extends Geometry {
         }\r
     }\r
 \r
+    /**\r
+     * Caches the transforms (except rotation) so the LOD calculator,\r
+     * which runs on a separate thread, can access them safely.\r
+     */\r
+    protected void cacheTerrainTransforms() {\r
+        this.worldScaleCached = getWorldScale().clone();\r
+        this.worldTranslationCached = getWorldTranslation().clone();\r
+    }\r
+\r
+    public Vector3f getWorldScaleCached() {\r
+        return worldScaleCached;\r
+    }\r
+\r
+    public Vector3f getWorldTranslationCached() {\r
+        return worldTranslationCached;\r
+    }\r
+\r
+    /**\r
+     * Removes any references when the terrain is being removed.\r
+     */\r
+    protected void clearCaches() {\r
+        if (leftNeighbour != null) {\r
+            leftNeighbour.rightNeighbour = null;\r
+            leftNeighbour = null;\r
+        }\r
+        if (rightNeighbour != null) {\r
+            rightNeighbour.leftNeighbour = null;\r
+            rightNeighbour = null;\r
+        }\r
+        if (topNeighbour != null) {\r
+            topNeighbour.bottomNeighbour = null;\r
+            topNeighbour = null;\r
+        }\r
+        if (bottomNeighbour != null) {\r
+            bottomNeighbour.topNeighbour = null;\r
+            bottomNeighbour = null;\r
+        }\r
+    }\r
 \r
 \r
 }\r
index cc8755d..8c9e0bc 100644 (file)
@@ -1,5 +1,5 @@
 /*\r
- * Copyright (c) 2009-2010 jMonkeyEngine\r
+ * Copyright (c) 2009-2012 jMonkeyEngine\r
  * All rights reserved.\r
  *\r
  * Redistribution and use in source and binary forms, with or without\r
  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\r
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
  */\r
-\r
 package com.jme3.terrain.geomipmap;\r
 \r
-import com.jme3.material.Material;\r
-import java.io.IOException;\r
-import java.util.HashMap;\r
-import java.util.concurrent.ExecutorService;\r
-import java.util.concurrent.Executors;\r
-import java.util.concurrent.ThreadFactory;\r
-\r
 import com.jme3.bounding.BoundingBox;\r
 import com.jme3.bounding.BoundingVolume;\r
 import com.jme3.collision.Collidable;\r
@@ -47,6 +39,7 @@ import com.jme3.export.InputCapsule;
 import com.jme3.export.JmeExporter;\r
 import com.jme3.export.JmeImporter;\r
 import com.jme3.export.OutputCapsule;\r
+import com.jme3.material.Material;\r
 import com.jme3.math.FastMath;\r
 import com.jme3.math.Ray;\r
 import com.jme3.math.Vector2f;\r
@@ -57,32 +50,50 @@ import com.jme3.scene.Spatial;
 import com.jme3.scene.debug.WireBox;\r
 import com.jme3.terrain.ProgressMonitor;\r
 import com.jme3.terrain.Terrain;\r
-import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;\r
 import com.jme3.terrain.geomipmap.lodcalc.LodCalculator;\r
-import com.jme3.terrain.geomipmap.lodcalc.SimpleLodThreshold;\r
 import com.jme3.terrain.geomipmap.picking.BresenhamTerrainPicker;\r
 import com.jme3.terrain.geomipmap.picking.TerrainPickData;\r
 import com.jme3.terrain.geomipmap.picking.TerrainPicker;\r
-import com.jme3.util.BufferUtils;\r
 import com.jme3.util.TangentBinormalGenerator;\r
+import java.io.IOException;\r
 import java.util.ArrayList;\r
+import java.util.HashMap;\r
 import java.util.List;\r
 import java.util.Map;\r
 import java.util.logging.Level;\r
 import java.util.logging.Logger;\r
 \r
 /**\r
+ * <p>\r
+ * TerrainQuad is a heightfield-based terrain system. Heightfield terrain is fast and can\r
+ * render large areas, and allows for easy Level of Detail control. However it does not\r
+ * permit caves easily.\r
+ * TerrainQuad is a quad tree, meaning that the root quad has four children, and each of\r
+ * those children have four children. All the way down until you reach the bottom, the actual\r
+ * geometry, the TerrainPatches.\r
+ * If you look at a TerrainQuad in wireframe mode with the TerrainLODControl attached, you will\r
+ * see blocks that change their LOD level together; these are the TerrainPatches. The TerrainQuad\r
+ * is just an organizational structure for the TerrainPatches so patches that are not in the\r
+ * view frustum get culled quickly.\r
+ * TerrainQuads size are a power of 2, plus 1. So 513x513, or 1025x1025 etc.\r
+ * Each point in the terrain is one unit apart from its neighbour. So a 513x513 terrain\r
+ * will be 513 units wide and 513 units long.\r
+ * Patch size can be specified on the terrain. This sets how large each geometry (TerrainPatch)\r
+ * is. It also must be a power of 2 plus 1 so the terrain can be subdivided equally.\r
+ * </p>\r
+ * <p>\r
+ * The height of the terrain can be modified at runtime using setHeight()\r
+ * </p>\r
+ * <p>\r
  * A terrain quad is a node in the quad tree of the terrain system.\r
  * The root terrain quad will be the only one that receives the update() call every frame\r
  * and it will determine if there has been any LOD change.\r
- *\r
+ * </p><p>\r
  * The leaves of the terrain quad tree are Terrain Patches. These have the real geometry mesh.\r
- *\r
- * \r
+ * </p><p>\r
  * Heightmap coordinates start from the bottom left of the world and work towards the\r
- * top right. This will seem upside down, but the texture coordinates compensate; and\r
- * it allows you to easily get the heightmap values at real world X,Z coordinates.\r
- * \r
+ * top right.\r
+ * </p><pre>\r
  *  +x\r
  *  ^\r
  *  | ......N = length of heightmap\r
@@ -91,11 +102,10 @@ import java.util.logging.Logger;
  *  | 0.....:\r
  *  +---------> +z\r
  * (world coordinates)\r
- * \r
+ * </pre>\r
  * @author Brent Owens\r
  */\r
 public class TerrainQuad extends Node implements Terrain {\r
-\r
     protected Vector2f offset;\r
 \r
     protected int totalSize; // the size of this entire terrain tree (on one side)\r
@@ -108,59 +118,98 @@ public class TerrainQuad extends Node implements Terrain {
 \r
     protected float offsetAmount;\r
 \r
-    protected int quadrant = 1; // 1=upper left, 2=lower left, 3=upper right, 4=lower right\r
-\r
-    //protected LodCalculatorFactory lodCalculatorFactory;\r
-    //protected LodCalculator lodCalculator;\r
-    \r
-    protected List<Vector3f> lastCameraLocations; // used for LOD calc\r
-    private boolean lodCalcRunning = false;\r
-    private int lodOffCount = 0;\r
+    protected int quadrant = 0; // 1=upper left, 2=lower left, 3=upper right, 4=lower right\r
     private int maxLod = -1;\r
-    private HashMap<String,UpdatedTerrainPatch> updatedPatches;\r
-    private final Object updatePatchesLock = new Object();\r
     private BoundingBox affectedAreaBBox; // only set in the root quad\r
 \r
     private TerrainPicker picker;\r
     private Vector3f lastScale = Vector3f.UNIT_XYZ;\r
 \r
-    protected ExecutorService executor;\r
-\r
-    protected ExecutorService createExecutorService() {\r
-        return Executors.newSingleThreadExecutor(new ThreadFactory() {\r
-            public Thread newThread(Runnable r) {\r
-                Thread th = new Thread(r);\r
-                th.setName("jME Terrain Thread");\r
-                th.setDaemon(true);\r
-                return th;\r
-            }\r
-        });\r
-    }\r
-\r
+    protected NeighbourFinder neighbourFinder;\r
+    \r
     public TerrainQuad() {\r
         super("Terrain");\r
     }\r
 \r
+    /**\r
+     * Creates a terrain with:\r
+     * <ul>\r
+     * <li>the total, real-world, size of the terrain</li>\r
+     * <li>the patchSize, or the size of each geometry tile of the terrain</li>\r
+     * <li>the heightmap that defines the height of the terrain</li>\r
+     * </ul>\r
+     * <p>\r
+     * A TerrainQuad of totalSize 513x513 will be 513 units wide and 513 units long.\r
+     * PatchSize is just used to subdivide the terrain into tiles that can be culled.\r
+     * </p>\r
+     * @param name the name of the scene element. This is required for\r
+     * identification and comparison purposes.\r
+     * @param patchSize size of the individual patches (geometry). Power of 2 plus 1, \r
+     * must be smaller than totalSize. (eg. 33, 65...)\r
+     * @param totalSize the size of this entire terrain (on one side). Power of 2 plus 1 \r
+     * (eg. 513, 1025, 2049...)\r
+     * @param heightMap The height map to generate the terrain from (a flat\r
+     * height map will be generated if this is null). The size of one side of the heightmap \r
+     * must match the totalSize. So a 513x513 heightmap is needed for a terrain with totalSize of 513.\r
+     */\r
     public TerrainQuad(String name, int patchSize, int totalSize, float[] heightMap) {\r
         this(name, patchSize, totalSize, Vector3f.UNIT_XYZ, heightMap);\r
+                \r
+        affectedAreaBBox = new BoundingBox(new Vector3f(0,0,0), size*2, Float.MAX_VALUE, size*2);\r
+        fixNormalEdges(affectedAreaBBox);\r
+        addControl(new NormalRecalcControl(this));\r
     }\r
     \r
+    /**\r
+     * \r
+     * @param name the name of the scene element. This is required for\r
+     * identification and comparison purposes.\r
+     * @param patchSize size of the individual patches\r
+     * @param quadSize\r
+     * @param totalSize the size of this entire terrain tree (on one side)\r
+     * @param heightMap The height map to generate the terrain from (a flat\r
+     * height map will be generated if this is null)\r
+     */\r
+    @Deprecated\r
     public TerrainQuad(String name, int patchSize, int quadSize, int totalSize, float[] heightMap) {\r
-        this(name, patchSize, totalSize, Vector3f.UNIT_XYZ, heightMap);\r
+        this(name, patchSize, totalSize, quadSize, Vector3f.UNIT_XYZ, heightMap);\r
     }\r
 \r
+    /**\r
+     * \r
+     * @param name the name of the scene element. This is required for\r
+     * identification and comparison purposes.\r
+     * @param patchSize size of the individual patches\r
+     * @param size size of this quad, can be between totalSize and patchSize\r
+     * @param scale\r
+     * @param heightMap The height map to generate the terrain from (a flat\r
+     * height map will be generated if this is null)\r
+     */\r
+    @Deprecated\r
     public TerrainQuad(String name, int patchSize, int size, Vector3f scale, float[] heightMap) {\r
         this(name, patchSize, size, scale, heightMap, size, new Vector2f(), 0);\r
-        affectedAreaBBox = new BoundingBox(new Vector3f(0,0,0), size*2, Float.MAX_VALUE, size*2);\r
-        fixNormalEdges(affectedAreaBBox);\r
-        addControl(new NormalRecalcControl(this));\r
+        //affectedAreaBBox = new BoundingBox(new Vector3f(0,0,0), size*2, Float.MAX_VALUE, size*2);\r
+        //fixNormalEdges(affectedAreaBBox);\r
+        //addControl(new NormalRecalcControl(this));\r
     }\r
     \r
+    /**\r
+     * \r
+     * @param name the name of the scene element. This is required for\r
+     * identification and comparison purposes.\r
+     * @param patchSize size of the individual patches\r
+     * @param totalSize the size of this entire terrain tree (on one side)\r
+     * @param quadSize\r
+     * @param scale\r
+     * @param heightMap The height map to generate the terrain from (a flat\r
+     * height map will be generated if this is null)\r
+     */\r
+    @Deprecated\r
     public TerrainQuad(String name, int patchSize, int totalSize, int quadSize, Vector3f scale, float[] heightMap) {\r
         this(name, patchSize, quadSize, scale, heightMap, totalSize, new Vector2f(), 0);\r
-        affectedAreaBBox = new BoundingBox(new Vector3f(0,0,0), totalSize*2, Float.MAX_VALUE, totalSize*2);\r
-        fixNormalEdges(affectedAreaBBox);\r
-        addControl(new NormalRecalcControl(this));\r
+        //affectedAreaBBox = new BoundingBox(new Vector3f(0,0,0), totalSize*2, Float.MAX_VALUE, totalSize*2);\r
+        //fixNormalEdges(affectedAreaBBox);\r
+        //addControl(new NormalRecalcControl(this));\r
     }\r
 \r
     protected TerrainQuad(String name, int patchSize, int quadSize,\r
@@ -185,26 +234,22 @@ public class TerrainQuad extends Node implements Terrain {
         this.size = quadSize;\r
         this.patchSize = patchSize;\r
         this.stepScale = scale;\r
-        //this.lodCalculatorFactory = lodCalculatorFactory;\r
-        //this.lodCalculator = lodCalculator;\r
         split(patchSize, heightMap);\r
     }\r
 \r
-    /*public void setLodCalculatorFactory(LodCalculatorFactory lodCalculatorFactory) {\r
-        if (children != null) {\r
-            for (int i = children.size(); --i >= 0;) {\r
-                Spatial child = children.get(i);\r
-                if (child instanceof TerrainQuad) {\r
-                    ((TerrainQuad) child).setLodCalculatorFactory(lodCalculatorFactory);\r
-                } else if (child instanceof TerrainPatch) {\r
-                    ((TerrainPatch) child).setLodCalculator(lodCalculatorFactory.createCalculator((TerrainPatch) child));\r
-                }\r
-            }\r
-        }\r
-    }*/\r
-\r
+    public void setNeighbourFinder(NeighbourFinder neighbourFinder) {\r
+        this.neighbourFinder = neighbourFinder;\r
+        resetCachedNeighbours();\r
+    }\r
 \r
     /**\r
+     * Forces the recalculation of all normals on the terrain.\r
+     */\r
+    public void recalculateAllNormals() {\r
+        affectedAreaBBox = new BoundingBox(new Vector3f(0,0,0), totalSize*2, Float.MAX_VALUE, totalSize*2);\r
+    }\r
+    \r
+    /**\r
      * Create just a flat heightmap\r
      */\r
     private float[] generateDefaultHeightMap(int size) {\r
@@ -212,16 +257,6 @@ public class TerrainQuad extends Node implements Terrain {
         return heightMap;\r
     }\r
 \r
-     /**\r
-      * Call from the update() method of a terrain controller to update\r
-      * the LOD values of each patch.\r
-      * This will perform the geometry calculation in a background thread and\r
-      * do the actual update on the opengl thread.\r
-      */\r
-    public void update(List<Vector3f> locations, LodCalculator lodCalculator) {\r
-        updateLOD(locations, lodCalculator);\r
-    }\r
-\r
     /**\r
      * update the normals if there were any height changes recently.\r
      * Should only be called on the root quad\r
@@ -236,73 +271,20 @@ public class TerrainQuad extends Node implements Terrain {
             setNormalRecalcNeeded(null); // set to false\r
         }\r
     }\r
-\r
-    // do all of the LOD calculations\r
-    protected void updateLOD(List<Vector3f> locations, LodCalculator lodCalculator) {\r
-        // update any existing ones that need updating\r
-        updateQuadLODs();\r
-\r
-        if (lodCalculator.isLodOff()) {\r
-            // we want to calculate the base lod at least once\r
-            if (lodOffCount == 1)\r
-                return;\r
-            else\r
-                lodOffCount++;\r
-        } else \r
-            lodOffCount = 0;\r
-        \r
-        if (lastCameraLocations != null) {\r
-            if (lastCameraLocationsTheSame(locations) && !lodCalculator.isLodOff())\r
-                return; // don't update if in same spot\r
-            else\r
-                lastCameraLocations = cloneVectorList(locations);\r
-        }\r
-        else {\r
-            lastCameraLocations = cloneVectorList(locations);\r
-            return;\r
-        }\r
-\r
-        if (isLodCalcRunning()) {\r
-            return;\r
-        }\r
-\r
-        if (getParent() instanceof TerrainQuad) {\r
-            return; // we just want the root quad to perform this.\r
-        }\r
-\r
-        if (executor == null)\r
-            executor = createExecutorService();\r
-        \r
-        UpdateLOD updateLodThread = new UpdateLOD(locations, lodCalculator);\r
-        executor.execute(updateLodThread);\r
-    }\r
-\r
-    private synchronized boolean isLodCalcRunning() {\r
-        return lodCalcRunning;\r
-    }\r
-\r
-    private synchronized void setLodCalcRunning(boolean running) {\r
-        lodCalcRunning = running;\r
-    }\r
-\r
-    private List<Vector3f> cloneVectorList(List<Vector3f> locations) {\r
-        List<Vector3f> cloned = new ArrayList<Vector3f>();\r
-        for(Vector3f l : locations)\r
-            cloned.add(l.clone());\r
-        return cloned;\r
-    }\r
-\r
-    private boolean lastCameraLocationsTheSame(List<Vector3f> locations) {\r
-        boolean theSame = true;\r
-        for (Vector3f l : locations) {\r
-            for (Vector3f v : lastCameraLocations) {\r
-                if (!v.equals(l) ) {\r
-                    theSame = false;\r
-                    return false;\r
-                }\r
+    \r
+    /**\r
+     * Caches the transforms (except rotation) so the LOD calculator,\r
+     * which runs on a separate thread, can access them safely.\r
+     */\r
+    protected void cacheTerrainTransforms() {\r
+        for (int i = children.size(); --i >= 0;) {\r
+            Spatial child = children.get(i);\r
+            if (child instanceof TerrainQuad) {\r
+                ((TerrainQuad) child).cacheTerrainTransforms();\r
+            } else if (child instanceof TerrainPatch) {\r
+                ((TerrainPatch) child).cacheTerrainTransforms();\r
             }\r
         }\r
-        return theSame;\r
     }\r
 \r
     private int collideWithRay(Ray ray, CollisionResults results) {\r
@@ -310,12 +292,23 @@ public class TerrainQuad extends Node implements Terrain {
             picker = new BresenhamTerrainPicker(this);\r
 \r
         Vector3f intersection = picker.getTerrainIntersection(ray, results);\r
-        if (intersection != null)\r
-            return 1;\r
-        else\r
+        if (intersection != null) {\r
+            if (ray.getLimit() < Float.POSITIVE_INFINITY) {\r
+                if (results.getClosestCollision().getDistance() <= ray.getLimit())\r
+                    return 1; // in range\r
+                else\r
+                    return 0; // out of range\r
+            } else\r
+                return 1;\r
+        } else\r
             return 0;\r
     }\r
 \r
+    /**\r
+     * Generate the entropy values for the terrain for the "perspective" LOD\r
+     * calculator. This routine can take a long time to run!\r
+     * @param progressMonitor optional\r
+     */\r
     public void generateEntropy(ProgressMonitor progressMonitor) {\r
         // only check this on the root quad\r
         if (isRootQuad())\r
@@ -348,12 +341,16 @@ public class TerrainQuad extends Node implements Terrain {
     }\r
 \r
     public Material getMaterial() {\r
+        return getMaterial(null);\r
+    }\r
+    \r
+    public Material getMaterial(Vector3f worldLocation) {\r
         // get the material from one of the children. They all share the same material\r
         if (children != null) {\r
             for (int i = children.size(); --i >= 0;) {\r
                 Spatial child = children.get(i);\r
                 if (child instanceof TerrainQuad) {\r
-                    return ((TerrainQuad)child).getMaterial();\r
+                    return ((TerrainQuad)child).getMaterial(worldLocation);\r
                 } else if (child instanceof TerrainPatch) {\r
                     return ((TerrainPatch)child).getMaterial();\r
                 }\r
@@ -362,83 +359,10 @@ public class TerrainQuad extends Node implements Terrain {
         return null;\r
     }\r
 \r
-    public float getTextureCoordinateScale() {\r
-        return 1f/(float)totalSize;\r
-    }\r
-\r
-    /**\r
-     * Calculates the LOD of all child terrain patches.\r
-     */\r
-    private class UpdateLOD implements Runnable {\r
-        private List<Vector3f> camLocations;\r
-        private LodCalculator lodCalculator;\r
-\r
-        UpdateLOD(List<Vector3f> camLocations, LodCalculator lodCalculator) {\r
-            this.camLocations = camLocations;\r
-            this.lodCalculator = lodCalculator;\r
-        }\r
-\r
-        public void run() {\r
-            long start = System.currentTimeMillis();\r
-            if (isLodCalcRunning()) {\r
-                //System.out.println("thread already running");\r
-                return;\r
-            }\r
-            //System.out.println("spawned thread "+toString());\r
-            setLodCalcRunning(true);\r
-\r
-            // go through each patch and calculate its LOD based on camera distance\r
-            HashMap<String,UpdatedTerrainPatch> updated = new HashMap<String,UpdatedTerrainPatch>();\r
-            boolean lodChanged = calculateLod(camLocations, updated, lodCalculator); // 'updated' gets populated here\r
-\r
-            if (!lodChanged) {\r
-                // not worth updating anything else since no one's LOD changed\r
-                setLodCalcRunning(false);\r
-                return;\r
-            }\r
-            // then calculate its neighbour LOD values for seaming in the shader\r
-            findNeighboursLod(updated);\r
-\r
-            fixEdges(updated); // 'updated' can get added to here\r
-\r
-            reIndexPages(updated, lodCalculator.usesVariableLod());\r
-\r
-            setUpdateQuadLODs(updated); // set back to main ogl thread\r
-\r
-            setLodCalcRunning(false);\r
-            //double duration = (System.currentTimeMillis()-start);\r
-            //System.out.println("terminated in "+duration);\r
-        }\r
-    }\r
-\r
-    private void setUpdateQuadLODs(HashMap<String,UpdatedTerrainPatch> updated) {\r
-        synchronized (updatePatchesLock) {\r
-            updatedPatches = updated;\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Back on the ogl thread: update the terrain patch geometries\r
-     * @param updatedPatches to be updated\r
-     */\r
-    private void updateQuadLODs() {\r
-        synchronized (updatePatchesLock) {\r
-            \r
-            if (updatedPatches == null || updatedPatches.isEmpty())\r
-                return;\r
-\r
-            // do the actual geometry update here\r
-            for (UpdatedTerrainPatch utp : updatedPatches.values()) {\r
-                utp.updateAll();\r
-            }\r
-\r
-            updatedPatches.clear();\r
-        }\r
+    public int getNumMajorSubdivisions() {\r
+        return 1;\r
     }\r
     \r
-    public boolean hasPatchesToUpdate() {\r
-        return updatedPatches != null && !updatedPatches.isEmpty();\r
-    }\r
 \r
     protected boolean calculateLod(List<Vector3f> location, HashMap<String,UpdatedTerrainPatch> updates, LodCalculator lodCalculator) {\r
 \r
@@ -481,6 +405,8 @@ public class TerrainQuad extends Node implements Terrain {
                     }\r
                     TerrainPatch right = patch.rightNeighbour;\r
                     TerrainPatch down = patch.bottomNeighbour;\r
+                    TerrainPatch left = patch.leftNeighbour;\r
+                    TerrainPatch top = patch.topNeighbour;\r
 \r
                     UpdatedTerrainPatch utp = updated.get(patch.getName());\r
                     if (utp == null) {\r
@@ -491,30 +417,69 @@ public class TerrainQuad extends Node implements Terrain {
                     if (right != null) {\r
                         UpdatedTerrainPatch utpR = updated.get(right.getName());\r
                         if (utpR == null) {\r
-                            utpR = new UpdatedTerrainPatch(right, right.lod);\r
+                            utpR = new UpdatedTerrainPatch(right);\r
                             updated.put(utpR.getName(), utpR);\r
+                            utpR.setNewLod(right.lod);\r
                         }\r
-\r
                         utp.setRightLod(utpR.getNewLod());\r
                         utpR.setLeftLod(utp.getNewLod());\r
                     }\r
                     if (down != null) {\r
                         UpdatedTerrainPatch utpD = updated.get(down.getName());\r
                         if (utpD == null) {\r
-                            utpD = new UpdatedTerrainPatch(down, down.lod);\r
+                            utpD = new UpdatedTerrainPatch(down);\r
                             updated.put(utpD.getName(), utpD);\r
+                            utpD.setNewLod(down.lod);\r
                         }\r
-\r
                         utp.setBottomLod(utpD.getNewLod());\r
                         utpD.setTopLod(utp.getNewLod());\r
                     }\r
-\r
+                    \r
+                    if (left != null) {\r
+                        UpdatedTerrainPatch utpL = updated.get(left.getName());\r
+                        if (utpL == null) {\r
+                            utpL = new UpdatedTerrainPatch(left);\r
+                            updated.put(utpL.getName(), utpL);\r
+                            utpL.setNewLod(left.lod);\r
+                        }\r
+                        utp.setLeftLod(utpL.getNewLod());\r
+                        utpL.setRightLod(utp.getNewLod());\r
+                    }\r
+                    if (top != null) {\r
+                        UpdatedTerrainPatch utpT = updated.get(top.getName());\r
+                        if (utpT == null) {\r
+                            utpT = new UpdatedTerrainPatch(top);\r
+                            updated.put(utpT.getName(), utpT);\r
+                            utpT.setNewLod(top.lod);\r
+                        }\r
+                        utp.setTopLod(utpT.getNewLod());\r
+                        utpT.setBottomLod(utp.getNewLod());\r
+                    }\r
                 }\r
             }\r
         }\r
     }\r
 \r
     /**\r
+     * Reset the cached references of neighbours.\r
+     * TerrainQuad caches neighbours for faster LOD checks.\r
+     * Sometimes you might want to reset this cache (for instance in TerrainGrid)\r
+     */\r
+    public void resetCachedNeighbours() {\r
+        if (children != null) {\r
+            for (int x = children.size(); --x >= 0;) {\r
+                Spatial child = children.get(x);\r
+                if (child instanceof TerrainQuad) {\r
+                    ((TerrainQuad) child).resetCachedNeighbours();\r
+                } else if (child instanceof TerrainPatch) {\r
+                    TerrainPatch patch = (TerrainPatch) child;\r
+                    patch.searchedForNeighboursAlready = false;\r
+                }\r
+            }\r
+        }\r
+    }\r
+    \r
+    /**\r
      * Find any neighbours that should have their edges seamed because another neighbour\r
      * changed its LOD to a greater value (less detailed)\r
      */\r
@@ -528,7 +493,7 @@ public class TerrainQuad extends Node implements Terrain {
                     TerrainPatch patch = (TerrainPatch) child;\r
                     UpdatedTerrainPatch utp = updated.get(patch.getName());\r
 \r
-                    if(utp.lodChanged()) {\r
+                    if(utp != null && utp.lodChanged()) {\r
                         if (!patch.searchedForNeighboursAlready) {\r
                             // set the references to the neighbours\r
                             patch.rightNeighbour = findRightPatch(patch);\r
@@ -544,33 +509,41 @@ public class TerrainQuad extends Node implements Terrain {
                         if (right != null) {\r
                             UpdatedTerrainPatch utpR = updated.get(right.getName());\r
                             if (utpR == null) {\r
-                                utpR = new UpdatedTerrainPatch(right, right.lod);\r
+                                utpR = new UpdatedTerrainPatch(right);\r
                                 updated.put(utpR.getName(), utpR);\r
+                                utpR.setNewLod(right.lod);\r
                             }\r
+                            utpR.setLeftLod(utp.getNewLod());\r
                             utpR.setFixEdges(true);\r
                         }\r
                         if (down != null) {\r
                             UpdatedTerrainPatch utpD = updated.get(down.getName());\r
                             if (utpD == null) {\r
-                                utpD = new UpdatedTerrainPatch(down, down.lod);\r
+                                utpD = new UpdatedTerrainPatch(down);\r
                                 updated.put(utpD.getName(), utpD);\r
+                                utpD.setNewLod(down.lod);\r
                             }\r
+                            utpD.setTopLod(utp.getNewLod());\r
                             utpD.setFixEdges(true);\r
                         }\r
                         if (top != null){\r
                             UpdatedTerrainPatch utpT = updated.get(top.getName());\r
                             if (utpT == null) {\r
-                                utpT = new UpdatedTerrainPatch(top, top.lod);\r
+                                utpT = new UpdatedTerrainPatch(top);\r
                                 updated.put(utpT.getName(), utpT);\r
+                                utpT.setNewLod(top.lod);\r
                             }\r
+                            utpT.setBottomLod(utp.getNewLod());\r
                             utpT.setFixEdges(true);\r
                         }\r
                         if (left != null){\r
                             UpdatedTerrainPatch utpL = updated.get(left.getName());\r
                             if (utpL == null) {\r
-                                utpL = new UpdatedTerrainPatch(left, left.lod);\r
+                                utpL = new UpdatedTerrainPatch(left);\r
                                 updated.put(utpL.getName(), utpL);\r
+                                utpL.setNewLod(left.lod);\r
                             }\r
+                            utpL.setRightLod(utp.getNewLod());\r
                             utpL.setFixEdges(true);\r
                         }\r
                     }\r
@@ -614,13 +587,14 @@ public class TerrainQuad extends Node implements Terrain {
 \r
     /**\r
      * Quadrants, world coordinates, and heightmap coordinates (Y-up):\r
-     *         x\r
-     *         | u\r
-     *        2|4 v\r
-     *  -z ----+---- z\r
-     *     -v 1|3\r
-     *      -u |\r
-     *        -x\r
+     * \r
+     *         -z\r
+     *      -u | \r
+     *    -v  1|3 \r
+     *  -x ----+---- x\r
+     *        2|4 u\r
+     *         | v\r
+     *         z\r
      * <code>createQuad</code> generates four new quads from this quad.\r
      * The heightmap's top left (0,0) coordinate is at the bottom, -x,-z\r
      * coordinate of the terrain, so it grows in the positive x.z direction.\r
@@ -637,7 +611,7 @@ public class TerrainQuad extends Node implements Terrain {
         //if (lodCalculator == null)\r
         //    lodCalculator = createDefaultLodCalculator(); // set a default one\r
 \r
-        // 1 upper left of heightmap, lower left quad\r
+        // 1 upper left of heightmap, upper left quad\r
         float[] heightBlock1 = createHeightSubBlock(heightMap, 0, 0, split);\r
 \r
         Vector3f origin1 = new Vector3f(-quarterSize * stepScale.x, 0,\r
@@ -655,7 +629,7 @@ public class TerrainQuad extends Node implements Terrain {
         quad1.quadrant = 1;\r
         this.attachChild(quad1);\r
 \r
-        // 2 lower left of heightmap, upper left quad\r
+        // 2 lower left of heightmap, lower left quad\r
         float[] heightBlock2 = createHeightSubBlock(heightMap, 0, split - 1,\r
                         split);\r
 \r
@@ -675,7 +649,7 @@ public class TerrainQuad extends Node implements Terrain {
         quad2.quadrant = 2;\r
         this.attachChild(quad2);\r
 \r
-        // 3 upper right of heightmap, lower right quad\r
+        // 3 upper right of heightmap, upper right quad\r
         float[] heightBlock3 = createHeightSubBlock(heightMap, split - 1, 0,\r
                         split);\r
 \r
@@ -695,7 +669,7 @@ public class TerrainQuad extends Node implements Terrain {
         quad3.quadrant = 3;\r
         this.attachChild(quad3);\r
         \r
-        // 4 lower right of heightmap, upper right quad\r
+        // 4 lower right of heightmap, lower right quad\r
         float[] heightBlock4 = createHeightSubBlock(heightMap, split - 1,\r
                         split - 1, split);\r
 \r
@@ -747,7 +721,7 @@ public class TerrainQuad extends Node implements Terrain {
 \r
         offsetAmount += quarterSize;\r
 \r
-        // 1 upper left\r
+        // 1 lower left\r
         float[] heightBlock1 = createHeightSubBlock(heightMap, 0, 0, split);\r
 \r
         Vector3f origin1 = new Vector3f(-halfSize * stepScale.x, 0, -halfSize\r
@@ -769,7 +743,7 @@ public class TerrainQuad extends Node implements Terrain {
         //patch1.setLodCalculator(lodCalculator);\r
         //TangentBinormalGenerator.generate(patch1);\r
 \r
-        // 2 lower left\r
+        // 2 upper left\r
         float[] heightBlock2 = createHeightSubBlock(heightMap, 0, split - 1,\r
                         split);\r
 \r
@@ -791,7 +765,7 @@ public class TerrainQuad extends Node implements Terrain {
         //patch2.setLodCalculator(lodCalculator);\r
         //TangentBinormalGenerator.generate(patch2);\r
 \r
-        // 3 upper right\r
+        // 3 lower right\r
         float[] heightBlock3 = createHeightSubBlock(heightMap, split - 1, 0,\r
                         split);\r
 \r
@@ -813,7 +787,7 @@ public class TerrainQuad extends Node implements Terrain {
         //patch3.setLodCalculator(lodCalculator);\r
         //TangentBinormalGenerator.generate(patch3);\r
 \r
-        // 4 lower right\r
+        // 4 upper right\r
         float[] heightBlock4 = createHeightSubBlock(heightMap, split - 1,\r
                         split - 1, split);\r
 \r
@@ -901,10 +875,10 @@ public class TerrainQuad extends Node implements Terrain {
         }\r
 \r
         if (affectedAreaBBox == null) {\r
-            affectedAreaBBox = new BoundingBox(new Vector3f(changedPoint.x, 0, changedPoint.y), 0.5f, Float.MAX_VALUE, 0.5f); // unit length\r
+            affectedAreaBBox = new BoundingBox(new Vector3f(changedPoint.x, 0, changedPoint.y), 1f, Float.MAX_VALUE, 1f); // unit length\r
         } else {\r
             // adjust size of box to be larger\r
-            affectedAreaBBox.mergeLocal(new BoundingBox(new Vector3f(changedPoint.x, 0, changedPoint.y), 0.5f, Float.MAX_VALUE, 0.5f));\r
+            affectedAreaBBox.mergeLocal(new BoundingBox(new Vector3f(changedPoint.x, 0, changedPoint.y), 1f, Float.MAX_VALUE, 1f));\r
         }\r
     }\r
 \r
@@ -912,12 +886,19 @@ public class TerrainQuad extends Node implements Terrain {
         if (affectedAreaBBox != null)\r
             return true;\r
         if (!lastScale.equals(getWorldScale())) {\r
-            affectedAreaBBox = new BoundingBox(new Vector3f(0,0,0), size, Float.MAX_VALUE, size);\r
+            affectedAreaBBox = new BoundingBox(getWorldTranslation(), Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE);\r
             lastScale = getWorldScale();\r
             return true;\r
         }\r
         return false;\r
     }\r
+    \r
+    /**\r
+     * This will cause all normals for this terrain quad to be recalculated\r
+     */\r
+    protected void setNeedToRecalculateNormals() {\r
+        affectedAreaBBox = new BoundingBox(getWorldTranslation(), Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE);\r
+    }\r
 \r
     public float getHeightmapHeight(Vector2f xz) {\r
         // offset\r
@@ -925,6 +906,8 @@ public class TerrainQuad extends Node implements Terrain {
         int x = Math.round((xz.x / getWorldScale().x) + halfSize);\r
         int z = Math.round((xz.y / getWorldScale().z) + halfSize);\r
 \r
+        if (!isInside(x, z))\r
+            return Float.NaN;\r
         return getHeightmapHeight(x, z);\r
     }\r
 \r
@@ -1022,50 +1005,107 @@ public class TerrainQuad extends Node implements Terrain {
         return null;\r
     }\r
 \r
+    /**\r
+     * is the 2d point inside the terrain?\r
+     * @param x local coordinate\r
+     * @param z local coordinate\r
+     */\r
+    private boolean isInside(int x, int z) {\r
+        if (x < 0 || z < 0 || x > totalSize || z > totalSize)\r
+            return false;\r
+        return true;\r
+    }\r
+\r
+    /**\r
+     * Used for searching for a child and keeping\r
+     * track of its quadrant\r
+     */\r
+    private class QuadrantChild {\r
+        int col;\r
+        int row;\r
+        Spatial child;\r
+        \r
+        QuadrantChild(int col, int row, Spatial child) {\r
+            this.col = col;\r
+            this.row = row;\r
+            this.child = child;\r
+        }\r
+    }\r
+    \r
+    private QuadrantChild findMatchingChild(int x, int z) {\r
+        int quad = findQuadrant(x, z);\r
+        int split = (size + 1) >> 1;\r
+        if (children != null) {\r
+            for (int i = children.size(); --i >= 0;) {\r
+                Spatial spat = children.get(i);\r
+                int col = x;\r
+                int row = z;\r
+                boolean match = false;\r
+\r
+                // get the childs quadrant\r
+                int childQuadrant = 0;\r
+                if (spat instanceof TerrainQuad) {\r
+                    childQuadrant = ((TerrainQuad) spat).getQuadrant();\r
+                } else if (spat instanceof TerrainPatch) {\r
+                    childQuadrant = ((TerrainPatch) spat).getQuadrant();\r
+                }\r
+\r
+                if (childQuadrant == 1 && (quad & 1) != 0) {\r
+                    match = true;\r
+                } else if (childQuadrant == 2 && (quad & 2) != 0) {\r
+                    row = z - split + 1;\r
+                    match = true;\r
+                } else if (childQuadrant == 3 && (quad & 4) != 0) {\r
+                    col = x - split + 1;\r
+                    match = true;\r
+                } else if (childQuadrant == 4 && (quad & 8) != 0) {\r
+                    col = x - split + 1;\r
+                    row = z - split + 1;\r
+                    match = true;\r
+                }\r
+                if (match)\r
+                    return new QuadrantChild(col, row, spat);\r
+            }\r
+        }\r
+        return null;\r
+    }\r
+    \r
+    /**\r
+     * Get the interpolated height of the terrain at the specified point.\r
+     * @param xz the location to get the height for\r
+     * @return Float.NAN if the value does not exist, or the coordinates are outside of the terrain\r
+     */\r
     public float getHeight(Vector2f xz) {\r
         // offset\r
-        float x = (float)(((xz.x - getLocalTranslation().x) / getWorldScale().x) + (float)totalSize / 2f);\r
-        float z = (float)(((xz.y - getLocalTranslation().z) / getWorldScale().z) + (float)totalSize / 2f);\r
-        float height = getHeight(x, z);\r
+        float x = (float)(((xz.x - getWorldTranslation().x) / getWorldScale().x) + (float)(totalSize-1) / 2f);\r
+        float z = (float)(((xz.y - getWorldTranslation().z) / getWorldScale().z) + (float)(totalSize-1) / 2f);\r
+        if (!isInside((int)x, (int)z))\r
+            return Float.NaN;\r
+        float height = getHeight((int)x, (int)z, (x%1f), (z%1f));\r
         height *= getWorldScale().y;\r
         return height;\r
     }\r
 \r
     /*\r
      * gets an interpolated value at the specified point\r
-     * @param x coordinate translated into actual (positive) terrain grid coordinates\r
-     * @param y coordinate translated into actual (positive) terrain grid coordinates\r
      */\r
-    protected float getHeight(float x, float z) {\r
-        x-=0.5f;\r
-        z-=0.5f;\r
-        float col = FastMath.floor(x);\r
-        float row = FastMath.floor(z);\r
-        boolean onX = false;\r
-        if(1 - (x - col)-(z - row) < 0) // what triangle to interpolate on\r
-            onX = true;\r
-        // v1--v2  ^\r
-        // |  / |  |\r
-        // | /  |  |\r
-        // v3--v4  | Z\r
-        //         |\r
-        // <-------Y\r
-        //     X \r
-        float v1 = getHeightmapHeight((int) FastMath.ceil(x), (int) FastMath.ceil(z));\r
-        float v2 = getHeightmapHeight((int) FastMath.floor(x), (int) FastMath.ceil(z));\r
-        float v3 = getHeightmapHeight((int) FastMath.ceil(x), (int) FastMath.floor(z));\r
-        float v4 = getHeightmapHeight((int) FastMath.floor(x), (int) FastMath.floor(z));\r
-        if (onX) {\r
-            return ((x - col) + (z - row) - 1f)*v1 + (1f - (x - col))*v2 + (1f - (z - row))*v3;\r
-        } else {\r
-            return (1f - (x - col) - (z - row))*v4 + (z - row)*v2 + (x - col)*v3;\r
+    protected float getHeight(int x, int z, float xm, float zm) {\r
+        \r
+        QuadrantChild match = findMatchingChild(x,z);\r
+        if (match != null) {\r
+            if (match.child instanceof TerrainQuad) {\r
+                return ((TerrainQuad) match.child).getHeight(match.col, match.row, xm, zm);\r
+            } else if (match.child instanceof TerrainPatch) {\r
+                return ((TerrainPatch) match.child).getHeight(match.col, match.row, xm, zm);\r
+            }\r
         }\r
+        return Float.NaN;\r
     }\r
 \r
     public Vector3f getNormal(Vector2f xz) {\r
         // offset\r
-        float x = (float)(((xz.x - getLocalTranslation().x) / getWorldScale().x) + (float)totalSize / 2f);\r
-        float z = (float)(((xz.y - getLocalTranslation().z) / getWorldScale().z) + (float)totalSize / 2f);\r
+        float x = (float)(((xz.x - getWorldTranslation().x) / getWorldScale().x) + (float)(totalSize-1) / 2f);\r
+        float z = (float)(((xz.y - getWorldTranslation().z) / getWorldScale().z) + (float)(totalSize-1) / 2f);\r
         Vector3f normal = getNormal(x, z, xz);\r
         \r
         return normal;\r
@@ -1132,6 +1172,8 @@ public class TerrainQuad extends Node implements Terrain {
         for (int i=0; i<xz.size(); i++) {\r
             int x = Math.round((xz.get(i).x / getWorldScale().x) + halfSize);\r
             int z = Math.round((xz.get(i).y / getWorldScale().z) + halfSize);\r
+            if (!isInside(x, z))\r
+                continue;\r
             locations.add(new LocationHeight(x,z,height.get(i)));\r
         }\r
 \r
@@ -1194,7 +1236,6 @@ public class TerrainQuad extends Node implements Terrain {
         // distribute each locationHeight into the quadrant it intersects\r
         for (LocationHeight lh : locations) {\r
             int quad = findQuadrant(lh.x, lh.z);\r
-\r
             int col = lh.x;\r
             int row = lh.z;\r
 \r
@@ -1313,6 +1354,8 @@ public class TerrainQuad extends Node implements Terrain {
     }\r
 \r
     protected TerrainQuad getQuad(int quad) {\r
+        if (quad == 0)\r
+            return this;\r
         if (children != null)\r
             for (int x = children.size(); --x >= 0;) {\r
                 Spatial child = children.get(x);\r
@@ -1390,7 +1433,7 @@ public class TerrainQuad extends Node implements Terrain {
         else if (tp.getQuadrant() == 4)\r
             return getPatch(2);\r
         else if (tp.getQuadrant() == 1) {\r
-            // find the patch above and ask it for child 2.\r
+            // find the patch above and ask it for child 3.\r
             TerrainQuad quad = findLeftQuad();\r
             if (quad != null)\r
                 return quad.getPatch(3);\r
@@ -1404,10 +1447,17 @@ public class TerrainQuad extends Node implements Terrain {
     }\r
 \r
     protected TerrainQuad findRightQuad() {\r
-        if (getParent() == null || !(getParent() instanceof TerrainQuad))\r
-            return null;\r
+        boolean useFinder = false;\r
+        if (getParent() == null || !(getParent() instanceof TerrainQuad)) {\r
+            if (neighbourFinder == null)\r
+                return null;\r
+            else\r
+                useFinder = true;\r
+        }\r
 \r
-        TerrainQuad pQuad = (TerrainQuad) getParent();\r
+        TerrainQuad pQuad = null;\r
+        if (!useFinder)\r
+            pQuad = (TerrainQuad) getParent();\r
 \r
         if (quadrant == 1)\r
             return pQuad.getQuad(3);\r
@@ -1421,16 +1471,29 @@ public class TerrainQuad extends Node implements Terrain {
             TerrainQuad quad = pQuad.findRightQuad();\r
             if (quad != null)\r
                 return quad.getQuad(2);\r
+        } else if (quadrant == 0) {\r
+            // at the top quad\r
+            if (useFinder) {\r
+                TerrainQuad quad = neighbourFinder.getRightQuad(this);\r
+                return quad;\r
+            }\r
         }\r
 \r
         return null;\r
     }\r
 \r
     protected TerrainQuad findDownQuad() {\r
-        if (getParent() == null || !(getParent() instanceof TerrainQuad))\r
-            return null;\r
+        boolean useFinder = false;\r
+        if (getParent() == null || !(getParent() instanceof TerrainQuad)) {\r
+            if (neighbourFinder == null)\r
+                return null;\r
+            else\r
+                useFinder = true;\r
+        }\r
 \r
-        TerrainQuad pQuad = (TerrainQuad) getParent();\r
+        TerrainQuad pQuad = null;\r
+        if (!useFinder)\r
+            pQuad = (TerrainQuad) getParent();\r
 \r
         if (quadrant == 1)\r
             return pQuad.getQuad(2);\r
@@ -1444,16 +1507,29 @@ public class TerrainQuad extends Node implements Terrain {
             TerrainQuad quad = pQuad.findDownQuad();\r
             if (quad != null)\r
                 return quad.getQuad(3);\r
+        } else if (quadrant == 0) {\r
+            // at the top quad\r
+            if (useFinder) {\r
+                TerrainQuad quad = neighbourFinder.getDownQuad(this);\r
+                return quad;\r
+            }\r
         }\r
 \r
         return null;\r
     }\r
 \r
     protected TerrainQuad findTopQuad() {\r
-        if (getParent() == null || !(getParent() instanceof TerrainQuad))\r
-            return null;\r
+        boolean useFinder = false;\r
+        if (getParent() == null || !(getParent() instanceof TerrainQuad)) {\r
+            if (neighbourFinder == null)\r
+                return null;\r
+            else\r
+                useFinder = true;\r
+        }\r
 \r
-        TerrainQuad pQuad = (TerrainQuad) getParent();\r
+        TerrainQuad pQuad = null;\r
+        if (!useFinder)\r
+            pQuad = (TerrainQuad) getParent();\r
 \r
         if (quadrant == 2)\r
             return pQuad.getQuad(1);\r
@@ -1467,16 +1543,29 @@ public class TerrainQuad extends Node implements Terrain {
             TerrainQuad quad = pQuad.findTopQuad();\r
             if (quad != null)\r
                 return quad.getQuad(4);\r
+        } else if (quadrant == 0) {\r
+            // at the top quad\r
+            if (useFinder) {\r
+                TerrainQuad quad = neighbourFinder.getTopQuad(this);\r
+                return quad;\r
+            }\r
         }\r
 \r
         return null;\r
     }\r
 \r
     protected TerrainQuad findLeftQuad() {\r
-        if (getParent() == null || !(getParent() instanceof TerrainQuad))\r
-            return null;\r
+        boolean useFinder = false;\r
+        if (getParent() == null || !(getParent() instanceof TerrainQuad)) {\r
+            if (neighbourFinder == null)\r
+                return null;\r
+            else\r
+                useFinder = true;\r
+        }\r
 \r
-        TerrainQuad pQuad = (TerrainQuad) getParent();\r
+        TerrainQuad pQuad = null;\r
+        if (!useFinder)\r
+            pQuad = (TerrainQuad) getParent();\r
 \r
         if (quadrant == 3)\r
             return pQuad.getQuad(1);\r
@@ -1490,6 +1579,12 @@ public class TerrainQuad extends Node implements Terrain {
             TerrainQuad quad = pQuad.findLeftQuad();\r
             if (quad != null)\r
                 return quad.getQuad(4);\r
+        } else if (quadrant == 0) {\r
+            // at the top quad\r
+            if (useFinder) {\r
+                TerrainQuad quad = neighbourFinder.getLeftQuad(this);\r
+                return quad;\r
+            }\r
         }\r
 \r
         return null;\r
@@ -1602,8 +1697,9 @@ public class TerrainQuad extends Node implements Terrain {
                             }\r
                         }\r
                     }\r
-                    else\r
+                    else if (children.get(i) instanceof TerrainQuad) {\r
                         ((TerrainQuad) children.get(i)).findPick(toTest, results);\r
+                    }\r
                 }\r
             }\r
         }\r
@@ -1687,8 +1783,12 @@ public class TerrainQuad extends Node implements Terrain {
         quadClone.name = name.toString();\r
         quadClone.size = size;\r
         quadClone.totalSize = totalSize;\r
-        quadClone.stepScale = stepScale.clone();\r
-        quadClone.offset = offset.clone();\r
+        if (stepScale != null) {\r
+            quadClone.stepScale = stepScale.clone();\r
+        }\r
+        if (offset != null) {\r
+            quadClone.offset = offset.clone();\r
+        }\r
         quadClone.offsetAmount = offsetAmount;\r
         quadClone.quadrant = quadrant;\r
         //quadClone.lodCalculatorFactory = lodCalculatorFactory.clone();\r
@@ -1706,6 +1806,34 @@ public class TerrainQuad extends Node implements Terrain {
 \r
         return quadClone;\r
     }\r
+        \r
+    @Override\r
+    protected void setParent(Node parent) {\r
+        super.setParent(parent);\r
+        if (parent == null) {\r
+            // if the terrain is being detached\r
+            clearCaches();\r
+        }\r
+    }\r
+    \r
+    /**\r
+     * Removes any cached references this terrain is holding, in particular\r
+     * the TerrainPatch's neighbour references.\r
+     * This is called automatically when the root terrainQuad is detached from\r
+     * its parent or if setParent(null) is called.\r
+     */\r
+    public void clearCaches() {\r
+        if (children != null) {\r
+            for (int i = children.size(); --i >= 0;) {\r
+                Spatial child = children.get(i);\r
+                if (child instanceof TerrainQuad) {\r
+                    ((TerrainQuad) child).clearCaches();\r
+                } else if (child instanceof TerrainPatch) {\r
+                    ((TerrainPatch) child).clearCaches();\r
+                }\r
+            }\r
+        }\r
+    }\r
     \r
     public int getMaxLod() {\r
         if (maxLod < 0)\r
@@ -1735,13 +1863,13 @@ public class TerrainQuad extends Node implements Terrain {
             if (getChild(0) instanceof TerrainPatch) {\r
                 for (Spatial s : getChildren()) {\r
                     if ( ((TerrainPatch)s).getQuadrant() == 1)\r
-                        ul = BufferUtils.getFloatArray(((TerrainPatch)s).getHeightmap());\r
+                        ul = ((TerrainPatch)s).getHeightMap();\r
                     else if(((TerrainPatch) s).getQuadrant() == 2)\r
-                        bl = BufferUtils.getFloatArray(((TerrainPatch)s).getHeightmap());\r
+                        bl = ((TerrainPatch)s).getHeightMap();\r
                     else if(((TerrainPatch) s).getQuadrant() == 3)\r
-                        ur = BufferUtils.getFloatArray(((TerrainPatch)s).getHeightmap());\r
+                        ur = ((TerrainPatch)s).getHeightMap();\r
                     else if(((TerrainPatch) s).getQuadrant() == 4)\r
-                        br = BufferUtils.getFloatArray(((TerrainPatch)s).getHeightmap());\r
+                        br = ((TerrainPatch)s).getHeightMap();\r
                 }\r
             }\r
             else {\r
index 1f3b1fc..f6d370b 100644 (file)
@@ -1,5 +1,5 @@
 /*\r
- * Copyright (c) 2009-2010 jMonkeyEngine\r
+ * Copyright (c) 2009-2012 jMonkeyEngine\r
  * All rights reserved.\r
  *\r
  * Redistribution and use in source and binary forms, with or without\r
  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\r
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
  */\r
-\r
 package com.jme3.terrain.geomipmap;\r
 \r
-import java.nio.IntBuffer;\r
-\r
 import com.jme3.scene.VertexBuffer.Type;\r
+import java.nio.Buffer;\r
+import java.nio.IntBuffer;\r
+import java.nio.ShortBuffer;\r
 \r
 /**\r
  * Stores a terrain patch's details so the LOD background thread can update\r
@@ -45,140 +45,142 @@ import com.jme3.scene.VertexBuffer.Type;
  */\r
 public class UpdatedTerrainPatch {\r
 \r
-       private TerrainPatch updatedPatch;\r
-       private int newLod;\r
-       private int previousLod;\r
-       private int rightLod,topLod,leftLod,bottomLod;\r
-       private IntBuffer newIndexBuffer;\r
-       private boolean reIndexNeeded = false;\r
-       private boolean fixEdges = false;\r
-\r
-       public UpdatedTerrainPatch(TerrainPatch updatedPatch, int newLod) {\r
-               this.updatedPatch = updatedPatch;\r
-               this.newLod = newLod;\r
-       }\r
-\r
-       public UpdatedTerrainPatch(TerrainPatch updatedPatch, int newLod, int prevLOD, boolean reIndexNeeded) {\r
-               this.updatedPatch = updatedPatch;\r
-               this.newLod = newLod;\r
-               this.previousLod = prevLOD;\r
-               this.reIndexNeeded = reIndexNeeded;\r
-               if (this.newLod <= 0)\r
-                    throw new IllegalArgumentException();\r
-       }\r
-\r
-       public String getName() {\r
-               return updatedPatch.getName();\r
-       }\r
-\r
-       protected boolean lodChanged() {\r
-               if (reIndexNeeded && previousLod != newLod)\r
-                       return true;\r
-               else\r
-                       return false;\r
-       }\r
-\r
-       protected TerrainPatch getUpdatedPatch() {\r
-               return updatedPatch;\r
-       }\r
-\r
-       protected void setUpdatedPatch(TerrainPatch updatedPatch) {\r
-               this.updatedPatch = updatedPatch;\r
-       }\r
-\r
-       protected int getNewLod() {\r
-               return newLod;\r
-       }\r
-\r
-       public void setNewLod(int newLod) {\r
-               this.newLod = newLod;\r
-                if (this.newLod < 0)\r
-                    throw new IllegalArgumentException();\r
-       }\r
-\r
-       protected IntBuffer getNewIndexBuffer() {\r
-               return newIndexBuffer;\r
-       }\r
-\r
-       protected void setNewIndexBuffer(IntBuffer newIndexBuffer) {\r
-               this.newIndexBuffer = newIndexBuffer;\r
-       }\r
-\r
-\r
-       protected int getRightLod() {\r
-               return rightLod;\r
-       }\r
-\r
-\r
-       protected void setRightLod(int rightLod) {\r
-               this.rightLod = rightLod;\r
-       }\r
-\r
-\r
-       protected int getTopLod() {\r
-               return topLod;\r
-       }\r
-\r
-\r
-       protected void setTopLod(int topLod) {\r
-               this.topLod = topLod;\r
-       }\r
-\r
-\r
-       protected int getLeftLod() {\r
-               return leftLod;\r
-       }\r
-\r
-\r
-       protected void setLeftLod(int leftLod) {\r
-               this.leftLod = leftLod;\r
-       }\r
-\r
-\r
-       protected int getBottomLod() {\r
-               return bottomLod;\r
-       }\r
-\r
-\r
-       protected void setBottomLod(int bottomLod) {\r
-               this.bottomLod = bottomLod;\r
-       }\r
-\r
-       public boolean isReIndexNeeded() {\r
-               return reIndexNeeded;\r
-       }\r
-\r
-       public void setReIndexNeeded(boolean reIndexNeeded) {\r
-               this.reIndexNeeded = reIndexNeeded;\r
-       }\r
+    private TerrainPatch updatedPatch;\r
+    private int newLod;\r
+    private int previousLod;\r
+    private int rightLod,topLod,leftLod,bottomLod;\r
+    private Buffer newIndexBuffer;\r
+    //private boolean reIndexNeeded = false;\r
+    private boolean fixEdges = false;\r
 \r
-       public boolean isFixEdges() {\r
-               return fixEdges;\r
-       }\r
+    public UpdatedTerrainPatch(TerrainPatch updatedPatch) {\r
+        this.updatedPatch = updatedPatch;\r
+    }\r
 \r
-       public void setFixEdges(boolean fixEdges) {\r
-               this.fixEdges = fixEdges;\r
-       }\r
+    public UpdatedTerrainPatch(TerrainPatch updatedPatch, int newLod) {\r
+        this.updatedPatch = updatedPatch;\r
+        this.newLod = newLod;\r
+    }\r
 \r
-       public int getPreviousLod() {\r
-               return previousLod;\r
-       }\r
+    public String getName() {\r
+        return updatedPatch.getName();\r
+    }\r
 \r
-       public void setPreviousLod(int previousLod) {\r
-               this.previousLod = previousLod;\r
-       }\r
+    protected boolean lodChanged() {\r
+        if ( previousLod != newLod)\r
+            return true;\r
+        else\r
+            return false;\r
+    }\r
 \r
-       public void updateAll() {\r
-               updatedPatch.setLod(newLod);\r
-               updatedPatch.setLodRight(rightLod);\r
-               updatedPatch.setLodTop(topLod);\r
-               updatedPatch.setLodLeft(leftLod);\r
-               updatedPatch.setLodBottom(bottomLod);\r
-               if (newIndexBuffer != null && (reIndexNeeded || fixEdges)) {\r
-                       updatedPatch.setPreviousLod(previousLod);\r
-                       updatedPatch.getMesh().clearBuffer(Type.Index);\r
-                       updatedPatch.getMesh().setBuffer(Type.Index, 3, newIndexBuffer);\r
-               }\r
-       }\r
+    protected TerrainPatch getUpdatedPatch() {\r
+        return updatedPatch;\r
+    }\r
 \r
+    protected void setUpdatedPatch(TerrainPatch updatedPatch) {\r
+        this.updatedPatch = updatedPatch;\r
+    }\r
+\r
+    protected int getNewLod() {\r
+        return newLod;\r
+    }\r
+    \r
+    public void setNewLod(int newLod) {\r
+        this.newLod = newLod;\r
+        if (this.newLod < 0)\r
+            throw new IllegalArgumentException("newLod cannot be less than zero, was: "+newLod);\r
+    }\r
+\r
+    /*protected IntBuffer getNewIndexBuffer() {\r
+        return newIndexBuffer;\r
+    }*/\r
+\r
+    protected void setNewIndexBuffer(Buffer newIndexBuffer) {\r
+        this.newIndexBuffer = newIndexBuffer;\r
+    }\r
+\r
+\r
+    protected int getRightLod() {\r
+        return rightLod;\r
+    }\r
+\r
+\r
+    protected void setRightLod(int rightLod) {\r
+        this.rightLod = rightLod;\r
+    }\r
+\r
+\r
+    protected int getTopLod() {\r
+        return topLod;\r
+    }\r
+\r
+\r
+    protected void setTopLod(int topLod) {\r
+        this.topLod = topLod;\r
+    }\r
+\r
+\r
+    protected int getLeftLod() {\r
+        return leftLod;\r
+    }\r
+\r
+\r
+    protected void setLeftLod(int leftLod) {\r
+        this.leftLod = leftLod;\r
+    }\r
+\r
+\r
+    protected int getBottomLod() {\r
+        return bottomLod;\r
+    }\r
+\r
+\r
+    protected void setBottomLod(int bottomLod) {\r
+        this.bottomLod = bottomLod;\r
+    }\r
+\r
+    public boolean isReIndexNeeded() {\r
+        if (lodChanged() || isFixEdges())\r
+            return true;\r
+        //if (leftLod != newLod || rightLod != newLod || bottomLod != newLod || topLod != newLod)\r
+        //    return true;\r
+        return false;\r
+    }\r
+\r
+    /*public void setReIndexNeeded(boolean reIndexNeeded) {\r
+        this.reIndexNeeded = reIndexNeeded;\r
+    }*/\r
+\r
+    public boolean isFixEdges() {\r
+        return fixEdges;\r
+    }\r
+\r
+    public void setFixEdges(boolean fixEdges) {\r
+        this.fixEdges = fixEdges;\r
+    }\r
+\r
+    /*public int getPreviousLod() {\r
+        return previousLod;\r
+    }*/\r
+\r
+    public void setPreviousLod(int previousLod) {\r
+        this.previousLod = previousLod;\r
+    }\r
+\r
+    public void updateAll() {\r
+        updatedPatch.setLod(newLod);\r
+        updatedPatch.setLodRight(rightLod);\r
+        updatedPatch.setLodTop(topLod);\r
+        updatedPatch.setLodLeft(leftLod);\r
+        updatedPatch.setLodBottom(bottomLod);\r
+        if (newIndexBuffer != null && isReIndexNeeded()) {\r
+            updatedPatch.setPreviousLod(previousLod);\r
+            updatedPatch.getMesh().clearBuffer(Type.Index);\r
+            if (newIndexBuffer instanceof IntBuffer)\r
+                updatedPatch.getMesh().setBuffer(Type.Index, 3, (IntBuffer)newIndexBuffer);\r
+            else if (newIndexBuffer instanceof ShortBuffer)\r
+                updatedPatch.getMesh().setBuffer(Type.Index, 3, (ShortBuffer)newIndexBuffer);\r
+        }\r
+    }\r
+    \r
 }\r
diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/grid/AssetTileLoader.java b/engine/src/terrain/com/jme3/terrain/geomipmap/grid/AssetTileLoader.java
new file mode 100644 (file)
index 0000000..54cd15c
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.terrain.geomipmap.grid;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Vector3f;
+import com.jme3.terrain.geomipmap.TerrainGridTileLoader;
+import com.jme3.terrain.geomipmap.TerrainQuad;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author normenhansen
+ */
+public class AssetTileLoader implements TerrainGridTileLoader {
+
+    private AssetManager manager;
+    private String assetPath;
+    private String name;
+    private int size;
+    private int patchSize;
+    private int quadSize;
+
+    public AssetTileLoader() {
+    }
+
+    public AssetTileLoader(AssetManager manager, String name, String assetPath) {
+        this.manager = manager;
+        this.name = name;
+        this.assetPath = assetPath;
+    }
+
+    public TerrainQuad getTerrainQuadAt(Vector3f location) {
+        String modelName = assetPath + "/" + name + "_" + Math.round(location.x) + "_" + Math.round(location.y) + "_" + Math.round(location.z) + ".j3o";
+        Logger.getLogger(this.getClass().getName()).log(Level.FINE, "Load terrain grid tile: {0}", modelName);
+        TerrainQuad quad = null;
+        try {
+            quad = (TerrainQuad) manager.loadModel(modelName);
+        } catch (Exception e) {
+//            e.printStackTrace();
+        }
+        if (quad == null) {
+            Logger.getLogger(this.getClass().getName()).log(Level.WARNING, "Could not load terrain grid tile: {0}", modelName);
+            quad = createNewQuad(location);
+        } else {
+            Logger.getLogger(this.getClass().getName()).log(Level.FINE, "Loaded terrain grid tile: {0}", modelName);
+        }
+        return quad;
+    }
+
+    public String getAssetPath() {
+        return assetPath;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setPatchSize(int patchSize) {
+        this.patchSize = patchSize;
+    }
+
+    public void setQuadSize(int quadSize) {
+        this.quadSize = quadSize;
+    }
+
+    private TerrainQuad createNewQuad(Vector3f location) {
+        TerrainQuad q = new TerrainQuad("Quad" + location, patchSize, quadSize, null);
+        return q;
+    }
+
+    public void write(JmeExporter ex) throws IOException {
+        OutputCapsule c = ex.getCapsule(this);
+        c.write(assetPath, "assetPath", null);
+        c.write(name, "name", null);
+    }
+
+    public void read(JmeImporter im) throws IOException {
+        InputCapsule c = im.getCapsule(this);
+        manager = im.getAssetManager();
+        assetPath = c.readString("assetPath", null);
+        name = c.readString("name", null);
+    }
+}
\ No newline at end of file
diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/grid/FractalTileLoader.java b/engine/src/terrain/com/jme3/terrain/geomipmap/grid/FractalTileLoader.java
new file mode 100644 (file)
index 0000000..60f01af
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.terrain.geomipmap.grid;
+
+
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.math.Vector3f;
+import com.jme3.terrain.geomipmap.TerrainGridTileLoader;
+import com.jme3.terrain.geomipmap.TerrainQuad;
+import com.jme3.terrain.heightmap.AbstractHeightMap;
+import com.jme3.terrain.heightmap.HeightMap;
+import com.jme3.terrain.noise.Basis;
+import java.io.IOException;
+import java.nio.FloatBuffer;
+
+/**
+ *
+ * @author Anthyon, normenhansen
+ */
+public class FractalTileLoader implements TerrainGridTileLoader{
+            
+    public class FloatBufferHeightMap extends AbstractHeightMap {
+
+        private final FloatBuffer buffer;
+
+        public FloatBufferHeightMap(FloatBuffer buffer) {
+            this.buffer = buffer;
+        }
+
+        @Override
+        public boolean load() {
+            this.heightData = this.buffer.array();
+            return true;
+        }
+
+    }
+
+    private int patchSize;
+    private int quadSize;
+    private final Basis base;
+    private final float heightScale;
+
+    public FractalTileLoader(Basis base, float heightScale) {
+        this.base = base;
+        this.heightScale = heightScale;
+    }
+
+    private HeightMap getHeightMapAt(Vector3f location) {
+        AbstractHeightMap heightmap = null;
+        
+        FloatBuffer buffer = this.base.getBuffer(location.x * (this.quadSize - 1), location.z * (this.quadSize - 1), 0, this.quadSize);
+
+        float[] arr = buffer.array();
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = arr[i] * this.heightScale;
+        }
+        heightmap = new FloatBufferHeightMap(buffer);
+        heightmap.load();
+        return heightmap;
+    }
+
+    public TerrainQuad getTerrainQuadAt(Vector3f location) {
+        HeightMap heightMapAt = getHeightMapAt(location);
+        TerrainQuad q = new TerrainQuad("Quad" + location, patchSize, quadSize, heightMapAt == null ? null : heightMapAt.getHeightMap());
+        return q;
+    }
+
+    public void setPatchSize(int patchSize) {
+        this.patchSize = patchSize;
+    }
+
+    public void setQuadSize(int quadSize) {
+        this.quadSize = quadSize;
+    }
+
+    public void write(JmeExporter ex) throws IOException {
+        //TODO: serialization
+    }
+
+    public void read(JmeImporter im) throws IOException {
+        //TODO: serialization
+    }    
+}
diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/grid/ImageTileLoader.java b/engine/src/terrain/com/jme3/terrain/geomipmap/grid/ImageTileLoader.java
new file mode 100644 (file)
index 0000000..4e2d14d
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.terrain.geomipmap.grid;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.asset.AssetNotFoundException;
+import com.jme3.asset.TextureKey;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.math.Vector3f;
+import com.jme3.terrain.geomipmap.TerrainGridTileLoader;
+import com.jme3.terrain.geomipmap.TerrainQuad;
+import com.jme3.terrain.heightmap.*;
+import com.jme3.texture.Texture;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author Anthyon, normenhansen
+ */
+public class ImageTileLoader implements TerrainGridTileLoader{
+    private static final Logger logger = Logger.getLogger(ImageTileLoader.class.getName());
+    private final AssetManager assetManager;
+    private final Namer namer;
+    private int patchSize;
+    private int quadSize;
+    private float heightScale = 1;
+    //private int imageType = BufferedImage.TYPE_USHORT_GRAY; // 16 bit grayscale
+    //private ImageHeightmap customImageHeightmap;
+
+    public ImageTileLoader(final String textureBase, final String textureExt, AssetManager assetManager) {
+        this(assetManager, new Namer() {
+
+            public String getName(int x, int y) {
+                return textureBase + "_" + x + "_" + y + "." + textureExt;
+            }
+        });
+    }
+
+    public ImageTileLoader(AssetManager assetManager, Namer namer) {
+        this.assetManager = assetManager;
+        this.namer = namer;
+    }
+
+    /**
+     * Effects vertical scale of the height of the terrain when loaded.
+     */
+    public void setHeightScale(float heightScale) {
+        this.heightScale = heightScale;
+    }
+    
+    
+    /**
+     * Lets you specify the type of images that are being loaded. All images
+     * must be the same type.
+     * @param imageType eg. BufferedImage.TYPE_USHORT_GRAY
+     */
+    /*public void setImageType(int imageType) {
+        this.imageType = imageType;
+    }*/
+
+    /**
+     * The ImageHeightmap that will parse the image type that you 
+     * specify with setImageType().
+     * @param customImageHeightmap must extend AbstractHeightmap
+     */
+    /*public void setCustomImageHeightmap(ImageHeightmap customImageHeightmap) {
+        if (!(customImageHeightmap instanceof AbstractHeightMap)) {
+            throw new IllegalArgumentException("customImageHeightmap must be an AbstractHeightMap!");
+        }
+        this.customImageHeightmap = customImageHeightmap;
+    }*/
+
+    private HeightMap getHeightMapAt(Vector3f location) {
+        // HEIGHTMAP image (for the terrain heightmap)
+        int x = (int) location.x;
+        int z = (int) location.z;
+        
+        AbstractHeightMap heightmap = null;
+        //BufferedImage im = null;
+        
+        String name = null;
+        try {
+            name = namer.getName(x, z);
+            logger.log(Level.FINE, "Loading heightmap from file: {0}", name);
+            final Texture texture = assetManager.loadTexture(new TextureKey(name));
+            heightmap = new ImageBasedHeightMap(texture.getImage());
+            /*if (assetInfo != null){
+                InputStream in = assetInfo.openStream();
+                im = ImageIO.read(in);
+            } else {
+                im = new BufferedImage(patchSize, patchSize, imageType);
+                logger.log(Level.WARNING, "File: {0} not found, loading zero heightmap instead", name);
+            }*/
+            // CREATE HEIGHTMAP
+            /*if (imageType == BufferedImage.TYPE_USHORT_GRAY) {
+                heightmap = new Grayscale16BitHeightMap(im);
+            } else if (imageType == BufferedImage.TYPE_3BYTE_BGR) {
+                heightmap = new ImageBasedHeightMap(im);
+            } else if (customImageHeightmap != null && customImageHeightmap instanceof AbstractHeightMap) {
+                // If it gets here, it means you have specified a different image type, and you must
+                // then also supply a custom image heightmap class that can parse that image into
+                // a heightmap.
+                customImageHeightmap.setImage(im);
+                heightmap = (AbstractHeightMap) customImageHeightmap;
+            } else {
+                // error, no supported image format and no custom image heightmap specified
+                if (customImageHeightmap == null)
+                    logger.log(Level.SEVERE, "Custom image type specified [{0}] but no customImageHeightmap declared! Use setCustomImageHeightmap()",imageType);
+                if (!(customImageHeightmap instanceof AbstractHeightMap))
+                    logger.severe("customImageHeightmap must be an AbstractHeightMap!");
+                return null;
+            }*/
+            heightmap.setHeightScale(1);
+            heightmap.load();
+        //} catch (IOException e) {
+        //    e.printStackTrace();
+        } catch (AssetNotFoundException e) {
+            logger.log(Level.WARNING, "Asset {0} not found, loading zero heightmap instead", name);
+        }
+        return heightmap;
+    }
+
+    public void setSize(int size) {
+        this.patchSize = size - 1;
+    }
+
+    public TerrainQuad getTerrainQuadAt(Vector3f location) {
+        HeightMap heightMapAt = getHeightMapAt(location);
+        TerrainQuad q = new TerrainQuad("Quad" + location, patchSize, quadSize, heightMapAt == null ? null : heightMapAt.getHeightMap());
+        return q;
+    }
+
+    public void setPatchSize(int patchSize) {
+        this.patchSize = patchSize;
+    }
+
+    public void setQuadSize(int quadSize) {
+        this.quadSize = quadSize;
+    }
+
+    public void write(JmeExporter ex) throws IOException {
+        //TODO: serialization
+    }
+
+    public void read(JmeImporter im) throws IOException {
+        //TODO: serialization
+    }
+}
index 3ab34c9..e60d80b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2010 jMonkeyEngine
+ * Copyright (c) 2009-2012 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -63,41 +63,42 @@ public class DistanceLodCalculator implements LodCalculator {
     }
     
     public boolean calculateLod(TerrainPatch terrainPatch, List<Vector3f> locations, HashMap<String, UpdatedTerrainPatch> updates) {
+        if (locations == null || locations.isEmpty())
+            return false;// no camera yet
         float distance = getCenterLocation(terrainPatch).distance(locations.get(0));
 
-        
         if (turnOffLod) {
             // set to full detail
             int prevLOD = terrainPatch.getLod();
             UpdatedTerrainPatch utp = updates.get(terrainPatch.getName());
             if (utp == null) {
-                utp = new UpdatedTerrainPatch(terrainPatch, 0);
+                utp = new UpdatedTerrainPatch(terrainPatch);
                 updates.put(utp.getName(), utp);
             }
             utp.setNewLod(0);
             utp.setPreviousLod(prevLOD);
-            utp.setReIndexNeeded(true);
+            //utp.setReIndexNeeded(true);
             return true;
         }
         
         // go through each lod level to find the one we are in
         for (int i = 0; i <= terrainPatch.getMaxLod(); i++) {
-            if (distance < getLodDistanceThreshold() * (i + 1)*terrainPatch.getWorldScale().x || i == terrainPatch.getMaxLod()) {
+            if (distance < getLodDistanceThreshold() * (i + 1)*terrainPatch.getWorldScaleCached().x || i == terrainPatch.getMaxLod()) {
                 boolean reIndexNeeded = false;
                 if (i != terrainPatch.getLod()) {
                     reIndexNeeded = true;
                     //System.out.println("lod change: "+lod+" > "+i+"    dist: "+distance);
                 }
                 int prevLOD = terrainPatch.getLod();
-                //previousLod = lod;
-                //lod = i;
+                
                 UpdatedTerrainPatch utp = updates.get(terrainPatch.getName());
                 if (utp == null) {
-                    utp = new UpdatedTerrainPatch(terrainPatch, i);//save in here, do not update actual variables
+                    utp = new UpdatedTerrainPatch(terrainPatch);//save in here, do not update actual variables
                     updates.put(utp.getName(), utp);
                 }
+                utp.setNewLod(i);
                 utp.setPreviousLod(prevLOD);
-                utp.setReIndexNeeded(reIndexNeeded);
+                //utp.setReIndexNeeded(reIndexNeeded);
 
                 return reIndexNeeded;
             }
@@ -107,9 +108,9 @@ public class DistanceLodCalculator implements LodCalculator {
     }
 
     protected Vector3f getCenterLocation(TerrainPatch terrainPatch) {
-        Vector3f loc = terrainPatch.getWorldTranslation().clone();
-        loc.x += terrainPatch.getSize()*terrainPatch.getWorldScale().x / 2;
-        loc.z += terrainPatch.getSize()*terrainPatch.getWorldScale().z / 2;
+        Vector3f loc = terrainPatch.getWorldTranslationCached();
+        loc.x += terrainPatch.getSize()*terrainPatch.getWorldScaleCached().x / 2;
+        loc.z += terrainPatch.getSize()*terrainPatch.getWorldScaleCached().z / 2;
         return loc;
     }
 
index 2d6c364..bf1b6f8 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2010 jMonkeyEngine
+ * Copyright (c) 2009-2012 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -29,7 +29,6 @@
  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
-
 package com.jme3.terrain.geomipmap.lodcalc;
 
 import com.jme3.export.Savable;
index 6d5f5b8..bfbd24a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2010 jMonkeyEngine
+ * Copyright (c) 2009-2012 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -29,7 +29,6 @@
  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
-
 package com.jme3.terrain.geomipmap.lodcalc;
 
 import com.jme3.export.Savable;
index 29d5c25..ab6e003 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2010 jMonkeyEngine
+ * Copyright (c) 2009-2012 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -29,7 +29,6 @@
  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
-
 package com.jme3.terrain.geomipmap.lodcalc;
 
 import com.jme3.export.InputCapsule;
index b453e26..cb4a7d4 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2010 jMonkeyEngine
+ * Copyright (c) 2009-2012 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -29,7 +29,6 @@
  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
-
 package com.jme3.terrain.geomipmap.lodcalc;
 
 import com.jme3.export.JmeExporter;
index 73d5c35..b085a07 100644 (file)
@@ -1,5 +1,5 @@
 /*\r
- * Copyright (c) 2009-2010 jMonkeyEngine\r
+ * Copyright (c) 2009-2012 jMonkeyEngine\r
  * All rights reserved.\r
  *\r
  * Redistribution and use in source and binary forms, with or without\r
@@ -29,7 +29,6 @@
  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\r
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
  */\r
-\r
 package com.jme3.terrain.geomipmap.lodcalc;\r
 \r
 import com.jme3.export.Savable;\r
index 312b17d..b266996 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2010 jMonkeyEngine
+ * Copyright (c) 2009-2012 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -29,7 +29,6 @@
  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
-
 package com.jme3.terrain.geomipmap.lodcalc;
 
 import com.jme3.export.JmeExporter;
@@ -45,14 +44,13 @@ import java.util.List;
 
 public class PerspectiveLodCalculator implements LodCalculator {
 
-    private TerrainPatch patch;
     private Camera cam;
-    private float[] entropyDistances;
     private float pixelError;
+    private boolean turnOffLod = false;
 
     public PerspectiveLodCalculator() {}
     
-    public PerspectiveLodCalculator(Camera cam, float pixelError){
+    public PerspectiveLodCalculator(Camera cam, float pixelError) {
         this.cam = cam;
         this.pixelError = pixelError;
     }
@@ -74,21 +72,24 @@ public class PerspectiveLodCalculator implements LodCalculator {
         return A / T;
     }
     
-    public boolean calculateLod(List<Vector3f> locations, HashMap<String, UpdatedTerrainPatch> updates) {
-        return calculateLod(patch, locations, updates);
-    }
-
-    public boolean calculateLod(TerrainPatch terrainPatch, List<Vector3f> locations, HashMap<String, UpdatedTerrainPatch> updates) {
-        if (entropyDistances == null){
-            // compute entropy distances
-            float[] lodEntropies = patch.getLodEntropies();
-            entropyDistances = new float[lodEntropies.length];
-            float cameraConstant = getCameraConstant(cam, pixelError);
-            for (int i = 0; i < lodEntropies.length; i++){
-                entropyDistances[i] = lodEntropies[i] * cameraConstant;
+    public boolean calculateLod(TerrainPatch patch, List<Vector3f> locations, HashMap<String, UpdatedTerrainPatch> updates) {
+        if (turnOffLod) {
+            // set to full detail
+            int prevLOD = patch.getLod();
+            UpdatedTerrainPatch utp = updates.get(patch.getName());
+            if (utp == null) {
+                utp = new UpdatedTerrainPatch(patch);
+                updates.put(utp.getName(), utp);
             }
+            utp.setNewLod(0);
+            utp.setPreviousLod(prevLOD);
+            //utp.setReIndexNeeded(true);
+            return true;
         }
-
+        
+        float[] lodEntropies = patch.getLodEntropies();
+        float cameraConstant = getCameraConstant(cam, pixelError);
+        
         Vector3f patchPos = getCenterLocation(patch);
 
         // vector from camera to patch
@@ -98,7 +99,7 @@ public class PerspectiveLodCalculator implements LodCalculator {
 
         // go through each lod level to find the one we are in
         for (int i = 0; i <= patch.getMaxLod(); i++) {
-            if (distance < entropyDistances[i] || i == patch.getMaxLod()){
+            if (distance < lodEntropies[i] * cameraConstant || i == patch.getMaxLod()){
                 boolean reIndexNeeded = false;
                 if (i != patch.getLod()) {
                     reIndexNeeded = true;
@@ -106,15 +107,15 @@ public class PerspectiveLodCalculator implements LodCalculator {
                 }
                 int prevLOD = patch.getLod();
 
-                //previousLod = lod;
-                //lod = i;
+                
                 UpdatedTerrainPatch utp = updates.get(patch.getName());
                 if (utp == null) {
-                    utp = new UpdatedTerrainPatch(patch, i);//save in here, do not update actual variables
+                    utp = new UpdatedTerrainPatch(patch);//save in here, do not update actual variables
                     updates.put(utp.getName(), utp);
                 }
+                utp.setNewLod(i);
                 utp.setPreviousLod(prevLOD);
-                utp.setReIndexNeeded(reIndexNeeded);
+                //utp.setReIndexNeeded(reIndexNeeded);
                 return reIndexNeeded;
             }
         }
@@ -139,6 +140,7 @@ public class PerspectiveLodCalculator implements LodCalculator {
     }
 
     public void write(JmeExporter ex) throws IOException {
+        
     }
 
     public void read(JmeImporter im) throws IOException {
@@ -161,15 +163,15 @@ public class PerspectiveLodCalculator implements LodCalculator {
     }
 
     public void turnOffLod() {
-        //TODO
+        turnOffLod = true;
     }
-
+    
     public boolean isLodOff() {
-        return false; //TODO
+        return turnOffLod;
     }
     
     public void turnOnLod() {
-        //TODO
+        turnOffLod = false;
     }
     
 }
index bd512e5..6f7ea57 100644 (file)
@@ -1,5 +1,5 @@
 /*\r
- * Copyright (c) 2009-2010 jMonkeyEngine\r
+ * Copyright (c) 2009-2012 jMonkeyEngine\r
  * All rights reserved.\r
  *\r
  * Redistribution and use in source and binary forms, with or without\r
@@ -29,7 +29,6 @@
  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\r
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
  */\r
-\r
 package com.jme3.terrain.geomipmap.lodcalc;\r
 \r
 import com.jme3.export.InputCapsule;\r
index c358aae..0f63883 100644 (file)
@@ -1,3 +1,34 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
 package com.jme3.terrain.geomipmap.lodcalc.util;
 
 import com.jme3.bounding.BoundingBox;
@@ -9,8 +40,10 @@ import com.jme3.scene.Mesh;
 import com.jme3.scene.VertexBuffer;
 import com.jme3.scene.VertexBuffer.Type;
 import com.jme3.util.BufferUtils;
+import java.nio.Buffer;
 import java.nio.FloatBuffer;
 import java.nio.IntBuffer;
+import java.nio.ShortBuffer;
 
 /**
  * Computes the entropy value Î´ (delta) for a given terrain block and
@@ -22,7 +55,7 @@ import java.nio.IntBuffer;
  */
 public class EntropyComputeUtil {
 
-    public static float computeLodEntropy(Mesh terrainBlock, IntBuffer lodIndices){
+    public static float computeLodEntropy(Mesh terrainBlock, Buffer lodIndices){
         // Bounding box for the terrain block
         BoundingBox bbox = (BoundingBox) terrainBlock.getBound();
 
@@ -41,13 +74,17 @@ public class EntropyComputeUtil {
         VertexBuffer originalIndices = terrainBlock.getBuffer(Type.Index);
 
         terrainBlock.clearBuffer(Type.Index);
-        terrainBlock.setBuffer(Type.Index, 3, lodIndices);
+        if (lodIndices instanceof IntBuffer)
+            terrainBlock.setBuffer(Type.Index, 3, (IntBuffer)lodIndices);
+        else if (lodIndices instanceof ShortBuffer) {
+            terrainBlock.setBuffer(Type.Index, 3, (ShortBuffer) lodIndices);
+        }
 
         // Recalculate collision mesh
         terrainBlock.createCollisionData();
 
         float entropy = 0;
-        for (int i = 0; i < positions.capacity() / 3; i++){
+        for (int i = 0; i < positions.limit() / 3; i++){
             BufferUtils.populateFromBuffer(pos, positions, i);
 
             float realHeight = pos.y;
index 9f044c6..cf7efc0 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2010 jMonkeyEngine
+ * Copyright (c) 2009-2012 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -29,7 +29,6 @@
  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
-
 package com.jme3.terrain.geomipmap.picking;
 
 import com.jme3.collision.CollisionResult;
@@ -189,8 +188,8 @@ public class BresenhamTerrainPicker implements TerrainPicker {
      *            grid row
      * @param gridY
      *            grid column
-     * @param block
-     *            the TerrainBlock we are working with
+     * @param patch
+     *            the TerrainPatch we are working with
      * @return true if the grid point is valid for the given block, false if it
      *         is off the block.
      */
@@ -222,8 +221,8 @@ public class BresenhamTerrainPicker implements TerrainPicker {
      *
      * @param position
      *            the position to check at
-     * @param block
-     *            the block to get height values from
+     * @param patch
+     *            the patch to get height values from
      * @return an index to the height position of the given block.
      */
     protected int findClosestHeightIndex(Vector3f position, TerrainPatch patch) {
index 8d8a53d..28d968a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2010 jMonkeyEngine
+ * Copyright (c) 2009-2012 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -29,7 +29,6 @@
  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
-
 package com.jme3.terrain.geomipmap.picking;
 
 import com.jme3.math.Ray;
index 7ec316c..576dd15 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2010 jMonkeyEngine
+ * Copyright (c) 2009-2012 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -29,7 +29,6 @@
  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
-
 package com.jme3.terrain.geomipmap.picking;
 
 import com.jme3.collision.CollisionResult;
@@ -68,4 +67,12 @@ public class TerrainPickData implements Comparable {
         return 0;
     }
 
+    @Override
+    public boolean equals(Object obj) {
+        if(obj instanceof TerrainPickData){
+            return ((TerrainPickData)obj).compareTo(this) == 0;
+        }
+        return super.equals(obj);
+    }
+    
 }
index 67df8e7..0830884 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2010 jMonkeyEngine
+ * Copyright (c) 2009-2012 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -29,7 +29,6 @@
  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
-
 package com.jme3.terrain.geomipmap.picking;
 
 import com.jme3.collision.CollisionResults;
index 3193f52..21c5045 100644 (file)
@@ -1,5 +1,5 @@
 /*\r
- * Copyright (c) 2009-2010 jMonkeyEngine\r
+ * Copyright (c) 2009-2012 jMonkeyEngine\r
  * All rights reserved.\r
  *\r
  * Redistribution and use in source and binary forms, with or without\r
@@ -149,7 +149,7 @@ public abstract class AbstractHeightMap implements HeightMap {
      * @return the value at (x,z).\r
      */\r
     public float getTrueHeightAtPoint(int x, int z) {\r
-        //logger.info( heightData[x + (z*size)]);\r
+        //logger.fine( heightData[x + (z*size)]);\r
         return heightData[x + (z * size)];\r
     }\r
 \r
@@ -253,9 +253,12 @@ public abstract class AbstractHeightMap implements HeightMap {
             throw new Exception("Filename must not be null");\r
         }\r
         //open the streams and send the height data to the file.\r
+        FileOutputStream fos = null;\r
+        DataOutputStream dos = null;\r
         try {\r
-            FileOutputStream fos = new FileOutputStream(filename);\r
-            DataOutputStream dos = new DataOutputStream(fos);\r
+            fos = new FileOutputStream(filename);\r
+            dos = new DataOutputStream(fos);\r
+            \r
             for (int i = 0; i < size; i++) {\r
                 for (int j = 0; j < size; j++) {\r
                     dos.write((int) heightData[j + (i * size)]);\r
@@ -270,9 +273,16 @@ public abstract class AbstractHeightMap implements HeightMap {
         } catch (IOException e) {\r
             logger.log(Level.WARNING, "Error writing to file {0}", filename);\r
             return false;\r
+        } finally {\r
+            if (fos != null) {\r
+                fos.close();\r
+            }\r
+            if (dos != null) {\r
+                dos.close();\r
+            }\r
         }\r
 \r
-        logger.log(Level.INFO, "Saved terrain to {0}", filename);\r
+        logger.log(Level.FINE, "Saved terrain to {0}", filename);\r
         return true;\r
     }\r
 \r
index 7a25834..283f149 100644 (file)
@@ -1,5 +1,5 @@
 /*\r
- * Copyright (c) 2009-2010 jMonkeyEngine\r
+ * Copyright (c) 2009-2012 jMonkeyEngine\r
  * All rights reserved.\r
  *\r
  * Redistribution and use in source and binary forms, with or without\r
@@ -261,7 +261,7 @@ public class CombinerHeightMap extends AbstractHeightMap {
         }\r
 \r
 \r
-        logger.info("Created heightmap using Combiner");\r
+        logger.fine("Created heightmap using Combiner");\r
 \r
 \r
         return true;\r
index da7d782..2518eb8 100644 (file)
@@ -1,5 +1,5 @@
 /*\r
- * Copyright (c) 2009-2010 jMonkeyEngine\r
+ * Copyright (c) 2009-2012 jMonkeyEngine\r
  * All rights reserved.\r
  *\r
  * Redistribution and use in source and binary forms, with or without\r
@@ -96,7 +96,6 @@ public class FaultHeightMap extends AbstractHeightMap {
      * @param faultShape Shape of the fault -line or circle\r
      * @param minFaultHeight Height modified on each side\r
      * @param maxFaultHeight Height modified on each side\r
-     * @param faultHeight Height modified on each side\r
      * @param seed A seed to feed the Random generator\r
      * @see setFaultRange, setMinRadius, setMaxRadius\r
      */\r
@@ -122,7 +121,8 @@ public class FaultHeightMap extends AbstractHeightMap {
      * Create an heightmap with linear step faults.\r
      * @param size size of heightmap\r
      * @param iterations number of iterations\r
-     * @param faultHeight height to modify\r
+     * @param minFaultHeight Height modified on each side\r
+     * @param maxFaultHeight Height modified on each side\r
      */\r
     public FaultHeightMap(int size, int iterations, float minFaultHeight, float maxFaultHeight) throws Exception {\r
         this(size, iterations, FAULTTYPE_STEP, FAULTSHAPE_LINE, minFaultHeight, maxFaultHeight, new Random().nextLong());\r
@@ -150,7 +150,7 @@ public class FaultHeightMap extends AbstractHeightMap {
 \r
         normalizeTerrain(NORMALIZE_RANGE);\r
 \r
-        logger.log(Level.INFO, "Fault heightmap generated");\r
+        logger.log(Level.FINE, "Fault heightmap generated");\r
         return true;\r
     }\r
 \r
index ec2dfdf..d5aff58 100644 (file)
@@ -1,5 +1,5 @@
 /*\r
- * Copyright (c) 2009-2010 jMonkeyEngine\r
+ * Copyright (c) 2009-2012 jMonkeyEngine\r
  * All rights reserved.\r
  *\r
  * Redistribution and use in source and binary forms, with or without\r
@@ -216,7 +216,7 @@ public class FluidSimHeightMap extends AbstractHeightMap {
         }\r
         normalizeTerrain(NORMALIZE_RANGE);\r
 \r
-        logger.info("Created Heightmap using fluid simulation");\r
+        logger.fine("Created Heightmap using fluid simulation");\r
 \r
         return true;\r
     }\r
diff --git a/engine/src/terrain/com/jme3/terrain/heightmap/FractalHeightMapGrid.java b/engine/src/terrain/com/jme3/terrain/heightmap/FractalHeightMapGrid.java
deleted file mode 100644 (file)
index 9aa72ff..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-/*\r
- * Copyright (c) 2009-2010 jMonkeyEngine\r
- * All rights reserved.\r
- *\r
- * Redistribution and use in source and binary forms, with or without\r
- * modification, are permitted provided that the following conditions are\r
- * met:\r
- *\r
- * * Redistributions of source code must retain the above copyright\r
- *   notice, this list of conditions and the following disclaimer.\r
- *\r
- * * Redistributions in binary form must reproduce the above copyright\r
- *   notice, this list of conditions and the following disclaimer in the\r
- *   documentation and/or other materials provided with the distribution.\r
- *\r
- * * Neither the name of 'jMonkeyEngine' nor the names of its contributors\r
- *   may be used to endorse or promote products derived from this software\r
- *   without specific prior written permission.\r
- *\r
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\r
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED\r
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\r
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\r
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\r
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\r
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\r
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\r
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\r
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\r
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
- */\r
-package com.jme3.terrain.heightmap;\r
-\r
-import java.awt.image.BufferedImage;\r
-import java.io.File;\r
-import java.io.IOException;\r
-import java.nio.FloatBuffer;\r
-\r
-import javax.imageio.ImageIO;\r
-\r
-import org.novyon.noise.Basis;\r
-\r
-import com.jme3.math.Vector3f;\r
-import com.jme3.terrain.MapUtils;\r
-\r
-public class FractalHeightMapGrid implements HeightMapGrid {\r
-\r
-       public class FloatBufferHeightMap extends AbstractHeightMap {\r
-\r
-               private final FloatBuffer buffer;\r
-\r
-               public FloatBufferHeightMap(FloatBuffer buffer) {\r
-                       this.buffer = buffer;\r
-               }\r
-\r
-               @Override\r
-               public boolean load() {\r
-                       this.heightData = this.buffer.array();\r
-                       return true;\r
-               }\r
-\r
-       }\r
-\r
-       private int size;\r
-       private final Basis base;\r
-       private final String cacheDir;\r
-       private final float heightScale;\r
-\r
-       public FractalHeightMapGrid(Basis base, String cacheDir, float heightScale) {\r
-               this.base = base;\r
-               this.cacheDir = cacheDir;\r
-               this.heightScale = heightScale;\r
-       }\r
-\r
-       @Override\r
-       public HeightMap getHeightMapAt(Vector3f location) {\r
-               AbstractHeightMap heightmap = null;\r
-               if (this.cacheDir != null && new File(this.cacheDir, "terrain_" + (int) location.x + "_" + (int) location.z + ".png").exists()) {\r
-                       try {\r
-                               BufferedImage im = null;\r
-                               im = ImageIO.read(new File(this.cacheDir, "terrain_" + (int) location.x + "_" + (int) location.z + ".png"));\r
-                               heightmap = new Grayscale16BitHeightMap(im);\r
-                               heightmap.setHeightScale(heightScale);\r
-                       } catch (IOException e) {}\r
-               } else {\r
-                       FloatBuffer buffer = this.base.getBuffer(location.x * (this.size - 1), location.z * (this.size - 1), 0, this.size);\r
-                       if (this.cacheDir != null) {\r
-                               MapUtils.saveImage(MapUtils.toGrayscale16Image(buffer, this.size), new File(this.cacheDir, "terrain_" + (int) location.x\r
-                                               + "_" + (int) location.z + ".png"));\r
-                       }\r
-                       float[] arr = buffer.array();\r
-                       for (int i = 0; i < arr.length; i++) {\r
-                               arr[i] = arr[i] * this.heightScale;\r
-                       }\r
-                       heightmap = new FloatBufferHeightMap(buffer);\r
-               }\r
-               heightmap.load();\r
-               return heightmap;\r
-       }\r
-\r
-       @Override\r
-       public void setSize(int size) {\r
-               this.size = size;\r
-       }\r
-\r
-}\r
diff --git a/engine/src/terrain/com/jme3/terrain/heightmap/Grayscale16BitHeightMap.java b/engine/src/terrain/com/jme3/terrain/heightmap/Grayscale16BitHeightMap.java
deleted file mode 100644 (file)
index 29e307d..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (c) 2009-2010 jMonkeyEngine
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the distribution.
- *
- * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
- *   may be used to endorse or promote products derived from this software
- *   without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package com.jme3.terrain.heightmap;
-
-import java.awt.image.BufferedImage;
-import java.io.File;
-import java.io.IOException;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import javax.imageio.ImageIO;
-
-/**
- *
- * @author Anthyon
- */
-public class Grayscale16BitHeightMap extends AbstractHeightMap {
-
-    private BufferedImage image;
-
-    public Grayscale16BitHeightMap() {
-    }
-
-    public Grayscale16BitHeightMap(BufferedImage image) {
-        this.image = image;
-    }
-
-    public Grayscale16BitHeightMap(String filename) {
-        this(new File(filename));
-    }
-
-    public Grayscale16BitHeightMap(File file) {
-        try {
-            this.image = ImageIO.read(file);
-        } catch (IOException ex) {
-            Logger.getLogger(Grayscale16BitHeightMap.class.getName()).log(Level.SEVERE, null, ex);
-        }
-    }
-
-    @Override
-    public boolean load() {
-        return load(false, false);
-    }
-
-    public boolean load(boolean flipX, boolean flipY) {
-        int imageWidth = image.getWidth();
-        int imageHeight = image.getHeight();
-
-        if (imageWidth != imageHeight) {
-            throw new RuntimeException("imageWidth: " + imageWidth
-                    + " != imageHeight: " + imageHeight);
-        }
-
-        Object out = new short[imageWidth * imageHeight];
-        out = image.getData().getDataElements(0, 0, imageWidth, imageHeight, out);
-        short[] values = (short[]) out;
-        heightData = new float[imageWidth * imageHeight];
-        int i = 0;
-        for (int y = 0; y < imageHeight; y++) {
-            for (int x = 0; x < imageWidth; x++, i++) {
-                heightData[i] = heightScale * (values[i] & 0x0000FFFF) / 65536f;
-            }
-        }
-
-        return true;
-    }
-}
index 1bbed36..65731b1 100644 (file)
@@ -1,5 +1,5 @@
 /*\r
- * Copyright (c) 2009-2010 jMonkeyEngine\r
+ * Copyright (c) 2009-2012 jMonkeyEngine\r
  * All rights reserved.\r
  *\r
  * Redistribution and use in source and binary forms, with or without\r
@@ -29,7 +29,6 @@
  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\r
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
  */\r
-\r
 package com.jme3.terrain.heightmap;\r
 \r
 /**\r
index f06608a..ca12923 100644 (file)
@@ -1,6 +1,33 @@
 /*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 package com.jme3.terrain.heightmap;
 
@@ -10,6 +37,10 @@ import com.jme3.math.Vector3f;
  *
  * @author Anthyon
  */
+@Deprecated
+/**
+ * @Deprecated in favor of TerrainGridTileLoader
+ */
 public interface HeightMapGrid {
 
     public HeightMap getHeightMapAt(Vector3f location);
index 4a6a462..5a284d8 100644 (file)
@@ -1,5 +1,5 @@
 /*\r
- * Copyright (c) 2009-2010 jMonkeyEngine\r
+ * Copyright (c) 2009-2012 jMonkeyEngine\r
  * All rights reserved.\r
  *\r
  * Redistribution and use in source and binary forms, with or without\r
@@ -63,8 +63,6 @@ public class HillHeightMap extends AbstractHeightMap {
      *            the minimum radius of a hill\r
      * @param maxRadius\r
      *            the maximum radius of a hill\r
-     * @param flattening\r
-     *            the power of flattening done, 1 means none\r
      * @param seed\r
      *            the seed to generate the same heightmap again\r
      * @throws Exception\r
@@ -83,7 +81,7 @@ public class HillHeightMap extends AbstractHeightMap {
                     + "or minimum radius is greater than maximum radius, "\r
                     + "or power of flattening is below one");\r
         }\r
-        logger.info("Contructing hill heightmap using seed: " + seed);\r
+        logger.fine("Contructing hill heightmap using seed: " + seed);\r
         this.size = size;\r
         this.seed = seed;\r
         this.iterations = iterations;\r
@@ -105,8 +103,6 @@ public class HillHeightMap extends AbstractHeightMap {
      *            the minimum radius of a hill\r
      * @param maxRadius\r
      *            the maximum radius of a hill\r
-     * @param flattening\r
-     *            the power of flattening done, 1 means none\r
      * @throws Exception\r
      * @throws JmeException\r
      *             if size of the terrain is not greater that zero, or number of\r
@@ -144,7 +140,7 @@ public class HillHeightMap extends AbstractHeightMap {
 \r
         normalizeTerrain(NORMALIZE_RANGE);\r
 \r
-        logger.info("Created Heightmap using the Hill Algorithm");\r
+        logger.fine("Created Heightmap using the Hill Algorithm");\r
 \r
         return true;\r
     }\r
@@ -252,7 +248,7 @@ public class HillHeightMap extends AbstractHeightMap {
      * @param minRadius\r
      *            the minimum radius of a hill\r
      * @throws Exception\r
-     * @throw JmeException if the minimum radius is not greater than zero or not\r
+     * @throws JmeException if the minimum radius is not greater than zero or not\r
      *        lower than the maximum radius\r
      */\r
     public void setMinRadius(float minRadius) throws Exception {\r
index 37219b2..d4e9b22 100644 (file)
@@ -1,5 +1,5 @@
 /*\r
- * Copyright (c) 2009-2010 jMonkeyEngine\r
+ * Copyright (c) 2009-2012 jMonkeyEngine\r
  * All rights reserved.\r
  *\r
  * Redistribution and use in source and binary forms, with or without\r
  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\r
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
  */\r
-\r
 package com.jme3.terrain.heightmap;\r
 \r
-import java.awt.Graphics2D;\r
-import java.awt.Image;\r
-import java.awt.image.BufferedImage;\r
-import java.awt.image.ColorModel;\r
-import java.awt.image.PixelGrabber;\r
-import java.awt.image.WritableRaster;\r
+import com.jme3.math.ColorRGBA;\r
+import com.jme3.texture.Image;\r
+import com.jme3.texture.image.ImageRaster;\r
 \r
 /**\r
  * <code>ImageBasedHeightMap</code> is a height map created from the grayscale\r
@@ -48,95 +44,17 @@ import java.awt.image.WritableRaster;
  * @author Mike Kienenberger\r
  * @version $id$\r
  */\r
-public class ImageBasedHeightMap extends AbstractHeightMap {\r
-\r
-    static protected class ImageConverter {\r
-\r
-        // Posted by DrLaszloJamf to Java Technology Forums\r
-        //\r
-        // Copyright 1994-2004 Sun Microsystems, Inc. All Rights Reserved.\r
-        // Redistribution and use in source and binary forms, with or without\r
-        // modification, are permitted provided that the following conditions\r
-        // are met:\r
-        //\r
-        // Redistribution of source code must retain the above copyright notice,\r
-        // this list of conditions and the following disclaimer.\r
-        //\r
-        // Redistribution in binary form must reproduce the above copyright\r
-        // notice, this list of conditions and the following disclaimer in the\r
-        // documentation and/or other materials provided with the distribution.\r
-        //\r
-        // Neither the name of Sun Microsystems, Inc. or the names of\r
-        // contributors may be used to endorse or promote products derived from\r
-        // this software without specific prior written permission.\r
-        //\r
-        // This software is provided "AS IS," without a warranty of any kind.\r
-        // ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,\r
-        // INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A\r
-        // PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN\r
-        // MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR\r
-        // ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR\r
-        // DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN\r
-        // OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR\r
-        // FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE\r
-        // DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,\r
-        // ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF\r
-        // SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.\r
-        //\r
-        //\r
-        // You acknowledge that this software is not designed, licensed or\r
-        // intended for use in the design, construction, operation or\r
-        // maintenance of any nuclear facility.\r
-\r
-        //    preserves image's colormodel. Assumes image is loaded\r
-        public static BufferedImage toBufferedImage(Image image) {\r
-            if (image instanceof BufferedImage) return (BufferedImage) image;\r
-            ColorModel cm = getColorModel(image);\r
-            int width = image.getWidth(null);\r
-            int height = image.getHeight(null);\r
-            return copy(createBufferedImage(cm, width, height), image);\r
-        }\r
-\r
-        public static BufferedImage toBufferedImage(Image image, int type) {\r
-            if (image instanceof BufferedImage\r
-                    && ((BufferedImage) image).getType() == type)\r
-                    return (BufferedImage) image;\r
-            int width = image.getWidth(null);\r
-            int height = image.getHeight(null);\r
-            return copy(new BufferedImage(width, height, type), image);\r
-        }\r
-\r
-        //    Returns target. Assumes source is loaded\r
-        public static BufferedImage copy(BufferedImage target, Image source) {\r
-            Graphics2D g = target.createGraphics();\r
-            g.drawImage(source, 0, 0, null);\r
-            g.dispose();\r
-            return target;\r
-        }\r
-\r
-        public static ColorModel getColorModel(Image image) {\r
-            try {\r
-                PixelGrabber pg = new PixelGrabber(image, 0, 0, 1, 1, false);\r
-                pg.grabPixels();\r
-                return pg.getColorModel();\r
-            } catch (InterruptedException e) {\r
-                throw new RuntimeException("Unexpected interruption", e);\r
-            }\r
-        }\r
-\r
-        public static BufferedImage createBufferedImage(ColorModel cm, int w,\r
-                int h) {\r
-            WritableRaster raster = cm.createCompatibleWritableRaster(w, h);\r
-            boolean isRasterPremultiplied = cm.isAlphaPremultiplied();\r
-            return new BufferedImage(cm, raster, isRasterPremultiplied, null);\r
-        }\r
-    }\r
+public class ImageBasedHeightMap extends AbstractHeightMap implements ImageHeightmap {\r
     \r
     \r
     protected Image colorImage;\r
-    protected float dampen = 1.0f;\r
+    private float backwardsCompScale = 255f;\r
 \r
     \r
+    public void setImage(Image image) {\r
+        this.colorImage = image;\r
+    }\r
+    \r
     /**\r
      * Creates a HeightMap from an Image. The image will be converted to\r
      * grayscale, and the grayscale values will be used to generate the height\r
@@ -149,13 +67,12 @@ public class ImageBasedHeightMap extends AbstractHeightMap {
      *            Image to map to the height map.\r
      */\r
     public ImageBasedHeightMap(Image colorImage) {\r
-        this(colorImage, 1.0f);\r
+        this.colorImage = colorImage;\r
     }\r
     \r
-    public ImageBasedHeightMap(Image colorImage, float dampen) {\r
-       super();\r
+    public ImageBasedHeightMap(Image colorImage, float heightScale) {\r
        this.colorImage = colorImage;\r
-       this.dampen = dampen;\r
+        this.heightScale = heightScale;\r
     }\r
 \r
     /**\r
@@ -169,78 +86,47 @@ public class ImageBasedHeightMap extends AbstractHeightMap {
      * Get the grayscale value, or override in your own sub-classes\r
      */\r
     protected float calculateHeight(float red, float green, float blue) {\r
-        return (float) ((0.299 * red + 0.587 * green + 0.114 * blue) * dampen);\r
+        return (float) (0.299 * red + 0.587 * green + 0.114 * blue);\r
+    }\r
+    \r
+    protected float calculateHeight(ColorRGBA color) {\r
+        return (float) (0.299 * color.r + 0.587 * color.g + 0.114 * color.b);\r
+    }\r
+    \r
+    protected ImageRaster getImageRaster() {\r
+        return ImageRaster.create(colorImage);\r
     }\r
     \r
     public boolean load(boolean flipX, boolean flipY) {\r
 \r
-        // FUTURE: Rescale image if not square?\r
-        BufferedImage colorBufferedImage = ImageConverter.toBufferedImage(\r
-                colorImage, BufferedImage.TYPE_3BYTE_BGR);\r
-\r
-        boolean hasAlpha = colorBufferedImage.getColorModel().hasAlpha();\r
-\r
-        int imageWidth = colorBufferedImage.getWidth();\r
-        int imageHeight = colorBufferedImage.getHeight();\r
+        int imageWidth = colorImage.getWidth();\r
+        int imageHeight = colorImage.getHeight();\r
 \r
         if (imageWidth != imageHeight)\r
                 throw new RuntimeException("imageWidth: " + imageWidth\r
                         + " != imageHeight: " + imageHeight);\r
 \r
         size = imageWidth;\r
-\r
-        byte data[] = (byte[]) colorBufferedImage.getRaster().getDataElements(\r
-                0, 0, imageWidth, imageHeight, null);\r
-\r
-        int bytesPerPixel = 3;\r
-        int blueBase = 0;\r
-        if (hasAlpha) {\r
-            bytesPerPixel = 4;\r
-            blueBase = 1;\r
-        }\r
+        ImageRaster raster = getImageRaster();\r
 \r
         heightData = new float[(imageWidth * imageHeight)];\r
 \r
-        int startW = 0;\r
-        int endW = imageWidth-1;\r
-        if (flipX) {\r
-            startW = imageWidth-1;\r
-            endW = 0;\r
-        }\r
-        int startH = imageHeight-1;\r
-        int endH = 0;\r
-        if (flipY) {\r
-            startH = 0;\r
-            endH = imageHeight-1;\r
-        }\r
-\r
+        ColorRGBA colorStore = new ColorRGBA();\r
+        \r
         int index = 0;\r
         if (flipY) {\r
             for (int h = 0; h < imageHeight; ++h) {\r
                 if (flipX) {\r
                     for (int w = imageWidth - 1; w >= 0; --w) {\r
-                        int baseIndex = (h * imageWidth * bytesPerPixel)\r
-                                + (w * bytesPerPixel) + blueBase;\r
-                        float blue = data[baseIndex] >= 0 ? data[baseIndex]\r
-                                : (256 + (data[baseIndex]));\r
-                        float green = data[baseIndex + 1] >= 0 ? data[baseIndex + 1]\r
-                                : (256 + (data[baseIndex + 1]));\r
-                        float red = data[baseIndex + 2] >= 0 ? data[baseIndex + 2]\r
-                                : (256 + (data[baseIndex + 2]));\r
-                        heightData[index++] = calculateHeight(red,green,blue);\r
+                        //int baseIndex = (h * imageWidth)+ w;\r
+                        //heightData[index++] = getHeightAtPostion(raster, baseIndex, colorStore)*heightScale;\r
+                        heightData[index++] = calculateHeight(raster.getPixel(w, h, colorStore))*heightScale*backwardsCompScale;\r
                     }\r
                 } else {\r
                     for (int w = 0; w < imageWidth; ++w) {\r
-                        int baseIndex = (h * imageWidth * bytesPerPixel)\r
-                                + (w * bytesPerPixel) + blueBase;\r
-                        float blue = data[baseIndex] >= 0 ? data[baseIndex]\r
-                                : (256 + (data[baseIndex]));\r
-                        float green = data[baseIndex + 1] >= 0 ? data[baseIndex + 1]\r
-                                : (256 + (data[baseIndex + 1]));\r
-                        float red = data[baseIndex + 2] >= 0 ? data[baseIndex + 2]\r
-                                : (256 + (data[baseIndex + 2]));\r
-                        heightData[index++] = calculateHeight(red,green,blue);\r
-\r
+                        //int baseIndex = (h * imageWidth)+ w;\r
+                        //heightData[index++] = getHeightAtPostion(raster, baseIndex, colorStore)*heightScale;\r
+                        heightData[index++] = calculateHeight(raster.getPixel(w, h, colorStore))*heightScale*backwardsCompScale;\r
                     }\r
                 }\r
             }\r
@@ -248,103 +134,54 @@ public class ImageBasedHeightMap extends AbstractHeightMap {
             for (int h = imageHeight - 1; h >= 0; --h) {\r
                 if (flipX) {\r
                     for (int w = imageWidth - 1; w >= 0; --w) {\r
-                        int baseIndex = (h * imageWidth * bytesPerPixel)\r
-                                + (w * bytesPerPixel) + blueBase;\r
-                        float blue = data[baseIndex] >= 0 ? data[baseIndex]\r
-                                : (256 + (data[baseIndex]));\r
-                        float green = data[baseIndex + 1] >= 0 ? data[baseIndex + 1]\r
-                                : (256 + (data[baseIndex + 1]));\r
-                        float red = data[baseIndex + 2] >= 0 ? data[baseIndex + 2]\r
-                                : (256 + (data[baseIndex + 2]));\r
-                        heightData[index++] = calculateHeight(red,green,blue);\r
+                        //int baseIndex = (h * imageWidth)+ w;\r
+                        //heightData[index++] = getHeightAtPostion(raster, baseIndex, colorStore)*heightScale;\r
+                        heightData[index++] = calculateHeight(raster.getPixel(w, h, colorStore))*heightScale*backwardsCompScale;\r
                     }\r
                 } else {\r
                     for (int w = 0; w < imageWidth; ++w) {\r
-                        int baseIndex = (h * imageWidth * bytesPerPixel)\r
-                                + (w * bytesPerPixel) + blueBase;\r
-                        float blue = data[baseIndex] >= 0 ? data[baseIndex]\r
-                                : (256 + (data[baseIndex]));\r
-                        float green = data[baseIndex + 1] >= 0 ? data[baseIndex + 1]\r
-                                : (256 + (data[baseIndex + 1]));\r
-                        float red = data[baseIndex + 2] >= 0 ? data[baseIndex + 2]\r
-                                : (256 + (data[baseIndex + 2]));\r
-                        heightData[index++] = calculateHeight(red,green,blue);\r
+                        //int baseIndex = (h * imageWidth)+ w;\r
+                        //heightData[index++] = getHeightAtPostion(raster, baseIndex, colorStore)*heightScale;\r
+                        heightData[index++] = calculateHeight(raster.getPixel(w, h, colorStore))*heightScale*backwardsCompScale;\r
                     }\r
                 }\r
             }\r
         }\r
 \r
-        /*int index = 0;\r
-        if (flipY) {\r
-            for (int h = 0; h < imageHeight; ++h) {\r
-                if (flipX) {\r
-                    for (int w = imageWidth-1; w >= 0; --w) {\r
-                        int baseIndex = (h * imageWidth * bytesPerPixel)\r
-                                + (w * bytesPerPixel) + blueBase;\r
-                        float blue = data[baseIndex] >= 0 ? data[baseIndex]\r
-                                : (256 + (data[baseIndex]));\r
-                        float green = data[baseIndex + 1] >= 0 ? data[baseIndex + 1]\r
-                                : (256 + (data[baseIndex + 1]));\r
-                        float red = data[baseIndex + 2] >= 0 ? data[baseIndex + 2]\r
-                                : (256 + (data[baseIndex + 2]));\r
-\r
-                        float grayscale = (float) ((0.299 * red + 0.587 * green + 0.114 * blue) * dampen);\r
-\r
-                        heightData[index++] = grayscale;\r
-                    }\r
-                } else {\r
-                    for (int w = 0; w < imageWidth; ++w) {\r
-                        int baseIndex = (h * imageWidth * bytesPerPixel)\r
-                                + (w * bytesPerPixel) + blueBase;\r
-                        float blue = data[baseIndex] >= 0 ? data[baseIndex]\r
-                                : (256 + (data[baseIndex]));\r
-                        float green = data[baseIndex + 1] >= 0 ? data[baseIndex + 1]\r
-                                : (256 + (data[baseIndex + 1]));\r
-                        float red = data[baseIndex + 2] >= 0 ? data[baseIndex + 2]\r
-                                : (256 + (data[baseIndex + 2]));\r
-\r
-                        float grayscale = (float) ((0.299 * red + 0.587 * green + 0.114 * blue) * dampen);\r
-\r
-                        heightData[index++] = grayscale;\r
-                    }\r
-                }\r
-            }\r
-        } else {\r
-            for (int h = imageHeight-1; h >= 0; --h) {\r
-                if (flipX) {\r
-                    for (int w = imageWidth-1; w >= 0; --w) {\r
-                        int baseIndex = (h * imageWidth * bytesPerPixel)\r
-                                + (w * bytesPerPixel) + blueBase;\r
-                        float blue = data[baseIndex] >= 0 ? data[baseIndex]\r
-                                : (256 + (data[baseIndex]));\r
-                        float green = data[baseIndex + 1] >= 0 ? data[baseIndex + 1]\r
-                                : (256 + (data[baseIndex + 1]));\r
-                        float red = data[baseIndex + 2] >= 0 ? data[baseIndex + 2]\r
-                                : (256 + (data[baseIndex + 2]));\r
-\r
-                        float grayscale = (float) ((0.299 * red + 0.587 * green + 0.114 * blue) * dampen);\r
-\r
-                        heightData[index++] = grayscale;\r
-                    }\r
-                } else {\r
-                    for (int w = 0; w < imageWidth; ++w) {\r
-                        int baseIndex = (h * imageWidth * bytesPerPixel)\r
-                                + (w * bytesPerPixel) + blueBase;\r
-                        float blue = data[baseIndex] >= 0 ? data[baseIndex]\r
-                                : (256 + (data[baseIndex]));\r
-                        float green = data[baseIndex + 1] >= 0 ? data[baseIndex + 1]\r
-                                : (256 + (data[baseIndex + 1]));\r
-                        float red = data[baseIndex + 2] >= 0 ? data[baseIndex + 2]\r
-                                : (256 + (data[baseIndex + 2]));\r
-\r
-                        float grayscale = (float) ((0.299 * red + 0.587 * green + 0.114 * blue) * dampen);\r
-\r
-                        heightData[index++] = grayscale;\r
-                    }\r
-                }\r
-            }\r
-        }*/\r
-        \r
         return true;\r
     }\r
+    \r
+    /*protected float getHeightAtPostion(ImageRaster image, int position, ColorRGBA store) {\r
+        switch (image.getFormat()){\r
+            case RGBA8:\r
+                buf.position( position * 4 );\r
+                store.set(byte2float(buf.get()), byte2float(buf.get()), byte2float(buf.get()), byte2float(buf.get()));\r
+                return calculateHeight(store.r, store.g, store.b);\r
+            case ABGR8:\r
+                buf.position( position * 4 );\r
+                float a = byte2float(buf.get());\r
+                float b = byte2float(buf.get());\r
+                float g = byte2float(buf.get());\r
+                float r = byte2float(buf.get());\r
+                store.set(r,g,b,a);\r
+                return calculateHeight(store.r, store.g, store.b);\r
+            case RGB8:\r
+                buf.position( position * 3 );\r
+                store.set(byte2float(buf.get()), byte2float(buf.get()), byte2float(buf.get()), 1);\r
+                return calculateHeight(store.r, store.g, store.b);\r
+            case Luminance8:\r
+                buf.position( position );\r
+                return byte2float(buf.get())*255*heightScale;\r
+            case Luminance16:\r
+                ShortBuffer sbuf = buf.asShortBuffer();\r
+                sbuf.position( position );\r
+                return (sbuf.get() & 0xFFFF) / 65535f * 255f * heightScale;\r
+            default:\r
+                throw new UnsupportedOperationException("Image format: "+image.getFormat());\r
+        }\r
+    }\r
+    \r
+    private float byte2float(byte b){\r
+        return ((float)(b & 0xFF)) / 255f;\r
+    }*/\r
 }
\ No newline at end of file
index 42dbed8..f0d841c 100644 (file)
@@ -1,24 +1,57 @@
 /*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 package com.jme3.terrain.heightmap;
 
-import com.jme3.asset.AssetInfo;
-import com.jme3.asset.AssetKey;
 import com.jme3.asset.AssetManager;
 import com.jme3.asset.AssetNotFoundException;
+import com.jme3.asset.TextureKey;
 import com.jme3.math.Vector3f;
-import java.awt.image.BufferedImage;
-import java.io.IOException;
-import java.io.InputStream;
+import com.jme3.texture.Texture;
 import java.util.logging.Level;
 import java.util.logging.Logger;
-import javax.imageio.ImageIO;
 
 /**
- *
- * @author Anthyon
+ * Loads Terrain grid tiles with image heightmaps.
+ * By default it expects a 16-bit grayscale image as the heightmap, but
+ * you can also call setImageType(BufferedImage.TYPE_) to set it to be a different
+ * image type. If you do this, you must also set a custom ImageHeightmap that will
+ * understand and be able to parse the image. By default if you pass in an image of type
+ * BufferedImage.TYPE_3BYTE_BGR, it will use the ImageBasedHeightMap for you.
+ * 
+ * @author Anthyon, Brent Owens
+ */
+@Deprecated
+/**
+ * @Deprecated in favor of ImageTileLoader
  */
 public class ImageBasedHeightMapGrid implements HeightMapGrid {
 
@@ -26,6 +59,7 @@ public class ImageBasedHeightMapGrid implements HeightMapGrid {
     private final AssetManager assetManager;
     private final Namer namer;
     private int size;
+    
 
     public ImageBasedHeightMapGrid(final String textureBase, final String textureExt, AssetManager assetManager) {
         this(assetManager, new Namer() {
@@ -47,25 +81,21 @@ public class ImageBasedHeightMapGrid implements HeightMapGrid {
         int z = (int) location.z;
         
         AbstractHeightMap heightmap = null;
-        BufferedImage im = null;
+        //BufferedImage im = null;
         
         try {
             String name = namer.getName(x, z);
-            logger.log(Level.INFO, "Loading heightmap from file: {0}", name);
-            final AssetInfo assetInfo = assetManager.locateAsset(new AssetKey(name));
-            if (assetInfo != null){
-                InputStream in = assetInfo.openStream();
-                im = ImageIO.read(in);
-            } else {
-                im = new BufferedImage(size, size, BufferedImage.TYPE_USHORT_GRAY);
-                logger.log(Level.WARNING, "File: {0} not found, loading zero heightmap instead", name);
-            }
+            logger.log(Level.FINE, "Loading heightmap from file: {0}", name);
+            final Texture texture = assetManager.loadTexture(new TextureKey(name));
+            
             // CREATE HEIGHTMAP
-            heightmap = new Grayscale16BitHeightMap(im);
-            heightmap.setHeightScale(256);
+            heightmap = new ImageBasedHeightMap(texture.getImage());
+            
+            heightmap.setHeightScale(1);
             heightmap.load();
-        } catch (IOException e) {
+        
         } catch (AssetNotFoundException e) {
+            logger.log(Level.SEVERE, "Asset Not found! ", e);
         }
         return heightmap;
     }
diff --git a/engine/src/terrain/com/jme3/terrain/heightmap/ImageHeightmap.java b/engine/src/terrain/com/jme3/terrain/heightmap/ImageHeightmap.java
new file mode 100644 (file)
index 0000000..a61000e
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.terrain.heightmap;
+
+/**
+ * A heightmap that is built off an image.
+ * If you want to be able to supply different Image types to 
+ * ImageBaseHeightMapGrid, you need to implement this interface,
+ * and have that class extend Abstract heightmap.
+ * 
+ * @author bowens
+ * @deprecated
+ */
+public interface ImageHeightmap {
+    
+    /**
+     * Set the image to use for this heightmap
+     */
+    //public void setImage(Image image);
+    
+    /**
+     * The BufferedImage.TYPE_ that is supported
+     * by this ImageHeightmap
+     */
+    //public int getSupportedImageType();
+}
index acb2b12..9998d64 100644 (file)
@@ -1,5 +1,5 @@
 /*\r
- * Copyright (c) 2009-2010 jMonkeyEngine\r
+ * Copyright (c) 2009-2012 jMonkeyEngine\r
  * All rights reserved.\r
  *\r
  * Redistribution and use in source and binary forms, with or without\r
@@ -151,7 +151,7 @@ public class MidpointDisplacementHeightMap extends AbstractHeightMap {
 \r
         normalizeTerrain(NORMALIZE_RANGE);\r
 \r
-        logger.log(Level.INFO, "Midpoint displacement heightmap generated");\r
+        logger.log(Level.FINE, "Midpoint displacement heightmap generated");\r
         return true;\r
     }\r
 \r
index 73171ae..cfd9d67 100644 (file)
@@ -1,6 +1,33 @@
 /*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 package com.jme3.terrain.heightmap;
 
index 63dccc6..c684aee 100644 (file)
@@ -1,5 +1,5 @@
 /*\r
- * Copyright (c) 2009-2010 jMonkeyEngine\r
+ * Copyright (c) 2009-2012 jMonkeyEngine\r
  * All rights reserved.\r
  *\r
  * Redistribution and use in source and binary forms, with or without\r
@@ -318,7 +318,7 @@ public class ParticleDepositionHeightMap extends AbstractHeightMap {
         erodeTerrain();\r
         normalizeTerrain(NORMALIZE_RANGE);\r
 \r
-        logger.info("Created heightmap using Particle Deposition");\r
+        logger.fine("Created heightmap using Particle Deposition");\r
 \r
 \r
         return false;\r
index b838d6d..6cba906 100644 (file)
@@ -1,5 +1,5 @@
 /*\r
- * Copyright (c) 2009-2010 jMonkeyEngine\r
+ * Copyright (c) 2009-2012 jMonkeyEngine\r
  * All rights reserved.\r
  *\r
  * Redistribution and use in source and binary forms, with or without\r
@@ -33,12 +33,7 @@ package com.jme3.terrain.heightmap;
 \r
 import com.jme3.math.FastMath;\r
 import com.jme3.util.LittleEndien;\r
-import java.io.BufferedInputStream;\r
-import java.io.DataInputStream;\r
-import java.io.FileInputStream;\r
-import java.io.FileNotFoundException;\r
-import java.io.IOException;\r
-import java.io.InputStream;\r
+import java.io.*;\r
 import java.net.URL;\r
 import java.util.logging.Logger;\r
 \r
diff --git a/engine/src/terrain/com/jme3/terrain/noise/Basis.java b/engine/src/terrain/com/jme3/terrain/noise/Basis.java
new file mode 100644 (file)
index 0000000..9d640cd
--- /dev/null
@@ -0,0 +1,76 @@
+/**\r
+ * Copyright (c) 2011, Novyon Events\r
+ * \r
+ * All rights reserved.\r
+ * \r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions are met:\r
+ * \r
+ * - Redistributions of source code must retain the above copyright notice, this\r
+ * list of conditions and the following disclaimer.\r
+ * \r
+ * - Redistributions in binary form must reproduce the above copyright notice,\r
+ * this list of conditions and the following disclaimer in the documentation\r
+ * and/or other materials provided with the distribution.\r
+ * \r
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\r
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\r
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\r
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR\r
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\r
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+ * \r
+ * @author Anthyon\r
+ */\r
+package com.jme3.terrain.noise;\r
+\r
+import com.jme3.terrain.noise.basis.ImprovedNoise;\r
+import com.jme3.terrain.noise.modulator.Modulator;\r
+import java.nio.FloatBuffer;\r
+\r
+/**\r
+ * Interface for - basically 3D - noise generation algorithms, based on the\r
+ * book: Texturing & Modeling - A Procedural Approach\r
+ * \r
+ * The main concept is to look at noise as a basis for generating fractals.\r
+ * Basis can be anything, like a simple:\r
+ * \r
+ * <code>\r
+ * float value(float x, float y, float z) {\r
+ *             return 0; // a flat noise with 0 value everywhere\r
+ * }\r
+ * </code>\r
+ * \r
+ * or a more complex perlin noise ({@link ImprovedNoise}\r
+ * \r
+ * Fractals use these functions to generate a more complex result based on some\r
+ * frequency, roughness, etc values.\r
+ * \r
+ * Fractals themselves are implementing the Basis interface as well, opening\r
+ * an infinite range of results.\r
+ * \r
+ * @author Anthyon\r
+ * \r
+ * @since 2011\r
+ * \r
+ */\r
+public interface Basis {\r
+\r
+       public void init();\r
+\r
+       public Basis setScale(float scale);\r
+\r
+       public float getScale();\r
+\r
+       public Basis addModulator(Modulator modulator);\r
+\r
+       public float value(float x, float y, float z);\r
+\r
+       public FloatBuffer getBuffer(float sx, float sy, float base, int size);\r
+\r
+}\r
diff --git a/engine/src/terrain/com/jme3/terrain/noise/Color.java b/engine/src/terrain/com/jme3/terrain/noise/Color.java
new file mode 100644 (file)
index 0000000..719c0ff
--- /dev/null
@@ -0,0 +1,134 @@
+/**\r
+ * Copyright (c) 2011, Novyon Events\r
+ * \r
+ * All rights reserved.\r
+ * \r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions are met:\r
+ * \r
+ * - Redistributions of source code must retain the above copyright notice, this\r
+ * list of conditions and the following disclaimer.\r
+ * \r
+ * - Redistributions in binary form must reproduce the above copyright notice,\r
+ * this list of conditions and the following disclaimer in the documentation\r
+ * and/or other materials provided with the distribution.\r
+ * \r
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\r
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\r
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\r
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR\r
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\r
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+ * \r
+ * @author Anthyon\r
+ */\r
+package com.jme3.terrain.noise;\r
+\r
+/**\r
+ * Helper class for working with colors and gradients\r
+ * \r
+ * @author Anthyon\r
+ * \r
+ */\r
+public class Color {\r
+\r
+       private final float[] rgba = new float[4];\r
+\r
+       public Color() {}\r
+\r
+       public Color(final int r, final int g, final int b) {\r
+               this(r, g, b, 255);\r
+       }\r
+\r
+       public Color(final int r, final int g, final int b, final int a) {\r
+               this.rgba[0] = (r & 255) / 256f;\r
+               this.rgba[1] = (g & 255) / 256f;\r
+               this.rgba[2] = (b & 255) / 256f;\r
+               this.rgba[3] = (a & 255) / 256f;\r
+       }\r
+\r
+       public Color(final float r, final float g, final float b) {\r
+               this(r, g, b, 1);\r
+       }\r
+\r
+       public Color(final float r, final float g, final float b, final float a) {\r
+               this.rgba[0] = ShaderUtils.clamp(r, 0, 1);\r
+               this.rgba[1] = ShaderUtils.clamp(g, 0, 1);\r
+               this.rgba[2] = ShaderUtils.clamp(b, 0, 1);\r
+               this.rgba[3] = ShaderUtils.clamp(a, 0, 1);\r
+       }\r
+\r
+       public Color(final int h, final float s, final float b) {\r
+               this(h, s, b, 1);\r
+       }\r
+\r
+       public Color(final int h, final float s, final float b, final float a) {\r
+               this.rgba[3] = a;\r
+               if (s == 0) {\r
+                       // achromatic ( grey )\r
+                       this.rgba[0] = b;\r
+                       this.rgba[1] = b;\r
+                       this.rgba[2] = b;\r
+                       return;\r
+               }\r
+\r
+               float hh = h / 60.0f;\r
+               int i = ShaderUtils.floor(hh);\r
+               float f = hh - i;\r
+               float p = b * (1 - s);\r
+               float q = b * (1 - s * f);\r
+               float t = b * (1 - s * (1 - f));\r
+\r
+               if (i == 0) {\r
+                       this.rgba[0] = b;\r
+                       this.rgba[1] = t;\r
+                       this.rgba[2] = p;\r
+               } else if (i == 1) {\r
+                       this.rgba[0] = q;\r
+                       this.rgba[1] = b;\r
+                       this.rgba[2] = p;\r
+               } else if (i == 2) {\r
+                       this.rgba[0] = p;\r
+                       this.rgba[1] = b;\r
+                       this.rgba[2] = t;\r
+               } else if (i == 3) {\r
+                       this.rgba[0] = p;\r
+                       this.rgba[1] = q;\r
+                       this.rgba[2] = b;\r
+               } else if (i == 4) {\r
+                       this.rgba[0] = t;\r
+                       this.rgba[1] = p;\r
+                       this.rgba[2] = b;\r
+               } else {\r
+                       this.rgba[0] = b;\r
+                       this.rgba[1] = p;\r
+                       this.rgba[2] = q;\r
+               }\r
+       }\r
+\r
+       public int toInteger() {\r
+               return 0x00000000 | (int) (this.rgba[3] * 256) << 24 | (int) (this.rgba[0] * 256) << 16 | (int) (this.rgba[1] * 256) << 8\r
+                               | (int) (this.rgba[2] * 256);\r
+       }\r
+\r
+       public String toWeb() {\r
+               return Integer.toHexString(this.toInteger());\r
+       }\r
+\r
+       public Color toGrayscale() {\r
+               float v = (this.rgba[0] + this.rgba[1] + this.rgba[2]) / 3f;\r
+               return new Color(v, v, v, this.rgba[3]);\r
+       }\r
+\r
+       public Color toSepia() {\r
+               float r = ShaderUtils.clamp(this.rgba[0] * 0.393f + this.rgba[1] * 0.769f + this.rgba[2] * 0.189f, 0, 1);\r
+               float g = ShaderUtils.clamp(this.rgba[0] * 0.349f + this.rgba[1] * 0.686f + this.rgba[2] * 0.168f, 0, 1);\r
+               float b = ShaderUtils.clamp(this.rgba[0] * 0.272f + this.rgba[1] * 0.534f + this.rgba[2] * 0.131f, 0, 1);\r
+               return new Color(r, g, b, this.rgba[3]);\r
+       }\r
+}\r
diff --git a/engine/src/terrain/com/jme3/terrain/noise/Filter.java b/engine/src/terrain/com/jme3/terrain/noise/Filter.java
new file mode 100644 (file)
index 0000000..1dca8bf
--- /dev/null
@@ -0,0 +1,44 @@
+/**\r
+ * Copyright (c) 2011, Novyon Events\r
+ * \r
+ * All rights reserved.\r
+ * \r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions are met:\r
+ * \r
+ * - Redistributions of source code must retain the above copyright notice, this\r
+ * list of conditions and the following disclaimer.\r
+ * \r
+ * - Redistributions in binary form must reproduce the above copyright notice,\r
+ * this list of conditions and the following disclaimer in the documentation\r
+ * and/or other materials provided with the distribution.\r
+ * \r
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\r
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\r
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\r
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR\r
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\r
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+ * \r
+ * @author Anthyon\r
+ */\r
+package com.jme3.terrain.noise;\r
+\r
+import java.nio.FloatBuffer;\r
+\r
+public interface Filter {\r
+       public Filter addPreFilter(Filter filter);\r
+\r
+       public Filter addPostFilter(Filter filter);\r
+\r
+       public FloatBuffer doFilter(float sx, float sy, float base, FloatBuffer data, int size);\r
+\r
+       public int getMargin(int size, int margin);\r
+\r
+       public boolean isEnabled();\r
+}\r
diff --git a/engine/src/terrain/com/jme3/terrain/noise/ShaderUtils.java b/engine/src/terrain/com/jme3/terrain/noise/ShaderUtils.java
new file mode 100644 (file)
index 0000000..d1c2f6e
--- /dev/null
@@ -0,0 +1,288 @@
+/**\r
+ * Copyright (c) 2011, Novyon Events\r
+ * \r
+ * All rights reserved.\r
+ * \r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions are met:\r
+ * \r
+ * - Redistributions of source code must retain the above copyright notice, this\r
+ * list of conditions and the following disclaimer.\r
+ * \r
+ * - Redistributions in binary form must reproduce the above copyright notice,\r
+ * this list of conditions and the following disclaimer in the documentation\r
+ * and/or other materials provided with the distribution.\r
+ * \r
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\r
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\r
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\r
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR\r
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\r
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+ * \r
+ * @author Anthyon\r
+ */\r
+package com.jme3.terrain.noise;\r
+\r
+import java.awt.Color;\r
+import java.awt.Graphics2D;\r
+import java.awt.image.BufferedImage;\r
+import java.awt.image.DataBuffer;\r
+import java.awt.image.DataBufferInt;\r
+import java.awt.image.WritableRaster;\r
+import java.nio.ByteBuffer;\r
+import java.nio.ByteOrder;\r
+\r
+/**\r
+ * Helper class containing useful functions explained in the book:\r
+ * Texturing & Modeling - A Procedural Approach\r
+ * \r
+ * @author Anthyon\r
+ * \r
+ */\r
+public class ShaderUtils {\r
+\r
+       public static final float[] i2c(final int color) {\r
+               return new float[] { (color & 0x00ff0000) / 256f, (color & 0x0000ff00) / 256f, (color & 0x000000ff) / 256f,\r
+                               (color & 0xff000000) / 256f };\r
+       }\r
+\r
+       public static final int c2i(final float[] color) {\r
+               return (color.length == 4 ? (int) (color[3] * 256) : 0xff000000) | ((int) (color[0] * 256) << 16) | ((int) (color[1] * 256) << 8)\r
+                               | (int) (color[2] * 256);\r
+       }\r
+\r
+       public static final float mix(final float a, final float b, final float f) {\r
+               return (1 - f) * a + f * b;\r
+       }\r
+\r
+       public static final Color mix(final Color a, final Color b, final float f) {\r
+               return new Color((int) ShaderUtils.clamp(ShaderUtils.mix(a.getRed(), b.getRed(), f), 0, 255), (int) ShaderUtils.clamp(\r
+                               ShaderUtils.mix(a.getGreen(), b.getGreen(), f), 0, 255), (int) ShaderUtils.clamp(\r
+                               ShaderUtils.mix(a.getBlue(), b.getBlue(), f), 0, 255));\r
+       }\r
+\r
+       public static final int mix(final int a, final int b, final float f) {\r
+               return (int) ((1 - f) * a + f * b);\r
+       }\r
+\r
+       public static final float[] mix(final float[] c1, final float[] c2, final float f) {\r
+               return new float[] { ShaderUtils.mix(c1[0], c2[0], f), ShaderUtils.mix(c1[1], c2[1], f), ShaderUtils.mix(c1[2], c2[2], f) };\r
+       }\r
+\r
+       public static final float step(final float a, final float x) {\r
+               return x < a ? 0 : 1;\r
+       }\r
+\r
+       public static final float boxstep(final float a, final float b, final float x) {\r
+               return ShaderUtils.clamp((x - a) / (b - a), 0, 1);\r
+       }\r
+\r
+       public static final float pulse(final float a, final float b, final float x) {\r
+               return ShaderUtils.step(a, x) - ShaderUtils.step(b, x);\r
+       }\r
+\r
+       public static final float clamp(final float x, final float a, final float b) {\r
+               return x < a ? a : x > b ? b : x;\r
+       }\r
+\r
+       public static final float min(final float a, final float b) {\r
+               return a < b ? a : b;\r
+       }\r
+\r
+       public static final float max(final float a, final float b) {\r
+               return a > b ? a : b;\r
+       }\r
+\r
+       public static final float abs(final float x) {\r
+               return x < 0 ? -x : x;\r
+       }\r
+\r
+       public static final float smoothstep(final float a, final float b, final float x) {\r
+               if (x < a) {\r
+                       return 0;\r
+               } else if (x > b) {\r
+                       return 1;\r
+               }\r
+               float xx = (x - a) / (b - a);\r
+               return xx * xx * (3 - 2 * xx);\r
+       }\r
+\r
+       public static final float mod(final float a, final float b) {\r
+               int n = (int) (a / b);\r
+               float aa = a - n * b;\r
+               if (aa < 0) {\r
+                       aa += b;\r
+               }\r
+               return aa;\r
+       }\r
+\r
+       public static final int floor(final float x) {\r
+               return x > 0 ? (int) x : (int) x - 1;\r
+       }\r
+\r
+       public static final float ceil(final float x) {\r
+               return (int) x + (x > 0 && x != (int) x ? 1 : 0);\r
+       }\r
+\r
+       public static final float spline(float x, final float[] knot) {\r
+               float CR00 = -0.5f;\r
+               float CR01 = 1.5f;\r
+               float CR02 = -1.5f;\r
+               float CR03 = 0.5f;\r
+               float CR10 = 1.0f;\r
+               float CR11 = -2.5f;\r
+               float CR12 = 2.0f;\r
+               float CR13 = -0.5f;\r
+               float CR20 = -0.5f;\r
+               float CR21 = 0.0f;\r
+               float CR22 = 0.5f;\r
+               float CR23 = 0.0f;\r
+               float CR30 = 0.0f;\r
+               float CR31 = 1.0f;\r
+               float CR32 = 0.0f;\r
+               float CR33 = 0.0f;\r
+\r
+               int span;\r
+               int nspans = knot.length - 3;\r
+               float c0, c1, c2, c3; /* coefficients of the cubic. */\r
+               if (nspans < 1) {/* illegal */\r
+                       throw new RuntimeException("Spline has too few knots.");\r
+               }\r
+               /* Find the appropriate 4-point span of the spline. */\r
+               x = ShaderUtils.clamp(x, 0, 1) * nspans;\r
+               span = (int) x;\r
+               if (span >= knot.length - 3) {\r
+                       span = knot.length - 3;\r
+               }\r
+               x -= span;\r
+               /* Evaluate the span cubic at x using Horner’s rule. */\r
+               c3 = CR00 * knot[span + 0] + CR01 * knot[span + 1] + CR02 * knot[span + 2] + CR03 * knot[span + 3];\r
+               c2 = CR10 * knot[span + 0] + CR11 * knot[span + 1] + CR12 * knot[span + 2] + CR13 * knot[span + 3];\r
+               c1 = CR20 * knot[span + 0] + CR21 * knot[span + 1] + CR22 * knot[span + 2] + CR23 * knot[span + 3];\r
+               c0 = CR30 * knot[span + 0] + CR31 * knot[span + 1] + CR32 * knot[span + 2] + CR33 * knot[span + 3];\r
+               return ((c3 * x + c2) * x + c1) * x + c0;\r
+       }\r
+\r
+       public static final float[] spline(final float x, final float[][] knots) {\r
+               float[] retval = new float[knots.length];\r
+               for (int i = 0; i < knots.length; i++) {\r
+                       retval[i] = ShaderUtils.spline(x, knots[i]);\r
+               }\r
+               return retval;\r
+       }\r
+\r
+       public static final float gammaCorrection(final float gamma, final float x) {\r
+               return (float) Math.pow(x, 1 / gamma);\r
+       }\r
+\r
+       public static final float bias(final float b, final float x) {\r
+               return (float) Math.pow(x, Math.log(b) / Math.log(0.5));\r
+       }\r
+\r
+       public static final float gain(final float g, final float x) {\r
+               return x < 0.5 ? ShaderUtils.bias(1 - g, 2 * x) / 2 : 1 - ShaderUtils.bias(1 - g, 2 - 2 * x) / 2;\r
+       }\r
+\r
+       public static final float sinValue(final float s, final float minFreq, final float maxFreq, final float swidth) {\r
+               float value = 0;\r
+               float cutoff = ShaderUtils.clamp(0.5f / swidth, 0, maxFreq);\r
+               float f;\r
+               for (f = minFreq; f < 0.5 * cutoff; f *= 2) {\r
+                       value += Math.sin(2 * Math.PI * f * s) / f;\r
+               }\r
+               float fade = ShaderUtils.clamp(2 * (cutoff - f) / cutoff, 0, 1);\r
+               value += fade * Math.sin(2 * Math.PI * f * s) / f;\r
+               return value;\r
+       }\r
+\r
+       public static final float length(final float x, final float y, final float z) {\r
+               return (float) Math.sqrt(x * x + y * y + z * z);\r
+       }\r
+\r
+       public static final float[] rotate(final float[] v, final float[][] m) {\r
+               float x = v[0] * m[0][0] + v[1] * m[0][1] + v[2] * m[0][2];\r
+               float y = v[0] * m[1][0] + v[1] * m[1][1] + v[2] * m[1][2];\r
+               float z = v[0] * m[2][0] + v[1] * m[2][1] + v[2] * m[2][2];\r
+               return new float[] { x, y, z };\r
+       }\r
+\r
+       public static final float[][] calcRotationMatrix(final float ax, final float ay, final float az) {\r
+               float[][] retval = new float[3][3];\r
+               float cax = (float) Math.cos(ax);\r
+               float sax = (float) Math.sin(ax);\r
+               float cay = (float) Math.cos(ay);\r
+               float say = (float) Math.sin(ay);\r
+               float caz = (float) Math.cos(az);\r
+               float saz = (float) Math.sin(az);\r
+\r
+               retval[0][0] = cay * caz;\r
+               retval[0][1] = -cay * saz;\r
+               retval[0][2] = say;\r
+               retval[1][0] = sax * say * caz + cax * saz;\r
+               retval[1][1] = -sax * say * saz + cax * caz;\r
+               retval[1][2] = -sax * cay;\r
+               retval[2][0] = -cax * say * caz + sax * saz;\r
+               retval[2][1] = cax * say * saz + sax * caz;\r
+               retval[2][2] = cax * cay;\r
+\r
+               return retval;\r
+       }\r
+\r
+       public static final float[] normalize(final float[] v) {\r
+               float l = ShaderUtils.length(v);\r
+               float[] r = new float[v.length];\r
+               int i = 0;\r
+               for (float vv : v) {\r
+                       r[i++] = vv / l;\r
+               }\r
+               return r;\r
+       }\r
+\r
+       public static final float length(final float[] v) {\r
+               float s = 0;\r
+               for (float vv : v) {\r
+                       s += vv * vv;\r
+               }\r
+               return (float) Math.sqrt(s);\r
+       }\r
+\r
+       public static final ByteBuffer getImageDataFromImage(BufferedImage bufferedImage) {\r
+               WritableRaster wr;\r
+               DataBuffer db;\r
+\r
+               BufferedImage bi = new BufferedImage(128, 64, BufferedImage.TYPE_INT_ARGB);\r
+               Graphics2D g = bi.createGraphics();\r
+               g.drawImage(bufferedImage, null, null);\r
+               bufferedImage = bi;\r
+               wr = bi.getRaster();\r
+               db = wr.getDataBuffer();\r
+\r
+               DataBufferInt dbi = (DataBufferInt) db;\r
+               int[] data = dbi.getData();\r
+\r
+               ByteBuffer byteBuffer = ByteBuffer.allocateDirect(data.length * 4);\r
+               byteBuffer.order(ByteOrder.LITTLE_ENDIAN);\r
+               byteBuffer.asIntBuffer().put(data);\r
+               byteBuffer.flip();\r
+\r
+               return byteBuffer;\r
+       }\r
+\r
+       public static float frac(float f) {\r
+               return f - ShaderUtils.floor(f);\r
+       }\r
+\r
+       public static float[] floor(float[] fs) {\r
+               float[] retval = new float[fs.length];\r
+               for (int i = 0; i < fs.length; i++) {\r
+                       retval[i] = ShaderUtils.floor(fs[i]);\r
+               }\r
+               return retval;\r
+       }\r
+}\r
diff --git a/engine/src/terrain/com/jme3/terrain/noise/basis/FilteredBasis.java b/engine/src/terrain/com/jme3/terrain/noise/basis/FilteredBasis.java
new file mode 100644 (file)
index 0000000..8fec02e
--- /dev/null
@@ -0,0 +1,110 @@
+/**\r
+ * Copyright (c) 2011, Novyon Events\r
+ * \r
+ * All rights reserved.\r
+ * \r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions are met:\r
+ * \r
+ * - Redistributions of source code must retain the above copyright notice, this\r
+ * list of conditions and the following disclaimer.\r
+ * \r
+ * - Redistributions in binary form must reproduce the above copyright notice,\r
+ * this list of conditions and the following disclaimer in the documentation\r
+ * and/or other materials provided with the distribution.\r
+ * \r
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\r
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\r
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\r
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR\r
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\r
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+ * \r
+ * @author Anthyon\r
+ */\r
+package com.jme3.terrain.noise.basis;\r
+\r
+import com.jme3.terrain.noise.Basis;\r
+import com.jme3.terrain.noise.filter.AbstractFilter;\r
+import com.jme3.terrain.noise.modulator.Modulator;\r
+import java.nio.FloatBuffer;\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+\r
+public class FilteredBasis extends AbstractFilter implements Basis {\r
+\r
+       private Basis basis;\r
+       private List<Modulator> modulators = new ArrayList<Modulator>();\r
+       private float scale;\r
+\r
+       public FilteredBasis() {}\r
+\r
+       public FilteredBasis(Basis basis) {\r
+               this.basis = basis;\r
+       }\r
+\r
+       public Basis getBasis() {\r
+               return this.basis;\r
+       }\r
+\r
+       public void setBasis(Basis basis) {\r
+               this.basis = basis;\r
+       }\r
+\r
+       @Override\r
+       public FloatBuffer filter(float sx, float sy, float base, FloatBuffer data, int size) {\r
+               return data;\r
+       }\r
+\r
+       @Override\r
+       public void init() {\r
+               this.basis.init();\r
+       }\r
+\r
+       @Override\r
+       public Basis setScale(float scale) {\r
+               this.scale = scale;\r
+               return this;\r
+       }\r
+\r
+       @Override\r
+       public float getScale() {\r
+               return this.scale;\r
+       }\r
+\r
+       @Override\r
+       public Basis addModulator(Modulator modulator) {\r
+               this.modulators.add(modulator);\r
+               return this;\r
+       }\r
+\r
+       @Override\r
+       public float value(float x, float y, float z) {\r
+               throw new UnsupportedOperationException(\r
+                               "Method value cannot be called on FilteredBasis and its descendants. Use getBuffer instead!");\r
+       }\r
+\r
+       @Override\r
+       public FloatBuffer getBuffer(float sx, float sy, float base, int size) {\r
+               int margin = this.getMargin(size, 0);\r
+               int workSize = size + 2 * margin;\r
+               FloatBuffer retval = this.basis.getBuffer(sx - margin, sy - margin, base, workSize);\r
+               return this.clip(this.doFilter(sx, sy, base, retval, workSize), workSize, size, margin);\r
+       }\r
+\r
+       public FloatBuffer clip(FloatBuffer buf, int origSize, int newSize, int offset) {\r
+               FloatBuffer result = FloatBuffer.allocate(newSize * newSize);\r
+\r
+               float[] orig = buf.array();\r
+               for (int i = offset; i < offset + newSize; i++) {\r
+                       result.put(orig, i * origSize + offset, newSize);\r
+               }\r
+\r
+               return result;\r
+       }\r
+}\r
diff --git a/engine/src/terrain/com/jme3/terrain/noise/basis/ImprovedNoise.java b/engine/src/terrain/com/jme3/terrain/noise/basis/ImprovedNoise.java
new file mode 100644 (file)
index 0000000..9233ef8
--- /dev/null
@@ -0,0 +1,127 @@
+/**\r
+ * Copyright (c) 2011, Novyon Events\r
+ * \r
+ * All rights reserved.\r
+ * \r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions are met:\r
+ * \r
+ * - Redistributions of source code must retain the above copyright notice, this\r
+ * list of conditions and the following disclaimer.\r
+ * \r
+ * - Redistributions in binary form must reproduce the above copyright notice,\r
+ * this list of conditions and the following disclaimer in the documentation\r
+ * and/or other materials provided with the distribution.\r
+ * \r
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\r
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\r
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\r
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR\r
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\r
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+ * \r
+ * @author Anthyon\r
+ */\r
+package com.jme3.terrain.noise.basis;\r
+\r
+import com.jme3.terrain.noise.ShaderUtils;\r
+\r
+// JAVA REFERENCE IMPLEMENTATION OF IMPROVED NOISE - COPYRIGHT 2002 KEN PERLIN.\r
+/**\r
+ * Perlin's default implementation of Improved Perlin Noise\r
+ * designed to work with Noise base\r
+ */\r
+public final class ImprovedNoise extends Noise {\r
+\r
+       @Override\r
+       public void init() {\r
+\r
+       }\r
+\r
+       static public float noise(float x, float y, float z) {\r
+               int X = ShaderUtils.floor(x), // FIND UNIT CUBE THAT\r
+               Y = ShaderUtils.floor(y), // CONTAINS POINT.\r
+               Z = ShaderUtils.floor(z);\r
+               x -= X; // FIND RELATIVE X,Y,Z\r
+               y -= Y; // OF POINT IN CUBE.\r
+               z -= Z;\r
+               X = X & 255;\r
+               Y = Y & 255;\r
+               Z = Z & 255;\r
+               float u = ImprovedNoise.fade(x), // COMPUTE FADE CURVES\r
+               v = ImprovedNoise.fade(y), // FOR EACH OF X,Y,Z.\r
+               w = ImprovedNoise.fade(z);\r
+               int A = ImprovedNoise.p[X] + Y;\r
+               int AA = ImprovedNoise.p[A] + Z;\r
+               int AB = ImprovedNoise.p[A + 1] + Z;\r
+               int B = ImprovedNoise.p[X + 1] + Y;\r
+               int BA = ImprovedNoise.p[B] + Z;\r
+               int BB = ImprovedNoise.p[B + 1] + Z;\r
+\r
+               return ImprovedNoise.lerp(\r
+                               w,\r
+                               ImprovedNoise.lerp(\r
+                                               v,\r
+                                               ImprovedNoise.lerp(u, ImprovedNoise.grad3(ImprovedNoise.p[AA], x, y, z),\r
+                                                               ImprovedNoise.grad3(ImprovedNoise.p[BA], x - 1, y, z)), // BLENDED\r
+                                               ImprovedNoise.lerp(u, ImprovedNoise.grad3(ImprovedNoise.p[AB], x, y - 1, z), // RESULTS\r
+                                                               ImprovedNoise.grad3(ImprovedNoise.p[BB], x - 1, y - 1, z))),// FROM\r
+                               ImprovedNoise.lerp(v,\r
+                                               ImprovedNoise.lerp(u, ImprovedNoise.grad3(ImprovedNoise.p[AA + 1], x, y, z - 1), // CORNERS\r
+                                                               ImprovedNoise.grad3(ImprovedNoise.p[BA + 1], x - 1, y, z - 1)), // OF\r
+                                               ImprovedNoise.lerp(u, ImprovedNoise.grad3(ImprovedNoise.p[AB + 1], x, y - 1, z - 1),\r
+                                                               ImprovedNoise.grad3(ImprovedNoise.p[BB + 1], x - 1, y - 1, z - 1))));\r
+       }\r
+\r
+       static final float fade(final float t) {\r
+               return t * t * t * (t * (t * 6 - 15) + 10);\r
+       }\r
+\r
+       static final float lerp(final float t, final float a, final float b) {\r
+               return a + t * (b - a);\r
+       }\r
+\r
+       static float grad(final int hash, final float x, final float y, final float z) {\r
+               int h = hash & 15; // CONVERT LO 4 BITS OF HASH CODE\r
+               float u = h < 8 ? x : y, // INTO 12 GRADIENT DIRECTIONS.\r
+               v = h < 4 ? y : h == 12 || h == 14 ? x : z;\r
+               return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);\r
+       }\r
+\r
+       static final float grad3(final int hash, final float x, final float y, final float z) {\r
+               int h = hash & 15; // CONVERT LO 4 BITS OF HASH CODE\r
+               return x * ImprovedNoise.GRAD3[h][0] + y * ImprovedNoise.GRAD3[h][1] + z * ImprovedNoise.GRAD3[h][2];\r
+       }\r
+\r
+       static final int p[] = new int[512], permutation[] = { 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36,\r
+                       103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11,\r
+                       32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158,\r
+                       231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80,\r
+                       73, 209, 76, 132, 187, 208, 89, 18, 169, 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217,\r
+                       226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183,\r
+                       170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79, 113,\r
+                       224, 232, 178, 185, 112, 104, 218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235,\r
+                       249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205,\r
+                       93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180 };\r
+\r
+       private static float[][] GRAD3 = new float[][] { { 1, 1, 0 }, { -1, 1, 0 }, { 1, -1, 0 }, { -1, -1, 0 }, { 1, 0, 1 }, { -1, 0, 1 },\r
+                       { 1, 0, -1 }, { -1, 0, -1 }, { 0, 1, 1 }, { 0, -1, 1 }, { 0, 1, -1 }, { 0, -1, -1 }, { 1, 0, -1 }, { -1, 0, -1 }, { 0, -1, 1 },\r
+                       { 0, 1, 1 } };\r
+\r
+       static {\r
+               for (int i = 0; i < 256; i++) {\r
+                       ImprovedNoise.p[256 + i] = ImprovedNoise.p[i] = ImprovedNoise.permutation[i];\r
+               }\r
+       }\r
+\r
+       @Override\r
+       public float value(final float x, final float y, final float z) {\r
+               return ImprovedNoise.noise(this.scale * x, this.scale * y, this.scale * z);\r
+       }\r
+\r
+}\r
diff --git a/engine/src/terrain/com/jme3/terrain/noise/basis/Noise.java b/engine/src/terrain/com/jme3/terrain/noise/basis/Noise.java
new file mode 100644 (file)
index 0000000..0ceaa24
--- /dev/null
@@ -0,0 +1,93 @@
+/**\r
+ * Copyright (c) 2011, Novyon Events\r
+ * \r
+ * All rights reserved.\r
+ * \r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions are met:\r
+ * \r
+ * - Redistributions of source code must retain the above copyright notice, this\r
+ * list of conditions and the following disclaimer.\r
+ * \r
+ * - Redistributions in binary form must reproduce the above copyright notice,\r
+ * this list of conditions and the following disclaimer in the documentation\r
+ * and/or other materials provided with the distribution.\r
+ * \r
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\r
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\r
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\r
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR\r
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\r
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+ * \r
+ * @author Anthyon\r
+ */\r
+package com.jme3.terrain.noise.basis;\r
+\r
+import com.jme3.terrain.noise.Basis;\r
+import com.jme3.terrain.noise.modulator.Modulator;\r
+import com.jme3.terrain.noise.modulator.NoiseModulator;\r
+import java.nio.FloatBuffer;\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+\r
+/**\r
+ * Utility base class for Noise implementations\r
+ * \r
+ * @author Anthyon\r
+ * \r
+ */\r
+public abstract class Noise implements Basis {\r
+\r
+       protected List<Modulator> modulators = new ArrayList<Modulator>();\r
+\r
+       protected float scale = 1.0f;\r
+\r
+       @Override\r
+       public String toString() {\r
+               return this.getClass().getSimpleName();\r
+       }\r
+\r
+       @Override\r
+       public FloatBuffer getBuffer(float sx, float sy, float base, int size) {\r
+               FloatBuffer retval = FloatBuffer.allocate(size * size);\r
+               for (int y = 0; y < size; y++) {\r
+                       for (int x = 0; x < size; x++) {\r
+                               retval.put(this.modulate((sx + x) / size, (sy + y) / size, base));\r
+                       }\r
+               }\r
+               return retval;\r
+       }\r
+\r
+       public float modulate(float x, float y, float z) {\r
+               float retval = this.value(x, y, z);\r
+               for (Modulator m : this.modulators) {\r
+                       if (m instanceof NoiseModulator) {\r
+                               retval = m.value(retval);\r
+                       }\r
+               }\r
+               return retval;\r
+       }\r
+\r
+       @Override\r
+       public Basis addModulator(Modulator modulator) {\r
+               this.modulators.add(modulator);\r
+               return this;\r
+       }\r
+\r
+       @Override\r
+       public Basis setScale(float scale) {\r
+               this.scale = scale;\r
+               return this;\r
+       }\r
+\r
+       @Override\r
+       public float getScale() {\r
+               return this.scale;\r
+       }\r
+}\r
diff --git a/engine/src/terrain/com/jme3/terrain/noise/basis/NoiseAggregator.java b/engine/src/terrain/com/jme3/terrain/noise/basis/NoiseAggregator.java
new file mode 100644 (file)
index 0000000..8a547a3
--- /dev/null
@@ -0,0 +1,64 @@
+/**\r
+ * Copyright (c) 2011, Novyon Events\r
+ * \r
+ * All rights reserved.\r
+ * \r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions are met:\r
+ * \r
+ * - Redistributions of source code must retain the above copyright notice, this\r
+ * list of conditions and the following disclaimer.\r
+ * \r
+ * - Redistributions in binary form must reproduce the above copyright notice,\r
+ * this list of conditions and the following disclaimer in the documentation\r
+ * and/or other materials provided with the distribution.\r
+ * \r
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\r
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\r
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\r
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR\r
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\r
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+ * \r
+ * @author Anthyon\r
+ */\r
+package com.jme3.terrain.noise.basis;\r
+\r
+import com.jme3.terrain.noise.Basis;\r
+\r
+/**\r
+ * A simple aggregator basis. Takes two basis functions and a rate and return\r
+ * some mixed values\r
+ * \r
+ * @author Anthyon\r
+ * \r
+ */\r
+public class NoiseAggregator extends Noise {\r
+\r
+       private final float rate;\r
+       private final Basis a;\r
+       private final Basis b;\r
+\r
+       public NoiseAggregator(final Basis a, final Basis b, final float rate) {\r
+               this.a = a;\r
+               this.b = b;\r
+               this.rate = rate;\r
+       }\r
+\r
+       @Override\r
+       public void init() {\r
+               this.a.init();\r
+               this.b.init();\r
+       }\r
+\r
+       @Override\r
+       public float value(final float x, final float y, final float z) {\r
+               return this.a.value(x, y, z) * (1 - this.rate) + this.rate * this.b.value(x, y, z);\r
+       }\r
+\r
+}\r
diff --git a/engine/src/terrain/com/jme3/terrain/noise/filter/AbstractFilter.java b/engine/src/terrain/com/jme3/terrain/noise/filter/AbstractFilter.java
new file mode 100644 (file)
index 0000000..02715d5
--- /dev/null
@@ -0,0 +1,99 @@
+/**\r
+ * Copyright (c) 2011, Novyon Events\r
+ * \r
+ * All rights reserved.\r
+ * \r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions are met:\r
+ * \r
+ * - Redistributions of source code must retain the above copyright notice, this\r
+ * list of conditions and the following disclaimer.\r
+ * \r
+ * - Redistributions in binary form must reproduce the above copyright notice,\r
+ * this list of conditions and the following disclaimer in the documentation\r
+ * and/or other materials provided with the distribution.\r
+ * \r
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\r
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\r
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\r
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR\r
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\r
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+ * \r
+ * @author Anthyon\r
+ */\r
+package com.jme3.terrain.noise.filter;\r
+\r
+import com.jme3.terrain.noise.Filter;\r
+import java.nio.FloatBuffer;\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+\r
+public abstract class AbstractFilter implements Filter {\r
+\r
+       protected List<Filter> preFilters = new ArrayList<Filter>();\r
+       protected List<Filter> postFilters = new ArrayList<Filter>();\r
+\r
+       private boolean enabled = true;\r
+\r
+       @Override\r
+       public Filter addPreFilter(Filter filter) {\r
+               this.preFilters.add(filter);\r
+               return this;\r
+       }\r
+\r
+       @Override\r
+       public Filter addPostFilter(Filter filter) {\r
+               this.postFilters.add(filter);\r
+               return this;\r
+       }\r
+\r
+       @Override\r
+       public FloatBuffer doFilter(float sx, float sy, float base, FloatBuffer data, int size) {\r
+               if (!this.isEnabled()) {\r
+                       return data;\r
+               }\r
+               FloatBuffer retval = data;\r
+               for (Filter f : this.preFilters) {\r
+                       retval = f.doFilter(sx, sy, base, retval, size);\r
+               }\r
+               retval = this.filter(sx, sy, base, retval, size);\r
+               for (Filter f : this.postFilters) {\r
+                       retval = f.doFilter(sx, sy, base, retval, size);\r
+               }\r
+               return retval;\r
+       }\r
+\r
+       public abstract FloatBuffer filter(float sx, float sy, float base, FloatBuffer buffer, int size);\r
+\r
+       @Override\r
+       public int getMargin(int size, int margin) {\r
+               // TODO sums up all the margins from filters... maybe there's a more\r
+               // efficient algorithm\r
+               if (!this.isEnabled()) {\r
+                       return margin;\r
+               }\r
+               for (Filter f : this.preFilters) {\r
+                       margin = f.getMargin(size, margin);\r
+               }\r
+               for (Filter f : this.postFilters) {\r
+                       margin = f.getMargin(size, margin);\r
+               }\r
+               return margin;\r
+       }\r
+\r
+       @Override\r
+       public boolean isEnabled() {\r
+               return this.enabled;\r
+       }\r
+\r
+       public void setEnabled(boolean enabled) {\r
+               this.enabled = enabled;\r
+       }\r
+\r
+}\r
diff --git a/engine/src/terrain/com/jme3/terrain/noise/filter/HydraulicErodeFilter.java b/engine/src/terrain/com/jme3/terrain/noise/filter/HydraulicErodeFilter.java
new file mode 100644 (file)
index 0000000..db0580f
--- /dev/null
@@ -0,0 +1,153 @@
+/**\r
+ * Copyright (c) 2011, Novyon Events\r
+ * \r
+ * All rights reserved.\r
+ * \r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions are met:\r
+ * \r
+ * - Redistributions of source code must retain the above copyright notice, this\r
+ * list of conditions and the following disclaimer.\r
+ * \r
+ * - Redistributions in binary form must reproduce the above copyright notice,\r
+ * this list of conditions and the following disclaimer in the documentation\r
+ * and/or other materials provided with the distribution.\r
+ * \r
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\r
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\r
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\r
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR\r
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\r
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+ * \r
+ * @author Anthyon\r
+ */\r
+package com.jme3.terrain.noise.filter;\r
+\r
+import com.jme3.terrain.noise.Basis;\r
+import java.nio.FloatBuffer;\r
+\r
+public class HydraulicErodeFilter extends AbstractFilter {\r
+\r
+       private Basis waterMap;\r
+       private Basis sedimentMap;\r
+       private float Kr;\r
+       private float Ks;\r
+       private float Ke;\r
+       private float Kc;\r
+       private float T;\r
+\r
+       public void setKc(float kc) {\r
+               this.Kc = kc;\r
+       }\r
+\r
+       public void setKe(float ke) {\r
+               this.Ke = ke;\r
+       }\r
+\r
+       public void setKr(float kr) {\r
+               this.Kr = kr;\r
+       }\r
+\r
+       public void setKs(float ks) {\r
+               this.Ks = ks;\r
+       }\r
+\r
+       public void setSedimentMap(Basis sedimentMap) {\r
+               this.sedimentMap = sedimentMap;\r
+       }\r
+\r
+       public void setT(float t) {\r
+               this.T = t;\r
+       }\r
+\r
+       public void setWaterMap(Basis waterMap) {\r
+               this.waterMap = waterMap;\r
+       }\r
+\r
+       @Override\r
+       public int getMargin(int size, int margin) {\r
+               return super.getMargin(size, margin) + 1;\r
+       }\r
+\r
+       @Override\r
+       public FloatBuffer filter(float sx, float sy, float base, FloatBuffer buffer, int workSize) {\r
+               float[] ga = buffer.array();\r
+               // float[] wa = this.waterMap.getBuffer(sx, sy, base, workSize).array();\r
+               // float[] sa = this.sedimentMap.getBuffer(sx, sy, base,\r
+               // workSize).array();\r
+               float[] wt = new float[workSize * workSize];\r
+               float[] st = new float[workSize * workSize];\r
+\r
+               int[] idxrel = { -workSize - 1, -workSize + 1, workSize - 1, workSize + 1 };\r
+\r
+               // step 1. water arrives and step 2. captures material\r
+               for (int y = 0; y < workSize; y++) {\r
+                       for (int x = 0; x < workSize; x++) {\r
+                               int idx = y * workSize + x;\r
+                               float wtemp = this.Kr; // * wa[idx];\r
+                               float stemp = this.Ks; // * sa[idx];\r
+                               if (wtemp > 0) {\r
+                                       wt[idx] += wtemp;\r
+                                       if (stemp > 0) {\r
+                                               ga[idx] -= stemp * wt[idx];\r
+                                               st[idx] += stemp * wt[idx];\r
+                                       }\r
+                               }\r
+\r
+                               // step 3. water is transported to it's neighbours\r
+                               float a = ga[idx] + wt[idx];\r
+                               // float[] aj = new float[idxrel.length];\r
+                               float amax = 0;\r
+                               int amaxidx = -1;\r
+                               float ac = 0;\r
+                               float dtotal = 0;\r
+\r
+                               for (int j = 0; j < idxrel.length; j++) {\r
+                                       if (idx + idxrel[j] > 0 && idx + idxrel[j] < workSize) {\r
+                                               float at = ga[idx + idxrel[j]] + wt[idx + idxrel[j]];\r
+                                               if (a - at > a - amax) {\r
+                                                       dtotal += at;\r
+                                                       amax = at;\r
+                                                       amaxidx = j;\r
+                                                       ac++;\r
+                                               }\r
+                                       }\r
+                               }\r
+\r
+                               float aa = (dtotal + a) / (ac + 1);\r
+                               // for (int j = 0; j < idxrel.length; j++) {\r
+                               // if (idx + idxrel[j] > 0 && idx + idxrel[j] < workSize && a -\r
+                               // aj[j] > 0) {\r
+                               if (amaxidx > -1) {\r
+                                       float dwj = Math.min(wt[idx], a - aa) * (a - amax) / dtotal;\r
+                                       float dsj = st[idx] * dwj / wt[idx];\r
+                                       wt[idx] -= dwj;\r
+                                       st[idx] -= dsj;\r
+                                       wt[idx + idxrel[amaxidx]] += dwj;\r
+                                       st[idx + idxrel[amaxidx]] += dsj;\r
+                               }\r
+                               // }\r
+\r
+                               // step 4. water evaporates and deposits material\r
+                               wt[idx] = wt[idx] * (1 - this.Ke);\r
+                               if (wt[idx] < this.T) {\r
+                                       wt[idx] = 0;\r
+                               }\r
+                               float smax = this.Kc * wt[idx];\r
+                               if (st[idx] > smax) {\r
+                                       ga[idx] += st[idx] - smax;\r
+                                       st[idx] -= st[idx] - smax;\r
+                               }\r
+                       }\r
+               }\r
+\r
+               return buffer;\r
+       }\r
+\r
+}\r
diff --git a/engine/src/terrain/com/jme3/terrain/noise/filter/IterativeFilter.java b/engine/src/terrain/com/jme3/terrain/noise/filter/IterativeFilter.java
new file mode 100644 (file)
index 0000000..60bc512
--- /dev/null
@@ -0,0 +1,101 @@
+/**\r
+ * Copyright (c) 2011, Novyon Events\r
+ * \r
+ * All rights reserved.\r
+ * \r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions are met:\r
+ * \r
+ * - Redistributions of source code must retain the above copyright notice, this\r
+ * list of conditions and the following disclaimer.\r
+ * \r
+ * - Redistributions in binary form must reproduce the above copyright notice,\r
+ * this list of conditions and the following disclaimer in the documentation\r
+ * and/or other materials provided with the distribution.\r
+ * \r
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\r
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\r
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\r
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR\r
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\r
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+ * \r
+ * @author Anthyon\r
+ */\r
+package com.jme3.terrain.noise.filter;\r
+\r
+import com.jme3.terrain.noise.Filter;\r
+import java.nio.FloatBuffer;\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+\r
+public class IterativeFilter extends AbstractFilter {\r
+\r
+       private int iterations;\r
+\r
+       private List<Filter> preIterateFilters = new ArrayList<Filter>();\r
+       private List<Filter> postIterateFilters = new ArrayList<Filter>();\r
+       private Filter filter;\r
+\r
+       @Override\r
+       public int getMargin(int size, int margin) {\r
+               if (!this.isEnabled()) {\r
+                       return margin;\r
+               }\r
+               for (Filter f : this.preIterateFilters) {\r
+                       margin = f.getMargin(size, margin);\r
+               }\r
+               margin = this.filter.getMargin(size, margin);\r
+               for (Filter f : this.postIterateFilters) {\r
+                       margin = f.getMargin(size, margin);\r
+               }\r
+               return this.iterations * margin + super.getMargin(size, margin);\r
+       }\r
+\r
+       public void setIterations(int iterations) {\r
+               this.iterations = iterations;\r
+       }\r
+\r
+       public int getIterations() {\r
+               return this.iterations;\r
+       }\r
+\r
+       public IterativeFilter addPostIterateFilter(Filter filter) {\r
+               this.postIterateFilters.add(filter);\r
+               return this;\r
+       }\r
+\r
+       public IterativeFilter addPreIterateFilter(Filter filter) {\r
+               this.preIterateFilters.add(filter);\r
+               return this;\r
+       }\r
+\r
+       public void setFilter(Filter filter) {\r
+               this.filter = filter;\r
+       }\r
+\r
+       @Override\r
+       public FloatBuffer filter(float sx, float sy, float base, FloatBuffer data, int size) {\r
+               if (!this.isEnabled()) {\r
+                       return data;\r
+               }\r
+               FloatBuffer retval = data;\r
+\r
+               for (int i = 0; i < this.iterations; i++) {\r
+                       for (Filter f : this.preIterateFilters) {\r
+                               retval = f.doFilter(sx, sy, base, retval, size);\r
+                       }\r
+                       retval = this.filter.doFilter(sx, sy, base, retval, size);\r
+                       for (Filter f : this.postIterateFilters) {\r
+                               retval = f.doFilter(sx, sy, base, retval, size);\r
+                       }\r
+               }\r
+\r
+               return retval;\r
+       }\r
+}\r
diff --git a/engine/src/terrain/com/jme3/terrain/noise/filter/OptimizedErode.java b/engine/src/terrain/com/jme3/terrain/noise/filter/OptimizedErode.java
new file mode 100644 (file)
index 0000000..fc20da6
--- /dev/null
@@ -0,0 +1,113 @@
+/**\r
+ * Copyright (c) 2011, Novyon Events\r
+ * \r
+ * All rights reserved.\r
+ * \r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions are met:\r
+ * \r
+ * - Redistributions of source code must retain the above copyright notice, this\r
+ * list of conditions and the following disclaimer.\r
+ * \r
+ * - Redistributions in binary form must reproduce the above copyright notice,\r
+ * this list of conditions and the following disclaimer in the documentation\r
+ * and/or other materials provided with the distribution.\r
+ * \r
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\r
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\r
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\r
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR\r
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\r
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+ * \r
+ * @author Anthyon\r
+ */\r
+package com.jme3.terrain.noise.filter;\r
+\r
+import java.nio.FloatBuffer;\r
+\r
+public class OptimizedErode extends AbstractFilter {\r
+\r
+       private float talus;\r
+       private int radius;\r
+\r
+       public OptimizedErode setRadius(int radius) {\r
+               this.radius = radius;\r
+               return this;\r
+       }\r
+\r
+       public int getRadius() {\r
+               return this.radius;\r
+       }\r
+\r
+       public OptimizedErode setTalus(float talus) {\r
+               this.talus = talus;\r
+               return this;\r
+       }\r
+\r
+       public float getTalus() {\r
+               return this.talus;\r
+       }\r
+\r
+       @Override\r
+       public int getMargin(int size, int margin) {\r
+               return super.getMargin(size, margin) + this.radius;\r
+       }\r
+\r
+       @Override\r
+       public FloatBuffer filter(float sx, float sy, float base, FloatBuffer buffer, int size) {\r
+               float[] tmp = buffer.array();\r
+               float[] retval = new float[tmp.length];\r
+\r
+               for (int y = this.radius + 1; y < size - this.radius; y++) {\r
+                       for (int x = this.radius + 1; x < size - this.radius; x++) {\r
+                               int idx = y * size + x;\r
+                               float h = tmp[idx];\r
+\r
+                               float horizAvg = 0;\r
+                               int horizCount = 0;\r
+                               float vertAvg = 0;\r
+                               int vertCount = 0;\r
+\r
+                               boolean horizT = false;\r
+                               boolean vertT = false;\r
+\r
+                               for (int i = 0; i >= -this.radius; i--) {\r
+                                       int idxV = (y + i) * size + x;\r
+                                       int idxVL = (y + i - 1) * size + x;\r
+                                       int idxH = y * size + x + i;\r
+                                       int idxHL = y * size + x + i - 1;\r
+                                       float hV = tmp[idxV];\r
+                                       float hH = tmp[idxH];\r
+\r
+                                       if (Math.abs(h - hV) > this.talus && Math.abs(h - tmp[idxVL]) > this.talus || vertT) {\r
+                                               vertT = true;\r
+                                       } else {\r
+                                               if (Math.abs(h - hV) <= this.talus) {\r
+                                                       vertAvg += hV;\r
+                                                       vertCount++;\r
+                                               }\r
+                                       }\r
+\r
+                                       if (Math.abs(h - hH) > this.talus && Math.abs(h - tmp[idxHL]) > this.talus || horizT) {\r
+                                               horizT = true;\r
+                                       } else {\r
+                                               if (Math.abs(h - hH) <= this.talus) {\r
+                                                       horizAvg += hH;\r
+                                                       horizCount++;\r
+                                               }\r
+                                       }\r
+                               }\r
+\r
+                               retval[idx] = 0.5f * (vertAvg / (vertCount > 0 ? vertCount : 1) + horizAvg / (horizCount > 0 ? horizCount : 1));\r
+                       }\r
+               }\r
+               return FloatBuffer.wrap(retval);\r
+       }\r
+\r
+}\r
diff --git a/engine/src/terrain/com/jme3/terrain/noise/filter/PerturbFilter.java b/engine/src/terrain/com/jme3/terrain/noise/filter/PerturbFilter.java
new file mode 100644 (file)
index 0000000..782f384
--- /dev/null
@@ -0,0 +1,97 @@
+/**\r
+ * Copyright (c) 2011, Novyon Events\r
+ * \r
+ * All rights reserved.\r
+ * \r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions are met:\r
+ * \r
+ * - Redistributions of source code must retain the above copyright notice, this\r
+ * list of conditions and the following disclaimer.\r
+ * \r
+ * - Redistributions in binary form must reproduce the above copyright notice,\r
+ * this list of conditions and the following disclaimer in the documentation\r
+ * and/or other materials provided with the distribution.\r
+ * \r
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\r
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\r
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\r
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR\r
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\r
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+ * \r
+ * @author Anthyon\r
+ */\r
+package com.jme3.terrain.noise.filter;\r
+\r
+import com.jme3.terrain.noise.ShaderUtils;\r
+import com.jme3.terrain.noise.fractal.FractalSum;\r
+import java.nio.FloatBuffer;\r
+import java.util.logging.Logger;\r
+\r
+public class PerturbFilter extends AbstractFilter {\r
+\r
+       private float magnitude;\r
+\r
+       @Override\r
+       public int getMargin(int size, int margin) {\r
+               margin = super.getMargin(size, margin);\r
+               return (int) Math.floor(this.magnitude * (margin + size) + margin);\r
+       }\r
+\r
+       public void setMagnitude(float magnitude) {\r
+               this.magnitude = magnitude;\r
+       }\r
+\r
+       public float getMagnitude() {\r
+               return this.magnitude;\r
+       }\r
+\r
+       @Override\r
+       public FloatBuffer filter(float sx, float sy, float base, FloatBuffer data, int workSize) {\r
+               float[] arr = data.array();\r
+               int origSize = (int) Math.ceil(workSize / (2 * this.magnitude + 1));\r
+               int offset = (workSize - origSize) / 2;\r
+               Logger.getLogger(PerturbFilter.class.getCanonicalName()).info(\r
+                               "Found origSize : " + origSize + " and offset: " + offset + " for workSize : " + workSize + " and magnitude : "\r
+                                               + this.magnitude);\r
+               float[] retval = new float[workSize * workSize];\r
+               float[] perturbx = new FractalSum().setOctaves(8).setScale(5f).getBuffer(sx, sy, base, workSize).array();\r
+               float[] perturby = new FractalSum().setOctaves(8).setScale(5f).getBuffer(sx, sy, base + 1, workSize).array();\r
+               for (int y = 0; y < workSize; y++) {\r
+                       for (int x = 0; x < workSize; x++) {\r
+                               // Perturb our coordinates\r
+                               float noisex = perturbx[y * workSize + x];\r
+                               float noisey = perturby[y * workSize + x];\r
+\r
+                               int px = (int) (origSize * noisex * this.magnitude);\r
+                               int py = (int) (origSize * noisey * this.magnitude);\r
+\r
+                               float c00 = arr[this.wrap(y - py, workSize) * workSize + this.wrap(x - px, workSize)];\r
+                               float c01 = arr[this.wrap(y - py, workSize) * workSize + this.wrap(x + px, workSize)];\r
+                               float c10 = arr[this.wrap(y + py, workSize) * workSize + this.wrap(x - px, workSize)];\r
+                               float c11 = arr[this.wrap(y + py, workSize) * workSize + this.wrap(x + px, workSize)];\r
+\r
+                               float c0 = ShaderUtils.mix(c00, c01, noisex);\r
+                               float c1 = ShaderUtils.mix(c10, c11, noisex);\r
+                               retval[y * workSize + x] = ShaderUtils.mix(c0, c1, noisey);\r
+                       }\r
+               }\r
+               return FloatBuffer.wrap(retval);\r
+       }\r
+\r
+       private int wrap(int v, int size) {\r
+               if (v < 0) {\r
+                       return v + size - 1;\r
+               } else if (v >= size) {\r
+                       return v - size;\r
+               } else {\r
+                       return v;\r
+               }\r
+       }\r
+}\r
diff --git a/engine/src/terrain/com/jme3/terrain/noise/filter/SmoothFilter.java b/engine/src/terrain/com/jme3/terrain/noise/filter/SmoothFilter.java
new file mode 100644 (file)
index 0000000..1c20c24
--- /dev/null
@@ -0,0 +1,80 @@
+/**\r
+ * Copyright (c) 2011, Novyon Events\r
+ * \r
+ * All rights reserved.\r
+ * \r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions are met:\r
+ * \r
+ * - Redistributions of source code must retain the above copyright notice, this\r
+ * list of conditions and the following disclaimer.\r
+ * \r
+ * - Redistributions in binary form must reproduce the above copyright notice,\r
+ * this list of conditions and the following disclaimer in the documentation\r
+ * and/or other materials provided with the distribution.\r
+ * \r
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\r
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\r
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\r
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR\r
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\r
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+ * \r
+ * @author Anthyon\r
+ */\r
+package com.jme3.terrain.noise.filter;\r
+\r
+import java.nio.FloatBuffer;\r
+\r
+public class SmoothFilter extends AbstractFilter {\r
+\r
+       private int radius;\r
+       private float effect;\r
+\r
+       public void setRadius(int radius) {\r
+               this.radius = radius;\r
+       }\r
+\r
+       public int getRadius() {\r
+               return this.radius;\r
+       }\r
+\r
+       public void setEffect(float effect) {\r
+               this.effect = effect;\r
+       }\r
+\r
+       public float getEffect() {\r
+               return this.effect;\r
+       }\r
+\r
+       @Override\r
+       public int getMargin(int size, int margin) {\r
+               return super.getMargin(size, margin) + this.radius;\r
+       }\r
+\r
+       @Override\r
+       public FloatBuffer filter(float sx, float sy, float base, FloatBuffer buffer, int size) {\r
+               float[] data = buffer.array();\r
+               float[] retval = new float[data.length];\r
+\r
+               for (int y = this.radius; y < size - this.radius; y++) {\r
+                       for (int x = this.radius; x < size - this.radius; x++) {\r
+                               int idx = y * size + x;\r
+                               float n = 0;\r
+                               for (int i = -this.radius; i < this.radius + 1; i++) {\r
+                                       for (int j = -this.radius; j < this.radius + 1; j++) {\r
+                                               n += data[(y + i) * size + x + j];\r
+                                       }\r
+                               }\r
+                               retval[idx] = this.effect * n / (4 * this.radius * (this.radius + 1) + 1) + (1 - this.effect) * data[idx];\r
+                       }\r
+               }\r
+\r
+               return FloatBuffer.wrap(retval);\r
+       }\r
+}\r
diff --git a/engine/src/terrain/com/jme3/terrain/noise/filter/ThermalErodeFilter.java b/engine/src/terrain/com/jme3/terrain/noise/filter/ThermalErodeFilter.java
new file mode 100644 (file)
index 0000000..2e49679
--- /dev/null
@@ -0,0 +1,101 @@
+/**\r
+ * Copyright (c) 2011, Novyon Events\r
+ * \r
+ * All rights reserved.\r
+ * \r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions are met:\r
+ * \r
+ * - Redistributions of source code must retain the above copyright notice, this\r
+ * list of conditions and the following disclaimer.\r
+ * \r
+ * - Redistributions in binary form must reproduce the above copyright notice,\r
+ * this list of conditions and the following disclaimer in the documentation\r
+ * and/or other materials provided with the distribution.\r
+ * \r
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\r
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\r
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\r
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR\r
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\r
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+ * \r
+ * @author Anthyon\r
+ */\r
+package com.jme3.terrain.noise.filter;\r
+\r
+import java.nio.FloatBuffer;\r
+\r
+public class ThermalErodeFilter extends AbstractFilter {\r
+\r
+       private float talus;\r
+       private float c;\r
+\r
+       public ThermalErodeFilter setC(float c) {\r
+               this.c = c;\r
+               return this;\r
+       }\r
+\r
+       public ThermalErodeFilter setTalus(float talus) {\r
+               this.talus = talus;\r
+               return this;\r
+       }\r
+\r
+       @Override\r
+       public int getMargin(int size, int margin) {\r
+               return super.getMargin(size, margin) + 1;\r
+       }\r
+\r
+       @Override\r
+       public FloatBuffer filter(float sx, float sy, float base, FloatBuffer buffer, int workSize) {\r
+               float[] ga = buffer.array();\r
+               float[] sa = new float[workSize * workSize];\r
+\r
+               int[] idxrel = { -workSize - 1, -workSize + 1, workSize - 1, workSize + 1 };\r
+\r
+               for (int y = 0; y < workSize; y++) {\r
+                       for (int x = 0; x < workSize; x++) {\r
+                               int idx = y * workSize + x;\r
+                               ga[idx] += sa[idx];\r
+                               sa[idx] = 0;\r
+\r
+                               float[] deltas = new float[idxrel.length];\r
+                               float deltaMax = this.talus;\r
+                               float deltaTotal = 0;\r
+\r
+                               for (int j = 0; j < idxrel.length; j++) {\r
+                                       if (idx + idxrel[j] > 0 && idx + idxrel[j] < ga.length) {\r
+                                               float dj = ga[idx] - ga[idx + idxrel[j]];\r
+                                               if (dj > this.talus) {\r
+                                                       deltas[j] = dj;\r
+                                                       deltaTotal += dj;\r
+                                                       if (dj > deltaMax) {\r
+                                                               deltaMax = dj;\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                               }\r
+\r
+                               for (int j = 0; j < idxrel.length; j++) {\r
+                                       if (deltas[j] != 0) {\r
+                                               float d = this.c * (deltaMax - this.talus) * deltas[j] / deltaTotal;\r
+                                               if (d > ga[idx] + sa[idx]) {\r
+                                                       d = ga[idx] + sa[idx];\r
+                                               }\r
+                                               sa[idx] -= d;\r
+                                               sa[idx + idxrel[j]] += d;\r
+                                       }\r
+                                       deltas[j] = 0;\r
+                               }\r
+                       }\r
+               }\r
+\r
+               return buffer;\r
+       }\r
+\r
+}\r
diff --git a/engine/src/terrain/com/jme3/terrain/noise/fractal/Fractal.java b/engine/src/terrain/com/jme3/terrain/noise/fractal/Fractal.java
new file mode 100644 (file)
index 0000000..9b53447
--- /dev/null
@@ -0,0 +1,57 @@
+/**\r
+ * Copyright (c) 2011, Novyon Events\r
+ * \r
+ * All rights reserved.\r
+ * \r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions are met:\r
+ * \r
+ * - Redistributions of source code must retain the above copyright notice, this\r
+ * list of conditions and the following disclaimer.\r
+ * \r
+ * - Redistributions in binary form must reproduce the above copyright notice,\r
+ * this list of conditions and the following disclaimer in the documentation\r
+ * and/or other materials provided with the distribution.\r
+ * \r
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\r
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\r
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\r
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR\r
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\r
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+ * \r
+ * @author Anthyon\r
+ */\r
+package com.jme3.terrain.noise.fractal;\r
+\r
+import com.jme3.terrain.noise.Basis;\r
+\r
+/**\r
+ * Interface for a general fractal basis.\r
+ * \r
+ * Takes any number of basis funcions to work with and a few common parameters\r
+ * for noise fractals\r
+ * \r
+ * @author Anthyon\r
+ * \r
+ */\r
+public interface Fractal extends Basis {\r
+\r
+       public Fractal setOctaves(final float octaves);\r
+\r
+       public Fractal setFrequency(final float frequency);\r
+\r
+       public Fractal setRoughness(final float roughness);\r
+\r
+       public Fractal setAmplitude(final float amplitude);\r
+\r
+       public Fractal setLacunarity(final float lacunarity);\r
+\r
+       public Fractal addBasis(Basis basis);\r
+\r
+}\r
diff --git a/engine/src/terrain/com/jme3/terrain/noise/fractal/FractalSum.java b/engine/src/terrain/com/jme3/terrain/noise/fractal/FractalSum.java
new file mode 100644 (file)
index 0000000..5fe5dcb
--- /dev/null
@@ -0,0 +1,142 @@
+/**\r
+ * Copyright (c) 2011, Novyon Events\r
+ * \r
+ * All rights reserved.\r
+ * \r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions are met:\r
+ * \r
+ * - Redistributions of source code must retain the above copyright notice, this\r
+ * list of conditions and the following disclaimer.\r
+ * \r
+ * - Redistributions in binary form must reproduce the above copyright notice,\r
+ * this list of conditions and the following disclaimer in the documentation\r
+ * and/or other materials provided with the distribution.\r
+ * \r
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\r
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\r
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\r
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR\r
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\r
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+ * \r
+ * @author Anthyon\r
+ */\r
+package com.jme3.terrain.noise.fractal;\r
+\r
+import com.jme3.terrain.noise.Basis;\r
+import com.jme3.terrain.noise.ShaderUtils;\r
+import com.jme3.terrain.noise.basis.ImprovedNoise;\r
+import com.jme3.terrain.noise.basis.Noise;\r
+\r
+/**\r
+ * FractalSum is the simplest form of fractal functions summing up a few octaves\r
+ * of the noise value with an ever decreasing (0 < roughness < 1) amplitude\r
+ * \r
+ * lacunarity = 2.0f is the classical octave distance\r
+ * \r
+ * Note: though noise basis functions are generally designed to return value\r
+ * between -1..1, there sum can easily be made to extend out of this range. To\r
+ * handle this is up to the user.\r
+ * \r
+ * @author Anthyon\r
+ * \r
+ */\r
+public class FractalSum extends Noise implements Fractal {\r
+\r
+       private Basis basis;\r
+       private float lacunarity;\r
+       private float amplitude;\r
+       private float roughness;\r
+       private float frequency;\r
+       private float octaves;\r
+       private int maxFreq;\r
+\r
+       public FractalSum() {\r
+               this.basis = new ImprovedNoise();\r
+               this.lacunarity = 2.124367f;\r
+               this.amplitude = 1.0f;\r
+               this.roughness = 0.6f;\r
+               this.frequency = 1f;\r
+               this.setOctaves(1);\r
+       }\r
+\r
+       @Override\r
+       public float value(final float x, final float y, final float z) {\r
+               float total = 0;\r
+\r
+               for (float f = this.frequency, a = this.amplitude; f < this.maxFreq; f *= this.lacunarity, a *= this.roughness) {\r
+                       total += this.basis.value(this.scale * x * f, this.scale * y * f, this.scale * z * f) * a;\r
+               }\r
+\r
+               return ShaderUtils.clamp(total, -1, 1);\r
+       }\r
+\r
+       @Override\r
+       public Fractal addBasis(final Basis basis) {\r
+               this.basis = basis;\r
+               return this;\r
+       }\r
+\r
+       public float getOctaves() {\r
+               return this.octaves;\r
+       }\r
+\r
+       @Override\r
+       public Fractal setOctaves(final float octaves) {\r
+               this.octaves = octaves;\r
+               this.maxFreq = 1 << (int) octaves;\r
+               return this;\r
+       }\r
+\r
+       public float getFrequency() {\r
+               return this.frequency;\r
+       }\r
+\r
+       @Override\r
+       public Fractal setFrequency(final float frequency) {\r
+               this.frequency = frequency;\r
+               return this;\r
+       }\r
+\r
+       public float getRoughness() {\r
+               return this.roughness;\r
+       }\r
+\r
+       @Override\r
+       public Fractal setRoughness(final float roughness) {\r
+               this.roughness = roughness;\r
+               return this;\r
+       }\r
+\r
+       public float getAmplitude() {\r
+               return this.amplitude;\r
+       }\r
+\r
+       @Override\r
+       public Fractal setAmplitude(final float amplitude) {\r
+               this.amplitude = amplitude;\r
+               return this;\r
+       }\r
+\r
+       public float getLacunarity() {\r
+               return this.lacunarity;\r
+       }\r
+\r
+       @Override\r
+       public Fractal setLacunarity(final float lacunarity) {\r
+               this.lacunarity = lacunarity;\r
+               return this;\r
+       }\r
+\r
+       @Override\r
+       public void init() {\r
+\r
+       }\r
+\r
+}\r
diff --git a/engine/src/terrain/com/jme3/terrain/noise/modulator/CatRom2.java b/engine/src/terrain/com/jme3/terrain/noise/modulator/CatRom2.java
new file mode 100644 (file)
index 0000000..ce65cd6
--- /dev/null
@@ -0,0 +1,77 @@
+/**\r
+ * Copyright (c) 2011, Novyon Events\r
+ * \r
+ * All rights reserved.\r
+ * \r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions are met:\r
+ * \r
+ * - Redistributions of source code must retain the above copyright notice, this\r
+ * list of conditions and the following disclaimer.\r
+ * \r
+ * - Redistributions in binary form must reproduce the above copyright notice,\r
+ * this list of conditions and the following disclaimer in the documentation\r
+ * and/or other materials provided with the distribution.\r
+ * \r
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\r
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\r
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\r
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR\r
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\r
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+ * \r
+ * @author Anthyon\r
+ */\r
+package com.jme3.terrain.noise.modulator;\r
+\r
+import com.jme3.terrain.noise.ShaderUtils;\r
+import java.util.HashMap;\r
+import java.util.Map;\r
+\r
+public class CatRom2 implements Modulator {\r
+\r
+       private int sampleRate = 100;\r
+\r
+       private final float[] table;\r
+\r
+       private static Map<Integer, CatRom2> instances = new HashMap<Integer, CatRom2>();\r
+\r
+       public CatRom2(final int sampleRate) {\r
+               this.sampleRate = sampleRate;\r
+               this.table = new float[4 * sampleRate + 1];\r
+               for (int i = 0; i < 4 * sampleRate + 1; i++) {\r
+                       float x = i / (float) sampleRate;\r
+                       x = (float) Math.sqrt(x);\r
+                       if (x < 1) {\r
+                               this.table[i] = 0.5f * (2 + x * x * (-5 + x * 3));\r
+                       } else {\r
+                               this.table[i] = 0.5f * (4 + x * (-8 + x * (5 - x)));\r
+                       }\r
+               }\r
+       }\r
+\r
+       public static CatRom2 getInstance(final int sampleRate) {\r
+               if (!CatRom2.instances.containsKey(sampleRate)) {\r
+                       CatRom2.instances.put(sampleRate, new CatRom2(sampleRate));\r
+               }\r
+               return CatRom2.instances.get(sampleRate);\r
+       }\r
+\r
+       @Override\r
+       public float value(final float... in) {\r
+               if (in[0] >= 4) {\r
+                       return 0;\r
+               }\r
+               in[0] = in[0] * this.sampleRate + 0.5f;\r
+               int i = ShaderUtils.floor(in[0]);\r
+               if (i >= 4 * this.sampleRate + 1) {\r
+                       return 0;\r
+               }\r
+               return this.table[i];\r
+       }\r
+}\r
diff --git a/engine/src/terrain/com/jme3/terrain/noise/modulator/Modulator.java b/engine/src/terrain/com/jme3/terrain/noise/modulator/Modulator.java
new file mode 100644 (file)
index 0000000..28bfd1c
--- /dev/null
@@ -0,0 +1,36 @@
+/**\r
+ * Copyright (c) 2011, Novyon Events\r
+ * \r
+ * All rights reserved.\r
+ * \r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions are met:\r
+ * \r
+ * - Redistributions of source code must retain the above copyright notice, this\r
+ * list of conditions and the following disclaimer.\r
+ * \r
+ * - Redistributions in binary form must reproduce the above copyright notice,\r
+ * this list of conditions and the following disclaimer in the documentation\r
+ * and/or other materials provided with the distribution.\r
+ * \r
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\r
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\r
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\r
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR\r
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\r
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+ * \r
+ * @author Anthyon\r
+ */\r
+package com.jme3.terrain.noise.modulator;\r
+\r
+public interface Modulator {\r
+\r
+       public float value(float... in);\r
+\r
+}\r
diff --git a/engine/src/terrain/com/jme3/terrain/noise/modulator/NoiseModulator.java b/engine/src/terrain/com/jme3/terrain/noise/modulator/NoiseModulator.java
new file mode 100644 (file)
index 0000000..38a3bd6
--- /dev/null
@@ -0,0 +1,34 @@
+/**\r
+ * Copyright (c) 2011, Novyon Events\r
+ * \r
+ * All rights reserved.\r
+ * \r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions are met:\r
+ * \r
+ * - Redistributions of source code must retain the above copyright notice, this\r
+ * list of conditions and the following disclaimer.\r
+ * \r
+ * - Redistributions in binary form must reproduce the above copyright notice,\r
+ * this list of conditions and the following disclaimer in the documentation\r
+ * and/or other materials provided with the distribution.\r
+ * \r
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\r
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\r
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\r
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR\r
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\r
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+ * \r
+ * @author Anthyon\r
+ */\r
+package com.jme3.terrain.noise.modulator;\r
+\r
+public interface NoiseModulator extends Modulator {\r
+\r
+}\r
index d49b9eb..0ab647d 100644 (file)
@@ -1,39 +1,26 @@
 package jme3test.terrain;
 
-import com.jme3.terrain.geomipmap.TerrainQuad;
-import java.util.ArrayList;
-import java.util.List;
-
 import com.jme3.app.SimpleApplication;
 import com.jme3.app.state.ScreenshotAppState;
-import com.jme3.bullet.BulletAppState;
-import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
-import com.jme3.bullet.collision.shapes.HeightfieldCollisionShape;
 import com.jme3.bullet.control.CharacterControl;
-import com.jme3.bullet.control.RigidBodyControl;
-import com.jme3.input.KeyInput;
-import com.jme3.input.controls.ActionListener;
-import com.jme3.input.controls.KeyTrigger;
 import com.jme3.material.Material;
 import com.jme3.math.ColorRGBA;
-import com.jme3.math.Vector2f;
 import com.jme3.math.Vector3f;
-import com.jme3.renderer.Camera;
 import com.jme3.terrain.geomipmap.TerrainGrid;
-import com.jme3.terrain.geomipmap.TerrainGridListener;
+import com.jme3.terrain.geomipmap.TerrainGridLodControl;
 import com.jme3.terrain.geomipmap.TerrainLodControl;
+import com.jme3.terrain.geomipmap.grid.FractalTileLoader;
 import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
-import com.jme3.terrain.heightmap.FractalHeightMapGrid;
+import com.jme3.terrain.noise.ShaderUtils;
+import com.jme3.terrain.noise.basis.FilteredBasis;
+import com.jme3.terrain.noise.filter.IterativeFilter;
+import com.jme3.terrain.noise.filter.OptimizedErode;
+import com.jme3.terrain.noise.filter.PerturbFilter;
+import com.jme3.terrain.noise.filter.SmoothFilter;
+import com.jme3.terrain.noise.fractal.FractalSum;
+import com.jme3.terrain.noise.modulator.NoiseModulator;
 import com.jme3.texture.Texture;
 import com.jme3.texture.Texture.WrapMode;
-import org.novyon.noise.ShaderUtils;
-import org.novyon.noise.basis.FilteredBasis;
-import org.novyon.noise.filter.IterativeFilter;
-import org.novyon.noise.filter.OptimizedErode;
-import org.novyon.noise.filter.PerturbFilter;
-import org.novyon.noise.filter.SmoothFilter;
-import org.novyon.noise.fractal.FractalSum;
-import org.novyon.noise.modulator.NoiseModulator;
 
 public class TerrainFractalGridTest extends SimpleApplication {
 
@@ -42,7 +29,6 @@ public class TerrainFractalGridTest extends SimpleApplication {
     private float grassScale = 64;
     private float dirtScale = 16;
     private float rockScale = 128;
-    private boolean usePhysics = false;
 
     public static void main(final String[] args) {
         TerrainFractalGridTest app = new TerrainFractalGridTest();
@@ -136,138 +122,27 @@ public class TerrainFractalGridTest extends SimpleApplication {
 
         ground.addPreFilter(this.iterate);
 
-        this.terrain = new TerrainGrid("terrain", 33, 129, new FractalHeightMapGrid(ground, null, 256f));
+        this.terrain = new TerrainGrid("terrain", 33, 129, new FractalTileLoader(ground, 256f));
 
         this.terrain.setMaterial(this.mat_terrain);
         this.terrain.setLocalTranslation(0, 0, 0);
         this.terrain.setLocalScale(2f, 1f, 2f);
         this.rootNode.attachChild(this.terrain);
 
-        List<Camera> cameras = new ArrayList<Camera>();
-        cameras.add(this.getCamera());
-        TerrainLodControl control = new TerrainLodControl(this.terrain, cameras);
-        control.setLodCalculator( new DistanceLodCalculator(33, 2.7f) ); // patch size, and a multiplier
+        TerrainLodControl control = new TerrainGridLodControl(this.terrain, this.getCamera());
+        control.setLodCalculator(new DistanceLodCalculator(33, 2.7f)); // patch size, and a multiplier
         this.terrain.addControl(control);
 
-        final BulletAppState bulletAppState = new BulletAppState();
-        stateManager.attach(bulletAppState);
 
 
-        this.getCamera().setLocation(new Vector3f(0, 0, 0));
+        this.getCamera().setLocation(new Vector3f(0, 300, 0));
 
         this.viewPort.setBackgroundColor(new ColorRGBA(0.7f, 0.8f, 1f, 1f));
 
-        if (usePhysics) {
-            CapsuleCollisionShape capsuleShape = new CapsuleCollisionShape(0.5f, 1.8f, 1);
-            player3 = new CharacterControl(capsuleShape, 0.5f);
-            player3.setJumpSpeed(20);
-            player3.setFallSpeed(10);
-            player3.setGravity(10);
-
-            player3.setPhysicsLocation(new Vector3f(cam.getLocation().x, 512, cam.getLocation().z));
-
-            bulletAppState.getPhysicsSpace().add(player3);
-
-            terrain.addListener("physicsStartListener", new TerrainGridListener() {
-
-                public void gridMoved(Vector3f newCenter) {
-                }
-
-                public Material tileLoaded(Material material, Vector3f cell) {
-                    return material;
-                }
-
-                public void tileAttached(Vector3f cell, TerrainQuad quad) {
-                    quad.addControl(new RigidBodyControl(new HeightfieldCollisionShape(quad.getHeightMap(), terrain.getLocalScale()), 0));
-                    bulletAppState.getPhysicsSpace().add(quad);
-                }
-
-                public void tileDetached(Vector3f cell, TerrainQuad quad) {
-                    bulletAppState.getPhysicsSpace().remove(quad);
-                    quad.removeControl(RigidBodyControl.class);
-                }
-
-            });
-        }
-        this.terrain.initialize(cam.getLocation());
-        this.initKeys();
+        
     }
 
-    private void initKeys() {
-        // You can map one or several inputs to one named action
-        this.inputManager.addMapping("Lefts", new KeyTrigger(KeyInput.KEY_A));
-        this.inputManager.addMapping("Rights", new KeyTrigger(KeyInput.KEY_D));
-        this.inputManager.addMapping("Ups", new KeyTrigger(KeyInput.KEY_W));
-        this.inputManager.addMapping("Downs", new KeyTrigger(KeyInput.KEY_S));
-        this.inputManager.addMapping("Jumps", new KeyTrigger(KeyInput.KEY_SPACE));
-        this.inputManager.addListener(this.actionListener, "Lefts");
-        this.inputManager.addListener(this.actionListener, "Rights");
-        this.inputManager.addListener(this.actionListener, "Ups");
-        this.inputManager.addListener(this.actionListener, "Downs");
-        this.inputManager.addListener(this.actionListener, "Jumps");
-    }
-    private boolean left;
-    private boolean right;
-    private boolean up;
-    private boolean down;
-    private final ActionListener actionListener = new ActionListener() {
-
-        @Override
-        public void onAction(final String name, final boolean keyPressed, final float tpf) {
-            if (name.equals("Lefts")) {
-                if (keyPressed) {
-                    TerrainFractalGridTest.this.left = true;
-                } else {
-                    TerrainFractalGridTest.this.left = false;
-                }
-            } else if (name.equals("Rights")) {
-                if (keyPressed) {
-                    TerrainFractalGridTest.this.right = true;
-                } else {
-                    TerrainFractalGridTest.this.right = false;
-                }
-            } else if (name.equals("Ups")) {
-                if (keyPressed) {
-                    TerrainFractalGridTest.this.up = true;
-                } else {
-                    TerrainFractalGridTest.this.up = false;
-                }
-            } else if (name.equals("Downs")) {
-                if (keyPressed) {
-                    TerrainFractalGridTest.this.down = true;
-                } else {
-                    TerrainFractalGridTest.this.down = false;
-                }
-            } else if (name.equals("Jumps")) {
-                if (usePhysics) {
-                    TerrainFractalGridTest.this.player3.jump();
-                }
-            }
-        }
-    };
-    private final Vector3f walkDirection = new Vector3f();
-
     @Override
     public void simpleUpdate(final float tpf) {
-            Vector3f camDir = this.cam.getDirection().clone().multLocal(0.6f);
-            Vector3f camLeft = this.cam.getLeft().clone().multLocal(0.4f);
-            this.walkDirection.set(0, 0, 0);
-            if (this.left) {
-                this.walkDirection.addLocal(camLeft);
-            }
-            if (this.right) {
-                this.walkDirection.addLocal(camLeft.negate());
-            }
-            if (this.up) {
-                this.walkDirection.addLocal(camDir);
-            }
-            if (this.down) {
-                this.walkDirection.addLocal(camDir.negate());
-            }
-
-        if (usePhysics) {
-            this.player3.setWalkDirection(this.walkDirection);
-            this.cam.setLocation(this.player3.getPhysicsLocation());
-        }
     }
 }
index 8715503..6a2167b 100644 (file)
@@ -1,8 +1,5 @@
 package jme3test.terrain;
 
-import java.util.ArrayList;
-import java.util.List;
-
 import com.jme3.app.SimpleApplication;
 import com.jme3.app.state.ScreenshotAppState;
 import com.jme3.asset.plugins.HttpZipLocator;
@@ -19,28 +16,30 @@ import com.jme3.light.AmbientLight;
 import com.jme3.light.DirectionalLight;
 import com.jme3.material.Material;
 import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector2f;
 import com.jme3.math.Vector3f;
-import com.jme3.renderer.Camera;
-import com.jme3.shader.VarType;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.debug.Arrow;
 import com.jme3.terrain.geomipmap.TerrainGrid;
 import com.jme3.terrain.geomipmap.TerrainGridListener;
+import com.jme3.terrain.geomipmap.TerrainGridLodControl;
 import com.jme3.terrain.geomipmap.TerrainLodControl;
 import com.jme3.terrain.geomipmap.TerrainQuad;
+import com.jme3.terrain.geomipmap.grid.FractalTileLoader;
 import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
-import com.jme3.terrain.heightmap.FractalHeightMapGrid;
-import com.jme3.terrain.heightmap.ImageBasedHeightMapGrid;
-import com.jme3.terrain.heightmap.Namer;
+import com.jme3.terrain.noise.ShaderUtils;
+import com.jme3.terrain.noise.basis.FilteredBasis;
+import com.jme3.terrain.noise.filter.IterativeFilter;
+import com.jme3.terrain.noise.filter.OptimizedErode;
+import com.jme3.terrain.noise.filter.PerturbFilter;
+import com.jme3.terrain.noise.filter.SmoothFilter;
+import com.jme3.terrain.noise.fractal.FractalSum;
+import com.jme3.terrain.noise.modulator.NoiseModulator;
 import com.jme3.texture.Texture;
 import com.jme3.texture.Texture.WrapMode;
 import java.io.File;
-import org.novyon.noise.ShaderUtils;
-import org.novyon.noise.basis.FilteredBasis;
-import org.novyon.noise.filter.IterativeFilter;
-import org.novyon.noise.filter.OptimizedErode;
-import org.novyon.noise.filter.PerturbFilter;
-import org.novyon.noise.filter.SmoothFilter;
-import org.novyon.noise.fractal.FractalSum;
-import org.novyon.noise.modulator.NoiseModulator;
 
 public class TerrainGridAlphaMapTest extends SimpleApplication {
 
@@ -48,7 +47,7 @@ public class TerrainGridAlphaMapTest extends SimpleApplication {
     private float grassScale = 64;
     private float dirtScale = 16;
     private float rockScale = 128;
-    private boolean usePhysics = true;
+    private boolean usePhysics = false;
 
     public static void main(final String[] args) {
         TerrainGridAlphaMapTest app = new TerrainGridAlphaMapTest();
@@ -60,7 +59,7 @@ public class TerrainGridAlphaMapTest extends SimpleApplication {
     private OptimizedErode therm;
     private SmoothFilter smooth;
     private IterativeFilter iterate;
-    private Material matRock;
+    private Material material;
     private Material matWire;
 
     @Override
@@ -74,11 +73,11 @@ public class TerrainGridAlphaMapTest extends SimpleApplication {
         al.setColor(ColorRGBA.White.mult(1.3f));
         rootNode.addLight(al);
 
-        File file = new File("mountains.zip");
+        File file = new File("TerrainGridTestData.zip");
         if (!file.exists()) {
-            assetManager.registerLocator("http://jmonkeyengine.googlecode.com/files/mountains.zip", HttpZipLocator.class);
+            assetManager.registerLocator("http://jmonkeyengine.googlecode.com/files/TerrainGridTestData.zip", HttpZipLocator.class);
         } else {
-            assetManager.registerLocator("mountains.zip", ZipLocator.class);
+            assetManager.registerLocator("TerrainGridTestData.zip", ZipLocator.class);
         }
 
         this.flyCam.setMoveSpeed(100f);
@@ -86,27 +85,28 @@ public class TerrainGridAlphaMapTest extends SimpleApplication {
         this.stateManager.attach(state);
 
         // TERRAIN TEXTURE material
-        matRock = new Material(assetManager, "Common/MatDefs/Terrain/TerrainLighting.j3md");
-        matRock.setBoolean("useTriPlanarMapping", false);
-        matRock.setBoolean("isTerrainGrid", true);
+        material = new Material(assetManager, "Common/MatDefs/Terrain/TerrainLighting.j3md");
+        material.setBoolean("useTriPlanarMapping", false);
+        //material.setBoolean("isTerrainGrid", true);
+        material.setFloat("Shininess", 0.0f);
 
         // GRASS texture
         Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg");
         grass.setWrap(WrapMode.Repeat);
-        matRock.setTexture("DiffuseMap", grass);
-        matRock.setFloat("DiffuseMap_0_scale", grassScale);
+        material.setTexture("DiffuseMap", grass);
+        material.setFloat("DiffuseMap_0_scale", grassScale);
 
         // DIRT texture
         Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg");
         dirt.setWrap(WrapMode.Repeat);
-        matRock.setTexture("DiffuseMap_1", dirt);
-        matRock.setFloat("DiffuseMap_1_scale", dirtScale);
+        material.setTexture("DiffuseMap_1", dirt);
+        material.setFloat("DiffuseMap_1_scale", dirtScale);
 
         // ROCK texture
         Texture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg");
         rock.setWrap(WrapMode.Repeat);
-        matRock.setTexture("DiffuseMap_2", rock);
-        matRock.setFloat("DiffuseMap_2_scale", rockScale);
+        material.setTexture("DiffuseMap_2", rock);
+        material.setFloat("DiffuseMap_2_scale", rockScale);
 
         // WIREFRAME material
         matWire = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
@@ -149,16 +149,14 @@ public class TerrainGridAlphaMapTest extends SimpleApplication {
 
         ground.addPreFilter(this.iterate);
 
-        this.terrain = new TerrainGrid("terrain", 33, 257, new FractalHeightMapGrid(ground, null, 256));
-        this.terrain.setMaterial(this.matRock);
+        this.terrain = new TerrainGrid("terrain", 33, 257, new FractalTileLoader(ground, 256));
+        this.terrain.setMaterial(this.material);
 
         this.terrain.setLocalTranslation(0, 0, 0);
         this.terrain.setLocalScale(2f, 1f, 2f);
         this.rootNode.attachChild(this.terrain);
 
-        List<Camera> cameras = new ArrayList<Camera>();
-        cameras.add(this.getCamera());
-        TerrainLodControl control = new TerrainLodControl(this.terrain, cameras);
+        TerrainLodControl control = new TerrainGridLodControl(this.terrain, this.getCamera());
         control.setLodCalculator( new DistanceLodCalculator(33, 2.7f) ); // patch size, and a multiplier
         this.terrain.addControl(control);
 
@@ -182,35 +180,74 @@ public class TerrainGridAlphaMapTest extends SimpleApplication {
             bulletAppState.getPhysicsSpace().add(player3);
 
         }
-        terrain.addListener("physicsStartListener", new TerrainGridListener() {
+        terrain.addListener(new TerrainGridListener() {
 
             public void gridMoved(Vector3f newCenter) {
             }
 
-            public Material tileLoaded(Material material, Vector3f cell) {
-                return material;
-            }
-
             public void tileAttached(Vector3f cell, TerrainQuad quad) {
-                Texture alpha = assetManager.loadTexture("Scenes/TerrainAlphaTest/alphamap_" + Math.abs((int) (cell.x % 2)) * 512 + "_" + Math.abs((int) (cell.z % 2) * 512) + ".png");
+                Texture alpha = null;
+                try {
+                    alpha = assetManager.loadTexture("TerrainAlphaTest/alpha_" + (int)cell.x+ "_" + (int)cell.z + ".png");
+                } catch (Exception e) {
+                    alpha = assetManager.loadTexture("TerrainAlphaTest/alpha_default.png");
+                }
                 quad.getMaterial().setTexture("AlphaMap", alpha);
                 if (usePhysics) {
                     quad.addControl(new RigidBodyControl(new HeightfieldCollisionShape(quad.getHeightMap(), terrain.getLocalScale()), 0));
                     bulletAppState.getPhysicsSpace().add(quad);
                 }
+                updateMarkerElevations();
             }
 
             public void tileDetached(Vector3f cell, TerrainQuad quad) {
                 if (usePhysics) {
-                    bulletAppState.getPhysicsSpace().remove(quad);
-                    quad.removeControl(RigidBodyControl.class);
+                    if (quad.getControl(RigidBodyControl.class) != null) {
+                        bulletAppState.getPhysicsSpace().remove(quad);
+                        quad.removeControl(RigidBodyControl.class);
+                    }
                 }
+                updateMarkerElevations();
             }
         });
-        this.terrain.initialize(cam.getLocation());
+        
         this.initKeys();
+    
+        markers = new Node();
+        rootNode.attachChild(markers);
+        createMarkerPoints(1);
     }
-
+    
+    Node markers;
+    
+    
+    private void createMarkerPoints(float count) {
+        Node center = createAxisMarker(10);
+        markers.attachChild(center);
+        
+        float xS = (count-1)*terrain.getTerrainSize() - (terrain.getTerrainSize()/2);
+        float zS = (count-1)*terrain.getTerrainSize() - (terrain.getTerrainSize()/2);
+        float xSi = xS;
+        float zSi = zS;
+        for (int x=0; x<count*2; x++) {
+            for (int z=0; z<count*2; z++) {
+                Node m = createAxisMarker(5);
+                m.setLocalTranslation(xSi, 0, zSi);
+                markers.attachChild(m);
+                zSi += terrain.getTerrainSize();
+            }
+            zSi = zS;
+            xSi += terrain.getTerrainSize();
+        }
+    }
+    
+    private void updateMarkerElevations() {
+        for (Spatial s : markers.getChildren()) {
+            float h = terrain.getHeight(new Vector2f(s.getLocalTranslation().x, s.getLocalTranslation().z));
+            s.setLocalTranslation(s.getLocalTranslation().x, h+1, s.getLocalTranslation().z);
+        }
+    }
+    
     private void initKeys() {
         // You can map one or several inputs to one named action
         this.inputManager.addMapping("Lefts", new KeyTrigger(KeyInput.KEY_A));
@@ -286,4 +323,35 @@ public class TerrainGridAlphaMapTest extends SimpleApplication {
             this.cam.setLocation(this.player3.getPhysicsLocation());
         }
     }
+    
+    protected Node createAxisMarker(float arrowSize) {
+
+        Material redMat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+        redMat.getAdditionalRenderState().setWireframe(true);
+        redMat.setColor("Color", ColorRGBA.Red);
+        
+        Material greenMat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+        greenMat.getAdditionalRenderState().setWireframe(true);
+        greenMat.setColor("Color", ColorRGBA.Green);
+        
+        Material blueMat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+        blueMat.getAdditionalRenderState().setWireframe(true);
+        blueMat.setColor("Color", ColorRGBA.Blue);
+
+        Node axis = new Node();
+
+        // create arrows
+        Geometry arrowX = new Geometry("arrowX", new Arrow(new Vector3f(arrowSize, 0, 0)));
+        arrowX.setMaterial(redMat);
+        Geometry arrowY = new Geometry("arrowY", new Arrow(new Vector3f(0, arrowSize, 0)));
+        arrowY.setMaterial(greenMat);
+        Geometry arrowZ = new Geometry("arrowZ", new Arrow(new Vector3f(0, 0, arrowSize)));
+        arrowZ.setMaterial(blueMat);
+        axis.attachChild(arrowX);
+        axis.attachChild(arrowY);
+        axis.attachChild(arrowZ);
+
+        //axis.setModelBound(new BoundingBox());
+        return axis;
+    }
 }
diff --git a/engine/src/test/jme3test/terrain/TerrainGridSerializationTest.java b/engine/src/test/jme3test/terrain/TerrainGridSerializationTest.java
new file mode 100644 (file)
index 0000000..27ce157
--- /dev/null
@@ -0,0 +1,178 @@
+package jme3test.terrain;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.app.state.ScreenshotAppState;
+import com.jme3.asset.plugins.HttpZipLocator;
+import com.jme3.asset.plugins.ZipLocator;
+import com.jme3.bullet.BulletAppState;
+import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
+import com.jme3.bullet.collision.shapes.HeightfieldCollisionShape;
+import com.jme3.bullet.control.CharacterControl;
+import com.jme3.bullet.control.RigidBodyControl;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.terrain.geomipmap.TerrainGrid;
+import com.jme3.terrain.geomipmap.TerrainGridListener;
+import com.jme3.terrain.geomipmap.TerrainGridLodControl;
+import com.jme3.terrain.geomipmap.TerrainLodControl;
+import com.jme3.terrain.geomipmap.TerrainQuad;
+import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
+import java.io.File;
+
+public class TerrainGridSerializationTest extends SimpleApplication {
+
+    private TerrainGrid terrain;
+    private boolean usePhysics = true;
+
+    public static void main(final String[] args) {
+        TerrainGridSerializationTest app = new TerrainGridSerializationTest();
+        app.start();
+    }
+    private CharacterControl player3;
+
+    @Override
+    public void simpleInitApp() {
+        File file = new File("TerrainGridTestData.zip");
+        if (!file.exists()) {
+            assetManager.registerLocator("http://jmonkeyengine.googlecode.com/files/TerrainGridTestData.zip", HttpZipLocator.class);
+        } else {
+            assetManager.registerLocator("TerrainGridTestData.zip", ZipLocator.class);
+        }
+
+        this.flyCam.setMoveSpeed(100f);
+        ScreenshotAppState state = new ScreenshotAppState();
+        this.stateManager.attach(state);
+
+        this.terrain= (TerrainGrid) assetManager.loadModel("TerrainGrid/TerrainGrid.j3o");
+        
+        this.rootNode.attachChild(this.terrain);
+        
+        TerrainLodControl control = new TerrainGridLodControl(this.terrain, getCamera());
+        control.setLodCalculator( new DistanceLodCalculator(65, 2.7f) ); // patch size, and a multiplier
+        this.terrain.addControl(control);
+        
+        final BulletAppState bulletAppState = new BulletAppState();
+        stateManager.attach(bulletAppState);
+
+        this.getCamera().setLocation(new Vector3f(0, 256, 0));
+
+        this.viewPort.setBackgroundColor(new ColorRGBA(0.7f, 0.8f, 1f, 1f));
+
+        if (usePhysics) {
+            CapsuleCollisionShape capsuleShape = new CapsuleCollisionShape(0.5f, 1.8f, 1);
+            player3 = new CharacterControl(capsuleShape, 0.5f);
+            player3.setJumpSpeed(20);
+            player3.setFallSpeed(10);
+            player3.setGravity(10);
+
+            player3.setPhysicsLocation(new Vector3f(cam.getLocation().x, 256, cam.getLocation().z));
+
+            bulletAppState.getPhysicsSpace().add(player3);
+
+            terrain.addListener(new TerrainGridListener() {
+
+                public void gridMoved(Vector3f newCenter) {
+                }
+
+                public void tileAttached(Vector3f cell, TerrainQuad quad) {
+                    //workaround for bugged test j3o's
+                    while(quad.getControl(RigidBodyControl.class)!=null){
+                        quad.removeControl(RigidBodyControl.class);
+                    }
+                    quad.addControl(new RigidBodyControl(new HeightfieldCollisionShape(quad.getHeightMap(), terrain.getLocalScale()), 0));
+                    bulletAppState.getPhysicsSpace().add(quad);
+                }
+
+                public void tileDetached(Vector3f cell, TerrainQuad quad) {
+                    if (quad.getControl(RigidBodyControl.class) != null) {
+                        bulletAppState.getPhysicsSpace().remove(quad);
+                        quad.removeControl(RigidBodyControl.class);
+                    }
+                }
+
+            });
+        }
+        
+        this.initKeys();
+    }
+
+    private void initKeys() {
+        // You can map one or several inputs to one named action
+        this.inputManager.addMapping("Lefts", new KeyTrigger(KeyInput.KEY_A));
+        this.inputManager.addMapping("Rights", new KeyTrigger(KeyInput.KEY_D));
+        this.inputManager.addMapping("Ups", new KeyTrigger(KeyInput.KEY_W));
+        this.inputManager.addMapping("Downs", new KeyTrigger(KeyInput.KEY_S));
+        this.inputManager.addMapping("Jumps", new KeyTrigger(KeyInput.KEY_SPACE));
+        this.inputManager.addListener(this.actionListener, "Lefts");
+        this.inputManager.addListener(this.actionListener, "Rights");
+        this.inputManager.addListener(this.actionListener, "Ups");
+        this.inputManager.addListener(this.actionListener, "Downs");
+        this.inputManager.addListener(this.actionListener, "Jumps");
+    }
+    private boolean left;
+    private boolean right;
+    private boolean up;
+    private boolean down;
+    private final ActionListener actionListener = new ActionListener() {
+
+        @Override
+        public void onAction(final String name, final boolean keyPressed, final float tpf) {
+            if (name.equals("Lefts")) {
+                if (keyPressed) {
+                    TerrainGridSerializationTest.this.left = true;
+                } else {
+                    TerrainGridSerializationTest.this.left = false;
+                }
+            } else if (name.equals("Rights")) {
+                if (keyPressed) {
+                    TerrainGridSerializationTest.this.right = true;
+                } else {
+                    TerrainGridSerializationTest.this.right = false;
+                }
+            } else if (name.equals("Ups")) {
+                if (keyPressed) {
+                    TerrainGridSerializationTest.this.up = true;
+                } else {
+                    TerrainGridSerializationTest.this.up = false;
+                }
+            } else if (name.equals("Downs")) {
+                if (keyPressed) {
+                    TerrainGridSerializationTest.this.down = true;
+                } else {
+                    TerrainGridSerializationTest.this.down = false;
+                }
+            } else if (name.equals("Jumps")) {
+                TerrainGridSerializationTest.this.player3.jump();
+            }
+        }
+    };
+    private final Vector3f walkDirection = new Vector3f();
+
+    @Override
+    public void simpleUpdate(final float tpf) {
+        Vector3f camDir = this.cam.getDirection().clone().multLocal(0.6f);
+        Vector3f camLeft = this.cam.getLeft().clone().multLocal(0.4f);
+        this.walkDirection.set(0, 0, 0);
+        if (this.left) {
+            this.walkDirection.addLocal(camLeft);
+        }
+        if (this.right) {
+            this.walkDirection.addLocal(camLeft.negate());
+        }
+        if (this.up) {
+            this.walkDirection.addLocal(camDir);
+        }
+        if (this.down) {
+            this.walkDirection.addLocal(camDir.negate());
+        }
+
+        if (usePhysics) {
+            this.player3.setWalkDirection(this.walkDirection);
+            this.cam.setLocation(this.player3.getPhysicsLocation());
+        }
+    }
+}
index 5bfd88c..f46455a 100644 (file)
@@ -12,15 +12,17 @@ import com.jme3.bullet.control.RigidBodyControl;
 import com.jme3.input.KeyInput;
 import com.jme3.input.controls.ActionListener;
 import com.jme3.input.controls.KeyTrigger;
+import com.jme3.light.DirectionalLight;
 import com.jme3.material.Material;
 import com.jme3.math.ColorRGBA;
 import com.jme3.math.Vector3f;
 import com.jme3.terrain.geomipmap.TerrainGrid;
 import com.jme3.terrain.geomipmap.TerrainGridListener;
+import com.jme3.terrain.geomipmap.TerrainGridLodControl;
 import com.jme3.terrain.geomipmap.TerrainLodControl;
 import com.jme3.terrain.geomipmap.TerrainQuad;
+import com.jme3.terrain.geomipmap.grid.ImageTileLoader;
 import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
-import com.jme3.terrain.heightmap.ImageBasedHeightMapGrid;
 import com.jme3.terrain.heightmap.Namer;
 import com.jme3.texture.Texture;
 import com.jme3.texture.Texture.WrapMode;
@@ -33,7 +35,7 @@ public class TerrainGridTest extends SimpleApplication {
     private float grassScale = 64;
     private float dirtScale = 16;
     private float rockScale = 128;
-    private boolean usePhysics = true;
+    private boolean usePhysics = false;
     private boolean physicsAdded = false;
 
     public static void main(final String[] args) {
@@ -44,11 +46,11 @@ public class TerrainGridTest extends SimpleApplication {
 
     @Override
     public void simpleInitApp() {
-        File file = new File("mountains.zip");
+        File file = new File("TerrainGridTestData.zip");
         if (!file.exists()) {
-            assetManager.registerLocator("http://jmonkeyengine.googlecode.com/files/mountains.zip", HttpZipLocator.class);
+            assetManager.registerLocator("http://jmonkeyengine.googlecode.com/files/TerrainGridTestData.zip", HttpZipLocator.class);
         } else {
-            assetManager.registerLocator("mountains.zip", ZipLocator.class);
+            assetManager.registerLocator("TerrainGridTestData.zip", ZipLocator.class);
         }
 
         this.flyCam.setMoveSpeed(100f);
@@ -94,29 +96,34 @@ public class TerrainGridTest extends SimpleApplication {
 
         this.mat_terrain.setFloat("terrainSize", 129);
 
-        this.terrain = new TerrainGrid("terrain", 65, 257, new ImageBasedHeightMapGrid(assetManager, new Namer() {
+        this.terrain = new TerrainGrid("terrain", 65, 257, new ImageTileLoader(assetManager, new Namer() {
 
             public String getName(int x, int y) {
                 return "Scenes/TerrainMountains/terrain_" + x + "_" + y + ".png";
             }
         }));
-
-        this.terrain.setMaterial(this.mat_terrain);
+        
+        this.terrain.setMaterial(mat_terrain);
         this.terrain.setLocalTranslation(0, 0, 0);
-        this.terrain.setLocalScale(2f, 1f, 2f);
+        this.terrain.setLocalScale(1f, 1f, 1f);
         this.rootNode.attachChild(this.terrain);
 
-        TerrainLodControl control = new TerrainLodControl(this.terrain, getCamera());
+        TerrainLodControl control = new TerrainGridLodControl(this.terrain, getCamera());
         control.setLodCalculator( new DistanceLodCalculator(65, 2.7f) ); // patch size, and a multiplier
         this.terrain.addControl(control);
 
         final BulletAppState bulletAppState = new BulletAppState();
         stateManager.attach(bulletAppState);
 
-        this.getCamera().setLocation(new Vector3f(0, 256, 0));
-
+        this.getCamera().setLocation(new Vector3f(0, 400, 0));
+        this.getCamera().lookAt(new Vector3f(0,0,0), Vector3f.UNIT_Y);
+        
         this.viewPort.setBackgroundColor(new ColorRGBA(0.7f, 0.8f, 1f, 1f));
 
+        DirectionalLight light = new DirectionalLight();
+        light.setDirection((new Vector3f(-0.5f, -1f, -0.5f)).normalize());
+        rootNode.addLight(light);
+        
         if (usePhysics) {
             CapsuleCollisionShape capsuleShape = new CapsuleCollisionShape(0.5f, 1.8f, 1);
             player3 = new CharacterControl(capsuleShape, 0.5f);
@@ -128,28 +135,29 @@ public class TerrainGridTest extends SimpleApplication {
 
             bulletAppState.getPhysicsSpace().add(player3);
 
-            terrain.addListener("physicsStartListener", new TerrainGridListener() {
+            terrain.addListener(new TerrainGridListener() {
 
                 public void gridMoved(Vector3f newCenter) {
                 }
 
-                public Material tileLoaded(Material material, Vector3f cell) {
-                    return material;
-                }
-
                 public void tileAttached(Vector3f cell, TerrainQuad quad) {
+                    while(quad.getControl(RigidBodyControl.class)!=null){
+                        quad.removeControl(RigidBodyControl.class);
+                    }
                     quad.addControl(new RigidBodyControl(new HeightfieldCollisionShape(quad.getHeightMap(), terrain.getLocalScale()), 0));
                     bulletAppState.getPhysicsSpace().add(quad);
                 }
 
                 public void tileDetached(Vector3f cell, TerrainQuad quad) {
-                    bulletAppState.getPhysicsSpace().remove(quad);
-                    quad.removeControl(RigidBodyControl.class);
+                    if (quad.getControl(RigidBodyControl.class) != null) {
+                        bulletAppState.getPhysicsSpace().remove(quad);
+                        quad.removeControl(RigidBodyControl.class);
+                    }
                 }
 
             });
         }
-        this.terrain.initialize(cam.getLocation());
+        
         this.initKeys();
     }
 
diff --git a/engine/src/test/jme3test/terrain/TerrainGridTileLoaderTest.java b/engine/src/test/jme3test/terrain/TerrainGridTileLoaderTest.java
new file mode 100644 (file)
index 0000000..3485e4d
--- /dev/null
@@ -0,0 +1,244 @@
+package jme3test.terrain;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.app.state.ScreenshotAppState;
+import com.jme3.asset.plugins.HttpZipLocator;
+import com.jme3.asset.plugins.ZipLocator;
+import com.jme3.bullet.BulletAppState;
+import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
+import com.jme3.bullet.collision.shapes.HeightfieldCollisionShape;
+import com.jme3.bullet.control.CharacterControl;
+import com.jme3.bullet.control.RigidBodyControl;
+import com.jme3.input.KeyInput;
+import com.jme3.input.MouseInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.input.controls.MouseButtonTrigger;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.terrain.Terrain;
+import com.jme3.terrain.geomipmap.TerrainGrid;
+import com.jme3.terrain.geomipmap.TerrainGridListener;
+import com.jme3.terrain.geomipmap.TerrainGridLodControl;
+import com.jme3.terrain.geomipmap.TerrainLodControl;
+import com.jme3.terrain.geomipmap.TerrainQuad;
+import com.jme3.terrain.geomipmap.grid.AssetTileLoader;
+import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapMode;
+import java.io.File;
+
+public class TerrainGridTileLoaderTest extends SimpleApplication {
+
+    private Material mat_terrain;
+    private TerrainGrid terrain;
+    private float grassScale = 64;
+    private float dirtScale = 16;
+    private float rockScale = 128;
+    private boolean usePhysics = true;
+    private boolean physicsAdded = false;
+
+    public static void main(final String[] args) {
+        TerrainGridTileLoaderTest app = new TerrainGridTileLoaderTest();
+        app.start();
+    }
+    private CharacterControl player3;
+
+    @Override
+    public void simpleInitApp() {
+        File file = new File("TerrainGridTestData.zip");
+        if (!file.exists()) {
+            assetManager.registerLocator("http://jmonkeyengine.googlecode.com/files/TerrainGridTestData.zip", HttpZipLocator.class);
+        } else {
+            assetManager.registerLocator("TerrainGridTestData.zip", ZipLocator.class);
+        }
+
+        this.flyCam.setMoveSpeed(100f);
+        ScreenshotAppState state = new ScreenshotAppState();
+        this.stateManager.attach(state);
+
+        // TERRAIN TEXTURE material
+        this.mat_terrain = new Material(this.assetManager, "Common/MatDefs/Terrain/HeightBasedTerrain.j3md");
+
+        // Parameters to material:
+        // regionXColorMap: X = 1..4 the texture that should be appliad to state X
+        // regionX: a Vector3f containing the following information:
+        //      regionX.x: the start height of the region
+        //      regionX.y: the end height of the region
+        //      regionX.z: the texture scale for the region
+        //  it might not be the most elegant way for storing these 3 values, but it packs the data nicely :)
+        // slopeColorMap: the texture to be used for cliffs, and steep mountain sites
+        // slopeTileFactor: the texture scale for slopes
+        // terrainSize: the total size of the terrain (used for scaling the texture)
+        // GRASS texture
+        Texture grass = this.assetManager.loadTexture("Textures/Terrain/splat/grass.jpg");
+        grass.setWrap(WrapMode.Repeat);
+        this.mat_terrain.setTexture("region1ColorMap", grass);
+        this.mat_terrain.setVector3("region1", new Vector3f(88, 200, this.grassScale));
+
+        // DIRT texture
+        Texture dirt = this.assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg");
+        dirt.setWrap(WrapMode.Repeat);
+        this.mat_terrain.setTexture("region2ColorMap", dirt);
+        this.mat_terrain.setVector3("region2", new Vector3f(0, 90, this.dirtScale));
+
+        // ROCK texture
+        Texture rock = this.assetManager.loadTexture("Textures/Terrain/Rock2/rock.jpg");
+        rock.setWrap(WrapMode.Repeat);
+        this.mat_terrain.setTexture("region3ColorMap", rock);
+        this.mat_terrain.setVector3("region3", new Vector3f(198, 260, this.rockScale));
+
+        this.mat_terrain.setTexture("region4ColorMap", rock);
+        this.mat_terrain.setVector3("region4", new Vector3f(198, 260, this.rockScale));
+
+        this.mat_terrain.setTexture("slopeColorMap", rock);
+        this.mat_terrain.setFloat("slopeTileFactor", 32);
+
+        this.mat_terrain.setFloat("terrainSize", 129);
+//quad.getHeightMap(), terrain.getLocalScale()), 0
+        AssetTileLoader grid = new AssetTileLoader(assetManager, "testgrid", "TerrainGrid");
+        this.terrain = new TerrainGrid("terrain", 65, 257, grid);
+
+        this.terrain.setMaterial(this.mat_terrain);
+        this.terrain.setLocalTranslation(0, 0, 0);
+        this.terrain.setLocalScale(2f, 1f, 2f);
+//        try {
+//            BinaryExporter.getInstance().save(terrain, new File("/Users/normenhansen/Documents/Code/jme3/engine/src/test-data/TerrainGrid/"
+//                    + "TerrainGrid.j3o"));
+//        } catch (IOException ex) {
+//            Logger.getLogger(TerrainFractalGridTest.class.getName()).log(Level.SEVERE, null, ex);
+//        }
+        
+        this.rootNode.attachChild(this.terrain);
+        
+        TerrainLodControl control = new TerrainGridLodControl(this.terrain, getCamera());
+        control.setLodCalculator( new DistanceLodCalculator(65, 2.7f) ); // patch size, and a multiplier
+        this.terrain.addControl(control);
+        
+        final BulletAppState bulletAppState = new BulletAppState();
+        stateManager.attach(bulletAppState);
+
+        this.getCamera().setLocation(new Vector3f(0, 256, 0));
+
+        this.viewPort.setBackgroundColor(new ColorRGBA(0.7f, 0.8f, 1f, 1f));
+
+        if (usePhysics) {
+            CapsuleCollisionShape capsuleShape = new CapsuleCollisionShape(0.5f, 1.8f, 1);
+            player3 = new CharacterControl(capsuleShape, 0.5f);
+            player3.setJumpSpeed(20);
+            player3.setFallSpeed(10);
+            player3.setGravity(10);
+
+            player3.setPhysicsLocation(new Vector3f(cam.getLocation().x, 256, cam.getLocation().z));
+
+            bulletAppState.getPhysicsSpace().add(player3);
+
+            terrain.addListener(new TerrainGridListener() {
+
+                public void gridMoved(Vector3f newCenter) {
+                }
+
+                public void tileAttached(Vector3f cell, TerrainQuad quad) {
+                    while(quad.getControl(RigidBodyControl.class)!=null){
+                        quad.removeControl(RigidBodyControl.class);
+                    }
+                    quad.addControl(new RigidBodyControl(new HeightfieldCollisionShape(quad.getHeightMap(), terrain.getLocalScale()), 0));
+                    bulletAppState.getPhysicsSpace().add(quad);
+                }
+
+                public void tileDetached(Vector3f cell, TerrainQuad quad) {
+                    if (quad.getControl(RigidBodyControl.class) != null) {
+                        bulletAppState.getPhysicsSpace().remove(quad);
+                        quad.removeControl(RigidBodyControl.class);
+                    }
+                }
+
+            });
+        }
+        
+        this.initKeys();
+    }
+
+    private void initKeys() {
+        // You can map one or several inputs to one named action
+        this.inputManager.addMapping("Lefts", new KeyTrigger(KeyInput.KEY_A));
+        this.inputManager.addMapping("Rights", new KeyTrigger(KeyInput.KEY_D));
+        this.inputManager.addMapping("Ups", new KeyTrigger(KeyInput.KEY_W));
+        this.inputManager.addMapping("Downs", new KeyTrigger(KeyInput.KEY_S));
+        this.inputManager.addMapping("Jumps", new KeyTrigger(KeyInput.KEY_SPACE));
+        this.inputManager.addMapping("pick", new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
+        this.inputManager.addListener(this.actionListener, "Lefts");
+        this.inputManager.addListener(this.actionListener, "Rights");
+        this.inputManager.addListener(this.actionListener, "Ups");
+        this.inputManager.addListener(this.actionListener, "Downs");
+        this.inputManager.addListener(this.actionListener, "Jumps");
+        this.inputManager.addListener(this.actionListener, "pick");
+    }
+    private boolean left;
+    private boolean right;
+    private boolean up;
+    private boolean down;
+    private final ActionListener actionListener = new ActionListener() {
+
+        @Override
+        public void onAction(final String name, final boolean keyPressed, final float tpf) {
+            if (name.equals("Lefts")) {
+                if (keyPressed) {
+                    TerrainGridTileLoaderTest.this.left = true;
+                } else {
+                    TerrainGridTileLoaderTest.this.left = false;
+                }
+            } else if (name.equals("Rights")) {
+                if (keyPressed) {
+                    TerrainGridTileLoaderTest.this.right = true;
+                } else {
+                    TerrainGridTileLoaderTest.this.right = false;
+                }
+            } else if (name.equals("Ups")) {
+                if (keyPressed) {
+                    TerrainGridTileLoaderTest.this.up = true;
+                } else {
+                    TerrainGridTileLoaderTest.this.up = false;
+                }
+            } else if (name.equals("Downs")) {
+                if (keyPressed) {
+                    TerrainGridTileLoaderTest.this.down = true;
+                } else {
+                    TerrainGridTileLoaderTest.this.down = false;
+                }
+            } else if (name.equals("Jumps")) {
+                TerrainGridTileLoaderTest.this.player3.jump();
+            } else if (name.equals("pick") && keyPressed) {
+                //Terrain picked = terrain.getTerrainAt(player3.getPhysicsLocation());
+                Terrain picked = terrain.getTerrainAtCell(terrain.getCurrentCell());
+                System.out.println("** cell "+player3.getPhysicsLocation()+" picked terrain: "+picked);
+            }
+        }
+    };
+    private final Vector3f walkDirection = new Vector3f();
+
+    @Override
+    public void simpleUpdate(final float tpf) {
+        Vector3f camDir = this.cam.getDirection().clone().multLocal(0.6f);
+        Vector3f camLeft = this.cam.getLeft().clone().multLocal(0.4f);
+        this.walkDirection.set(0, 0, 0);
+        if (this.left) {
+            this.walkDirection.addLocal(camLeft);
+        }
+        if (this.right) {
+            this.walkDirection.addLocal(camLeft.negate());
+        }
+        if (this.up) {
+            this.walkDirection.addLocal(camDir);
+        }
+        if (this.down) {
+            this.walkDirection.addLocal(camDir.negate());
+        }
+
+        if (usePhysics) {
+            this.player3.setWalkDirection(this.walkDirection);
+            this.cam.setLocation(this.player3.getPhysicsLocation());
+        }
+    }
+}
index b7e5fd0..7eff18b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2010 jMonkeyEngine
+ * Copyright (c) 2009-2012 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -31,7 +31,6 @@
  */
 package jme3test.terrain;
 
-import jme3tools.converters.ImageToAwt;
 import com.jme3.app.SimpleApplication;
 import com.jme3.font.BitmapText;
 import com.jme3.input.KeyInput;
@@ -44,11 +43,10 @@ import com.jme3.math.ColorRGBA;
 import com.jme3.math.Vector3f;
 import com.jme3.scene.Geometry;
 import com.jme3.terrain.geomipmap.TerrainLodControl;
-import com.jme3.terrain.heightmap.AbstractHeightMap;
-import com.jme3.terrain.heightmap.ImageBasedHeightMap;
 import com.jme3.terrain.geomipmap.TerrainQuad;
 import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
-import com.jme3.terrain.geomipmap.lodcalc.SimpleLodThreshold;
+import com.jme3.terrain.heightmap.AbstractHeightMap;
+import com.jme3.terrain.heightmap.ImageBasedHeightMap;
 import com.jme3.texture.Texture;
 import com.jme3.texture.Texture.WrapMode;
 
@@ -141,7 +139,7 @@ public class TerrainTest extends SimpleApplication {
         try {
             //heightmap = new HillHeightMap(1025, 1000, 50, 100, (byte) 3);
 
-            heightmap = new ImageBasedHeightMap(ImageToAwt.convert(heightMapImage.getImage(), false, true, 0), 1f);
+            heightmap = new ImageBasedHeightMap(heightMapImage.getImage(), 1f);
             heightmap.load();
 
         } catch (Exception e) {
@@ -163,7 +161,7 @@ public class TerrainTest extends SimpleApplication {
         terrain.addControl(control);
         terrain.setMaterial(matRock);
         terrain.setLocalTranslation(0, -100, 0);
-        terrain.setLocalScale(2f, 1f, 2f);
+        terrain.setLocalScale(2f, 0.5f, 2f);
         rootNode.attachChild(terrain);
 
         DirectionalLight light = new DirectionalLight();
index 533dce4..21f8435 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2010 jMonkeyEngine
+ * Copyright (c) 2009-2012 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  */
 package jme3test.terrain;
 
-import jme3tools.converters.ImageToAwt;
 import com.jme3.app.SimpleApplication;
 import com.jme3.bounding.BoundingBox;
 import com.jme3.font.BitmapText;
 import com.jme3.input.KeyInput;
 import com.jme3.input.controls.ActionListener;
 import com.jme3.input.controls.KeyTrigger;
-import com.jme3.light.AmbientLight;
 import com.jme3.light.DirectionalLight;
 import com.jme3.light.PointLight;
 import com.jme3.material.Material;
 import com.jme3.math.ColorRGBA;
 import com.jme3.math.Vector3f;
 import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
 import com.jme3.scene.Spatial;
+import com.jme3.scene.debug.Arrow;
 import com.jme3.terrain.geomipmap.TerrainLodControl;
-import com.jme3.terrain.heightmap.AbstractHeightMap;
-import com.jme3.terrain.heightmap.ImageBasedHeightMap;
 import com.jme3.terrain.geomipmap.TerrainQuad;
 import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
+import com.jme3.terrain.heightmap.AbstractHeightMap;
+import com.jme3.terrain.heightmap.ImageBasedHeightMap;
 import com.jme3.texture.Texture;
 import com.jme3.texture.Texture.WrapMode;
 import com.jme3.util.SkyFactory;
@@ -97,26 +97,28 @@ public class TerrainTestAdvanced extends SimpleApplication {
         // TERRAIN TEXTURE material
         matTerrain = new Material(assetManager, "Common/MatDefs/Terrain/TerrainLighting.j3md");
         matTerrain.setBoolean("useTriPlanarMapping", false);
+        matTerrain.setFloat("Shininess", 0.0f);
 
         // ALPHA map (for splat textures)
         matTerrain.setTexture("AlphaMap", assetManager.loadTexture("Textures/Terrain/splat/alpha1.png"));
         matTerrain.setTexture("AlphaMap_1", assetManager.loadTexture("Textures/Terrain/splat/alpha2.png"));
-
+        // this material also supports 'AlphaMap_2', so you can get up to 12 diffuse textures
+        
         // HEIGHTMAP image (for the terrain heightmap)
         Texture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png");
-
+        
+        // DIRT texture, Diffuse textures 0 to 3 use the first AlphaMap
+        Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg");
+        dirt.setWrap(WrapMode.Repeat);
+        matTerrain.setTexture("DiffuseMap", dirt);
+        matTerrain.setFloat("DiffuseMap_0_scale", dirtScale);
+        
         // GRASS texture
         Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg");
         grass.setWrap(WrapMode.Repeat);
         matTerrain.setTexture("DiffuseMap_1", grass);
         matTerrain.setFloat("DiffuseMap_1_scale", grassScale);
 
-        // DIRT texture
-        Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg");
-        dirt.setWrap(WrapMode.Repeat);
-        matTerrain.setTexture("DiffuseMap", dirt);
-        matTerrain.setFloat("DiffuseMap_0_scale", dirtScale);
-
         // ROCK texture
         Texture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg");
         rock.setWrap(WrapMode.Repeat);
@@ -129,12 +131,14 @@ public class TerrainTestAdvanced extends SimpleApplication {
         matTerrain.setTexture("DiffuseMap_3", brick);
         matTerrain.setFloat("DiffuseMap_3_scale", rockScale);
 
-        // RIVER ROCK texture
+        // RIVER ROCK texture, this texture will use the next alphaMap: AlphaMap_1
         Texture riverRock = assetManager.loadTexture("Textures/Terrain/Pond/Pond.jpg");
         riverRock.setWrap(WrapMode.Repeat);
         matTerrain.setTexture("DiffuseMap_4", riverRock);
         matTerrain.setFloat("DiffuseMap_4_scale", rockScale);
-
+        
+        // diffuse textures 4 to 7 use AlphaMap_1
+        // diffuse textures 8 to 11 use AlphaMap_2
 
         Texture normalMap0 = assetManager.loadTexture("Textures/Terrain/splat/grass_normal.jpg");
         normalMap0.setWrap(WrapMode.Repeat);
@@ -142,22 +146,23 @@ public class TerrainTestAdvanced extends SimpleApplication {
         normalMap1.setWrap(WrapMode.Repeat);
         Texture normalMap2 = assetManager.loadTexture("Textures/Terrain/splat/road_normal.png");
         normalMap2.setWrap(WrapMode.Repeat);
-        matTerrain.setTexture("NormalMap", normalMap2);
+        //matTerrain.setTexture("NormalMap", normalMap0);
         matTerrain.setTexture("NormalMap_1", normalMap2);
         matTerrain.setTexture("NormalMap_2", normalMap2);
         matTerrain.setTexture("NormalMap_4", normalMap2);
 
-        // WIREFRAME material
+        
+        // WIREFRAME material (used to debug the terrain, only useful for this test case)
         matWire = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
         matWire.getAdditionalRenderState().setWireframe(true);
         matWire.setColor("Color", ColorRGBA.Green);
-
+        
         createSky();
 
         // CREATE HEIGHTMAP
         AbstractHeightMap heightmap = null;
         try {
-            heightmap = new ImageBasedHeightMap(ImageToAwt.convert(heightMapImage.getImage(), false, true, 0), 0.5f);
+            heightmap = new ImageBasedHeightMap(heightMapImage.getImage(), 0.5f);
             heightmap.load();
             heightmap.smooth(0.9f, 1);
 
@@ -185,16 +190,18 @@ public class TerrainTestAdvanced extends SimpleApplication {
         terrain.setLocalScale(1f, 1f, 1f);
         rootNode.attachChild(terrain);
         
-        Material debugMat = assetManager.loadMaterial("Common/Materials/VertexColor.j3m");
+        //Material debugMat = assetManager.loadMaterial("Common/Materials/VertexColor.j3m");
         //terrain.generateDebugTangents(debugMat);
 
         DirectionalLight light = new DirectionalLight();
-        light.setDirection((new Vector3f(-0.25f, -1f, -0.25f)).normalize());
+        light.setDirection((new Vector3f(-0.1f, -0.1f, -0.1f)).normalize());
         rootNode.addLight(light);
 
         cam.setLocation(new Vector3f(0, 10, -10));
         cam.lookAtDirection(new Vector3f(0, -1.5f, -1).normalizeLocal(), Vector3f.UNIT_Y);
         flyCam.setMoveSpeed(400);
+        
+        rootNode.attachChild(createAxisMarker(20));
     }
 
     public void loadHintText() {
@@ -213,8 +220,8 @@ public class TerrainTestAdvanced extends SimpleApplication {
         inputManager.addListener(actionListener, "triPlanar");
         inputManager.addMapping("WardIso", new KeyTrigger(KeyInput.KEY_9));
         inputManager.addListener(actionListener, "WardIso");
-        inputManager.addMapping("Minnaert", new KeyTrigger(KeyInput.KEY_0));
-        inputManager.addListener(actionListener, "Minnaert");
+        inputManager.addMapping("DetachControl", new KeyTrigger(KeyInput.KEY_0));
+        inputManager.addListener(actionListener, "DetachControl");
     }
     private ActionListener actionListener = new ActionListener() {
 
@@ -246,6 +253,15 @@ public class TerrainTestAdvanced extends SimpleApplication {
                     matTerrain.setFloat("DiffuseMap_3_scale", rockScale);
                     matTerrain.setFloat("DiffuseMap_4_scale", rockScale);
                 }
+            } if (name.equals("DetachControl") && !pressed) {
+                TerrainLodControl control = terrain.getControl(TerrainLodControl.class);
+                if (control != null)
+                    control.detachAndCleanUpControl();
+                else {
+                    control = new TerrainLodControl(terrain, cam);
+                    terrain.addControl(control);
+                }
+                    
             }
         }
     };
@@ -261,4 +277,35 @@ public class TerrainTestAdvanced extends SimpleApplication {
         Spatial sky = SkyFactory.createSky(assetManager, west, east, north, south, up, down);
         rootNode.attachChild(sky);
     }
+    
+    protected Node createAxisMarker(float arrowSize) {
+
+        Material redMat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+        redMat.getAdditionalRenderState().setWireframe(true);
+        redMat.setColor("Color", ColorRGBA.Red);
+        
+        Material greenMat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+        greenMat.getAdditionalRenderState().setWireframe(true);
+        greenMat.setColor("Color", ColorRGBA.Green);
+        
+        Material blueMat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+        blueMat.getAdditionalRenderState().setWireframe(true);
+        blueMat.setColor("Color", ColorRGBA.Blue);
+
+        Node axis = new Node();
+
+        // create arrows
+        Geometry arrowX = new Geometry("arrowX", new Arrow(new Vector3f(arrowSize, 0, 0)));
+        arrowX.setMaterial(redMat);
+        Geometry arrowY = new Geometry("arrowY", new Arrow(new Vector3f(0, arrowSize, 0)));
+        arrowY.setMaterial(greenMat);
+        Geometry arrowZ = new Geometry("arrowZ", new Arrow(new Vector3f(0, 0, arrowSize)));
+        arrowZ.setMaterial(blueMat);
+        axis.attachChild(arrowX);
+        axis.attachChild(arrowY);
+        axis.attachChild(arrowZ);
+
+        //axis.setModelBound(new BoundingBox());
+        return axis;
+    }
 }
diff --git a/engine/src/test/jme3test/terrain/TerrainTestAndroid.java b/engine/src/test/jme3test/terrain/TerrainTestAndroid.java
new file mode 100644 (file)
index 0000000..d5b74b2
--- /dev/null
@@ -0,0 +1,200 @@
+/*\r
+ * Copyright (c) 2009-2012 jMonkeyEngine\r
+ * All rights reserved.\r
+ *\r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions are\r
+ * met:\r
+ *\r
+ * * Redistributions of source code must retain the above copyright\r
+ *   notice, this list of conditions and the following disclaimer.\r
+ *\r
+ * * Redistributions in binary form must reproduce the above copyright\r
+ *   notice, this list of conditions and the following disclaimer in the\r
+ *   documentation and/or other materials provided with the distribution.\r
+ *\r
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors\r
+ *   may be used to endorse or promote products derived from this software\r
+ *   without specific prior written permission.\r
+ *\r
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\r
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED\r
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\r
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\r
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\r
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\r
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\r
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\r
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\r
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\r
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+ */\r
+package jme3test.terrain;\r
+\r
+import com.jme3.app.SimpleApplication;\r
+import com.jme3.font.BitmapText;\r
+import com.jme3.input.KeyInput;\r
+import com.jme3.input.controls.ActionListener;\r
+import com.jme3.input.controls.KeyTrigger;\r
+import com.jme3.light.DirectionalLight;\r
+import com.jme3.light.PointLight;\r
+import com.jme3.material.Material;\r
+import com.jme3.math.ColorRGBA;\r
+import com.jme3.math.Vector3f;\r
+import com.jme3.scene.Geometry;\r
+import com.jme3.terrain.geomipmap.TerrainLodControl;\r
+import com.jme3.terrain.geomipmap.TerrainQuad;\r
+import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;\r
+import com.jme3.terrain.heightmap.AbstractHeightMap;\r
+import com.jme3.terrain.heightmap.ImageBasedHeightMap;\r
+import com.jme3.texture.Texture;\r
+import com.jme3.texture.Texture.WrapMode;\r
+\r
+/**\r
+ * Demonstrates how to use terrain on Android.\r
+ * The only difference is it uses a much smaller heightmap so it won't use up\r
+ * all of the android device's memory.\r
+ *\r
+ * @author bowens\r
+ */\r
+public class TerrainTestAndroid extends SimpleApplication {\r
+\r
+    private TerrainQuad terrain;\r
+    Material matRock;\r
+    Material matWire;\r
+    boolean wireframe = false;\r
+    boolean triPlanar = false;\r
+    protected BitmapText hintText;\r
+    PointLight pl;\r
+    Geometry lightMdl;\r
+    private float grassScale = 64;\r
+    private float dirtScale = 16;\r
+    private float rockScale = 128;\r
+\r
+    public static void main(String[] args) {\r
+        TerrainTestAndroid app = new TerrainTestAndroid();\r
+        app.start();\r
+    }\r
+\r
+    @Override\r
+    public void initialize() {\r
+        super.initialize();\r
+\r
+        loadHintText();\r
+    }\r
+\r
+    @Override\r
+    public void simpleInitApp() {\r
+        setupKeys();\r
+\r
+        // First, we load up our textures and the heightmap texture for the terrain\r
+\r
+        // TERRAIN TEXTURE material\r
+        matRock = new Material(assetManager, "Common/MatDefs/Terrain/Terrain.j3md");\r
+        matRock.setBoolean("useTriPlanarMapping", false);\r
+\r
+        // ALPHA map (for splat textures)\r
+        matRock.setTexture("Alpha", assetManager.loadTexture("Textures/Terrain/splat/alphamap.png"));\r
+\r
+        // HEIGHTMAP image (for the terrain heightmap)\r
+        Texture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains128.png");\r
+\r
+        // GRASS texture\r
+        Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg");\r
+        grass.setWrap(WrapMode.Repeat);\r
+        matRock.setTexture("Tex1", grass);\r
+        matRock.setFloat("Tex1Scale", grassScale);\r
+\r
+        // DIRT texture\r
+        Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg");\r
+        dirt.setWrap(WrapMode.Repeat);\r
+        matRock.setTexture("Tex2", dirt);\r
+        matRock.setFloat("Tex2Scale", dirtScale);\r
+\r
+        // ROCK texture\r
+        Texture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg");\r
+        rock.setWrap(WrapMode.Repeat);\r
+        matRock.setTexture("Tex3", rock);\r
+        matRock.setFloat("Tex3Scale", rockScale);\r
+\r
+        // WIREFRAME material\r
+        matWire = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");\r
+        matWire.getAdditionalRenderState().setWireframe(true);\r
+        matWire.setColor("Color", ColorRGBA.Green);\r
+\r
+        // CREATE HEIGHTMAP\r
+        AbstractHeightMap heightmap = null;\r
+        try {\r
+            heightmap = new ImageBasedHeightMap(heightMapImage.getImage(), 1f);\r
+            heightmap.load();\r
+\r
+        } catch (Exception e) {\r
+            e.printStackTrace();\r
+        }\r
+\r
+        /*\r
+         * Here we create the actual terrain. The tiles will be 33x33, and the total size of the\r
+         * terrain will be 129x129. It uses the heightmap we created to generate the height values.\r
+         */\r
+        terrain = new TerrainQuad("terrain", 33, 129, heightmap.getHeightMap());\r
+        TerrainLodControl control = new TerrainLodControl(terrain, getCamera());\r
+        control.setLodCalculator( new DistanceLodCalculator(33, 2.7f) ); // patch size, and a multiplier\r
+        terrain.addControl(control);\r
+        terrain.setMaterial(matRock);\r
+        terrain.setLocalTranslation(0, -100, 0);\r
+        terrain.setLocalScale(8f, 0.5f, 8f);\r
+        rootNode.attachChild(terrain);\r
+\r
+        DirectionalLight light = new DirectionalLight();\r
+        light.setDirection((new Vector3f(-0.5f, -1f, -0.5f)).normalize());\r
+        rootNode.addLight(light);\r
+\r
+        cam.setLocation(new Vector3f(0, 10, -10));\r
+        cam.lookAtDirection(new Vector3f(0, -1.5f, -1).normalizeLocal(), Vector3f.UNIT_Y);\r
+    }\r
+\r
+    public void loadHintText() {\r
+        hintText = new BitmapText(guiFont, false);\r
+        hintText.setSize(guiFont.getCharSet().getRenderedSize());\r
+        hintText.setLocalTranslation(0, getCamera().getHeight(), 0);\r
+        hintText.setText("Hit T to switch to wireframe,  P to switch to tri-planar texturing");\r
+        guiNode.attachChild(hintText);\r
+    }\r
+\r
+    private void setupKeys() {\r
+        flyCam.setMoveSpeed(50);\r
+        inputManager.addMapping("wireframe", new KeyTrigger(KeyInput.KEY_T));\r
+        inputManager.addListener(actionListener, "wireframe");\r
+        inputManager.addMapping("triPlanar", new KeyTrigger(KeyInput.KEY_P));\r
+        inputManager.addListener(actionListener, "triPlanar");\r
+    }\r
+    private ActionListener actionListener = new ActionListener() {\r
+\r
+        public void onAction(String name, boolean pressed, float tpf) {\r
+            if (name.equals("wireframe") && !pressed) {\r
+                wireframe = !wireframe;\r
+                if (!wireframe) {\r
+                    terrain.setMaterial(matWire);\r
+                } else {\r
+                    terrain.setMaterial(matRock);\r
+                }\r
+            } else if (name.equals("triPlanar") && !pressed) {\r
+                triPlanar = !triPlanar;\r
+                if (triPlanar) {\r
+                    matRock.setBoolean("useTriPlanarMapping", true);\r
+                    // planar textures don't use the mesh's texture coordinates but real world coordinates,\r
+                    // so we need to convert these texture coordinate scales into real world scales so it looks\r
+                    // the same when we switch to/from tr-planar mode\r
+                    matRock.setFloat("Tex1Scale", 1f / (float) (512f / grassScale));\r
+                    matRock.setFloat("Tex2Scale", 1f / (float) (512f / dirtScale));\r
+                    matRock.setFloat("Tex3Scale", 1f / (float) (512f / rockScale));\r
+                } else {\r
+                    matRock.setBoolean("useTriPlanarMapping", false);\r
+                    matRock.setFloat("Tex1Scale", grassScale);\r
+                    matRock.setFloat("Tex2Scale", dirtScale);\r
+                    matRock.setFloat("Tex3Scale", rockScale);\r
+                }\r
+            }\r
+        }\r
+    };\r
+}\r
index a408f71..32f1734 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2010 jMonkeyEngine
+ * Copyright (c) 2009-2012 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  */
 package jme3test.terrain;
 
-import jme3tools.converters.ImageToAwt;
-import com.jme3.bullet.collision.shapes.SphereCollisionShape;
+import com.jme3.app.SimpleApplication;
 import com.jme3.bounding.BoundingBox;
 import com.jme3.bullet.BulletAppState;
-import com.jme3.app.SimpleApplication;
+import com.jme3.bullet.collision.shapes.SphereCollisionShape;
 import com.jme3.bullet.control.RigidBodyControl;
 import com.jme3.collision.CollisionResult;
 import com.jme3.collision.CollisionResults;
@@ -57,10 +56,10 @@ import com.jme3.scene.Node;
 import com.jme3.scene.shape.Box;
 import com.jme3.scene.shape.Sphere;
 import com.jme3.terrain.geomipmap.TerrainLodControl;
-import com.jme3.terrain.heightmap.AbstractHeightMap;
-import com.jme3.terrain.heightmap.ImageBasedHeightMap;
 import com.jme3.terrain.geomipmap.TerrainQuad;
 import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
+import com.jme3.terrain.heightmap.AbstractHeightMap;
+import com.jme3.terrain.heightmap.ImageBasedHeightMap;
 import com.jme3.texture.Texture;
 import com.jme3.texture.Texture.WrapMode;
 
@@ -127,7 +126,7 @@ public class TerrainTestCollision extends SimpleApplication {
         matWire.setColor("Color", ColorRGBA.Green);
         AbstractHeightMap heightmap = null;
         try {
-            heightmap = new ImageBasedHeightMap(ImageToAwt.convert(heightMapImage.getImage(), false, true, 0), 0.25f);
+            heightmap = new ImageBasedHeightMap(heightMapImage.getImage(), 0.25f);
             heightmap.load();
 
         } catch (Exception e) {
index 0fe4a0c..b687615 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2010 jMonkeyEngine
+ * Copyright (c) 2009-2012 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -54,23 +54,22 @@ import com.jme3.scene.shape.Sphere;
 import com.jme3.terrain.geomipmap.TerrainGrid;
 import com.jme3.terrain.geomipmap.TerrainLodControl;
 import com.jme3.terrain.geomipmap.TerrainQuad;
+import com.jme3.terrain.geomipmap.grid.FractalTileLoader;
 import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
 import com.jme3.terrain.heightmap.AbstractHeightMap;
-import com.jme3.terrain.heightmap.FractalHeightMapGrid;
 import com.jme3.terrain.heightmap.ImageBasedHeightMap;
+import com.jme3.terrain.noise.ShaderUtils;
+import com.jme3.terrain.noise.basis.FilteredBasis;
+import com.jme3.terrain.noise.filter.IterativeFilter;
+import com.jme3.terrain.noise.filter.OptimizedErode;
+import com.jme3.terrain.noise.filter.PerturbFilter;
+import com.jme3.terrain.noise.filter.SmoothFilter;
+import com.jme3.terrain.noise.fractal.FractalSum;
+import com.jme3.terrain.noise.modulator.NoiseModulator;
 import com.jme3.texture.Texture;
 import com.jme3.texture.Texture.WrapMode;
 import java.util.ArrayList;
 import java.util.List;
-import jme3tools.converters.ImageToAwt;
-import org.novyon.noise.ShaderUtils;
-import org.novyon.noise.basis.FilteredBasis;
-import org.novyon.noise.filter.IterativeFilter;
-import org.novyon.noise.filter.OptimizedErode;
-import org.novyon.noise.filter.PerturbFilter;
-import org.novyon.noise.filter.SmoothFilter;
-import org.novyon.noise.fractal.FractalSum;
-import org.novyon.noise.modulator.NoiseModulator;
 
 /**
  *
@@ -281,6 +280,7 @@ public class TerrainTestModifyHeight extends SimpleApplication {
         matTerrain = new Material(assetManager, "Common/MatDefs/Terrain/TerrainLighting.j3md");
         matTerrain.setBoolean("useTriPlanarMapping", false);
         matTerrain.setBoolean("WardIso", true);
+        matTerrain.setFloat("Shininess", 0);
 
         // ALPHA map (for splat textures)
         matTerrain.setTexture("AlphaMap", assetManager.loadTexture("Textures/Terrain/splat/alphamap.png"));
@@ -307,7 +307,7 @@ public class TerrainTestModifyHeight extends SimpleApplication {
         Texture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png");
         AbstractHeightMap heightmap = null;
         try {
-            heightmap = new ImageBasedHeightMap(ImageToAwt.convert(heightMapImage.getImage(), false, true, 0), 0.5f);
+            heightmap = new ImageBasedHeightMap(heightMapImage.getImage(), 0.5f);
             heightmap.load();
             heightmap.smooth(0.9f, 1);
 
@@ -402,13 +402,13 @@ public class TerrainTestModifyHeight extends SimpleApplication {
 
         ground.addPreFilter(iterate);
 
-        this.terrain = new TerrainGrid("terrain", 65, 257, new FractalHeightMapGrid(ground, null, 256f));
+        this.terrain = new TerrainGrid("terrain", 65, 257, new FractalTileLoader(ground, 256f));
 
 
         terrain.setMaterial(matTerrain);
         terrain.setLocalTranslation(0, 0, 0);
         terrain.setLocalScale(2f, 1f, 2f);
-        ((TerrainGrid)terrain).initialize(Vector3f.ZERO);
+        
         rootNode.attachChild(this.terrain);
 
         TerrainLodControl control = new TerrainLodControl(this.terrain, getCamera());
index b8c6153..b6bba1c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2010 jMonkeyEngine
+ * Copyright (c) 2009-2012 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -52,15 +52,9 @@ import com.jme3.terrain.heightmap.AbstractHeightMap;
 import com.jme3.terrain.heightmap.ImageBasedHeightMap;
 import com.jme3.texture.Texture;
 import com.jme3.texture.Texture.WrapMode;
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
+import java.io.*;
 import java.util.logging.Level;
 import java.util.logging.Logger;
-import jme3tools.converters.ImageToAwt;
 
 /**
  * Saves and loads terrain.
@@ -147,7 +141,7 @@ public class TerrainTestReadWrite extends SimpleApplication {
         // CREATE HEIGHTMAP
         AbstractHeightMap heightmap = null;
         try {
-            heightmap = new ImageBasedHeightMap(ImageToAwt.convert(heightMapImage.getImage(), false, true, 0), 1f);
+            heightmap = new ImageBasedHeightMap(heightMapImage.getImage(), 1f);
             heightmap.load();
 
         } catch (Exception e) {
diff --git a/engine/src/test/jme3test/terrain/TerrainTestTile.java b/engine/src/test/jme3test/terrain/TerrainTestTile.java
new file mode 100644 (file)
index 0000000..4e093e8
--- /dev/null
@@ -0,0 +1,356 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.terrain;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.font.BitmapText;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.light.AmbientLight;
+import com.jme3.light.DirectionalLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.shape.Sphere;
+import com.jme3.terrain.ProgressMonitor;
+import com.jme3.terrain.Terrain;
+import com.jme3.terrain.geomipmap.MultiTerrainLodControl;
+import com.jme3.terrain.geomipmap.NeighbourFinder;
+import com.jme3.terrain.geomipmap.TerrainQuad;
+import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapMode;
+import java.util.List;
+
+/**
+ * Demonstrates the NeighbourFinder interface for TerrainQuads,
+ * allowing you to tile terrains together without having to use
+ * TerrainGrid. It also introduces the MultiTerrainLodControl that
+ * will seam the edges of all the terrains supplied.
+ * 
+ * @author sploreg
+ */
+public class TerrainTestTile extends SimpleApplication {
+
+    private TiledTerrain terrain;
+    Material matTerrain;
+    Material matWire;
+    boolean wireframe = true;
+    boolean triPlanar = false;
+    boolean wardiso = false;
+    boolean minnaert = false;
+    protected BitmapText hintText;
+    private float grassScale = 256;
+    
+
+    public static void main(String[] args) {
+        TerrainTestTile app = new TerrainTestTile();
+        app.start();
+    }
+
+    
+    
+    @Override
+    public void simpleInitApp() {
+        loadHintText();
+        setupKeys();
+
+        // WIREFRAME material
+        matWire = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+        matWire.getAdditionalRenderState().setWireframe(true);
+        matWire.setColor("Color", ColorRGBA.Green);
+        
+        terrain = new TiledTerrain();
+        rootNode.attachChild(terrain);
+        
+        DirectionalLight light = new DirectionalLight();
+        light.setDirection((new Vector3f(-0.5f, -1f, -0.5f)).normalize());
+        rootNode.addLight(light);
+
+        AmbientLight ambLight = new AmbientLight();
+        ambLight.setColor(new ColorRGBA(1f, 1f, 0.8f, 0.2f));
+        rootNode.addLight(ambLight);
+
+        cam.setLocation(new Vector3f(0, 256, 0));
+        cam.lookAtDirection(new Vector3f(0, -1, -1).normalizeLocal(), Vector3f.UNIT_Y);
+        
+        
+        Sphere s = new Sphere(12, 12, 3);
+        Geometry g = new Geometry("marker");
+        g.setMesh(s);
+        Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+        mat.setColor("Color", ColorRGBA.Red);
+        g.setMaterial(mat);
+        g.setLocalTranslation(0, -100, 0);
+        rootNode.attachChild(g);
+        
+        Geometry g2 = new Geometry("marker");
+        g2.setMesh(s);
+        mat.setColor("Color", ColorRGBA.Red);
+        g2.setMaterial(mat);
+        g2.setLocalTranslation(10, -100, 0);
+        rootNode.attachChild(g2);
+        
+        Geometry g3 = new Geometry("marker");
+        g3.setMesh(s);
+        mat.setColor("Color", ColorRGBA.Red);
+        g3.setMaterial(mat);
+        g3.setLocalTranslation(0, -100, 10);
+        rootNode.attachChild(g3);
+    }
+    
+    public void loadHintText() {
+        hintText = new BitmapText(guiFont, false);
+        hintText.setLocalTranslation(0, getCamera().getHeight(), 0);
+        hintText.setText("Hit 'T' to toggle wireframe");
+        guiNode.attachChild(hintText);
+    }
+
+
+    private void setupKeys() {
+        flyCam.setMoveSpeed(100);
+        inputManager.addMapping("wireframe", new KeyTrigger(KeyInput.KEY_T));
+        inputManager.addListener(actionListener, "wireframe");
+    }
+    private ActionListener actionListener = new ActionListener() {
+
+        public void onAction(String name, boolean pressed, float tpf) {
+            if (name.equals("wireframe") && !pressed) {
+                wireframe = !wireframe;
+                if (!wireframe) {
+                    terrain.setMaterial(matWire);
+                } else {
+                    terrain.setMaterial(matTerrain);
+                }
+            }
+        }
+    };
+
+    /**
+     * A sample class (node in this case) that demonstrates
+     * the use of NeighbourFinder.
+     * It just links up the left,right,top,bottom TerrainQuads
+     * so LOD can work.
+     * It does not implement many of the Terrain interface's methods,
+     * you will want to do that for your own implementations.
+     */
+    private class TiledTerrain extends Node implements Terrain, NeighbourFinder {
+
+        private TerrainQuad terrain1;
+        private TerrainQuad terrain2;
+        private TerrainQuad terrain3;
+        private TerrainQuad terrain4;
+        
+        TiledTerrain() {
+            // TERRAIN TEXTURE material
+            matTerrain = new Material(assetManager, "Common/MatDefs/Terrain/TerrainLighting.j3md");
+            matTerrain.setBoolean("useTriPlanarMapping", false);
+            matTerrain.setBoolean("WardIso", true);
+            matTerrain.setFloat("Shininess", 0);
+
+            // GRASS texture
+            Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg");
+            grass.setWrap(WrapMode.Repeat);
+            matTerrain.setTexture("DiffuseMap", grass);
+            matTerrain.setFloat("DiffuseMap_0_scale", grassScale);
+
+            // CREATE THE TERRAIN
+            terrain1 = new TerrainQuad("terrain 1", 65, 513, null);
+            terrain1.setMaterial(matTerrain);
+            terrain1.setLocalTranslation(-256, -100, -256);
+            terrain1.setLocalScale(1f, 1f, 1f);
+            this.attachChild(terrain1);
+
+            terrain2 = new TerrainQuad("terrain 2", 65, 513, null);
+            terrain2.setMaterial(matTerrain);
+            terrain2.setLocalTranslation(-256, -100, 256);
+            terrain2.setLocalScale(1f, 1f, 1f);
+            this.attachChild(terrain2);
+
+            terrain3 = new TerrainQuad("terrain 3", 65, 513, null);
+            terrain3.setMaterial(matTerrain);
+            terrain3.setLocalTranslation(256, -100, -256);
+            terrain3.setLocalScale(1f, 1f, 1f);
+            this.attachChild(terrain3);
+
+            terrain4 = new TerrainQuad("terrain 4", 65, 513, null);
+            terrain4.setMaterial(matTerrain);
+            terrain4.setLocalTranslation(256, -100, 256);
+            terrain4.setLocalScale(1f, 1f, 1f);
+            this.attachChild(terrain4);
+            
+            terrain1.setNeighbourFinder(this);
+            terrain2.setNeighbourFinder(this);
+            terrain3.setNeighbourFinder(this);
+            terrain4.setNeighbourFinder(this);
+            
+            MultiTerrainLodControl lodControl = new MultiTerrainLodControl(getCamera());
+            lodControl.setLodCalculator( new DistanceLodCalculator(65, 2.7f) ); // patch size, and a multiplier
+            lodControl.addTerrain(terrain1);
+            lodControl.addTerrain(terrain2);
+            lodControl.addTerrain(terrain3);// order of these seems to matter
+            lodControl.addTerrain(terrain4);
+            this.addControl(lodControl);
+            
+        }
+        
+        /**
+         * 1  3
+         * 2  4
+         */
+        public TerrainQuad getRightQuad(TerrainQuad center) {
+            //System.out.println("lookup neighbour");
+            if (center == terrain1)
+                return terrain3;
+            if (center == terrain2)
+                return terrain4;
+            
+            return null;
+        }
+
+        /**
+         * 1  3
+         * 2  4
+         */
+        public TerrainQuad getLeftQuad(TerrainQuad center) {
+            //System.out.println("lookup neighbour");
+            if (center == terrain3)
+                return terrain1;
+            if (center == terrain4)
+                return terrain2;
+            
+            return null;
+        }
+
+        /**
+         * 1  3
+         * 2  4
+         */
+        public TerrainQuad getTopQuad(TerrainQuad center) {
+            //System.out.println("lookup neighbour");
+            if (center == terrain2)
+                return terrain1;
+            if (center == terrain4)
+                return terrain3;
+            
+            return null;
+        }
+
+        /**
+         * 1  3
+         * 2  4
+         */
+        public TerrainQuad getDownQuad(TerrainQuad center) {
+            //System.out.println("lookup neighbour");
+            if (center == terrain1)
+                return terrain2;
+            if (center == terrain3)
+                return terrain4;
+            
+            return null;
+        }
+        
+        public float getHeight(Vector2f xz) {
+            // you will have to offset the coordinate for each terrain, to center on it
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public Vector3f getNormal(Vector2f xz) {
+            // you will have to offset the coordinate for each terrain, to center on it
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public float getHeightmapHeight(Vector2f xz) {
+            // you will have to offset the coordinate for each terrain, to center on it
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public void setHeight(Vector2f xzCoordinate, float height) {
+            // you will have to offset the coordinate for each terrain, to center on it
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public void setHeight(List<Vector2f> xz, List<Float> height) {
+            // you will have to offset the coordinate for each terrain, to center on it
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public void adjustHeight(Vector2f xzCoordinate, float delta) {
+            // you will have to offset the coordinate for each terrain, to center on it
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public void adjustHeight(List<Vector2f> xz, List<Float> height) {
+            // you will have to offset the coordinate for each terrain, to center on it
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public float[] getHeightMap() {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public int getMaxLod() {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public void setLocked(boolean locked) {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public void generateEntropy(ProgressMonitor monitor) {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public Material getMaterial() {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public Material getMaterial(Vector3f worldLocation) {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public int getTerrainSize() {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        public int getNumMajorSubdivisions() {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        
+        
+    }
+}