1 package jme3test.terrain; 2 3 import com.jme3.app.SimpleApplication; 4 import com.jme3.app.state.ScreenshotAppState; 5 import com.jme3.asset.plugins.HttpZipLocator; 6 import com.jme3.asset.plugins.ZipLocator; 7 import com.jme3.bullet.BulletAppState; 8 import com.jme3.bullet.collision.shapes.CapsuleCollisionShape; 9 import com.jme3.bullet.collision.shapes.HeightfieldCollisionShape; 10 import com.jme3.bullet.control.CharacterControl; 11 import com.jme3.bullet.control.RigidBodyControl; 12 import com.jme3.input.KeyInput; 13 import com.jme3.input.controls.ActionListener; 14 import com.jme3.input.controls.KeyTrigger; 15 import com.jme3.light.AmbientLight; 16 import com.jme3.light.DirectionalLight; 17 import com.jme3.material.Material; 18 import com.jme3.math.ColorRGBA; 19 import com.jme3.math.Vector2f; 20 import com.jme3.math.Vector3f; 21 import com.jme3.renderer.Camera; 22 import com.jme3.scene.Geometry; 23 import com.jme3.scene.Node; 24 import com.jme3.scene.Spatial; 25 import com.jme3.scene.debug.Arrow; 26 import com.jme3.terrain.geomipmap.TerrainGrid; 27 import com.jme3.terrain.geomipmap.TerrainGridListener; 28 import com.jme3.terrain.geomipmap.TerrainLodControl; 29 import com.jme3.terrain.geomipmap.TerrainQuad; 30 import com.jme3.terrain.geomipmap.grid.FractalTileLoader; 31 import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator; 32 import com.jme3.texture.Texture; 33 import com.jme3.texture.Texture.WrapMode; 34 import java.io.File; 35 import java.util.ArrayList; 36 import java.util.List; 37 import com.jme3.terrain.noise.ShaderUtils; 38 import com.jme3.terrain.noise.basis.FilteredBasis; 39 import com.jme3.terrain.noise.filter.IterativeFilter; 40 import com.jme3.terrain.noise.filter.OptimizedErode; 41 import com.jme3.terrain.noise.filter.PerturbFilter; 42 import com.jme3.terrain.noise.filter.SmoothFilter; 43 import com.jme3.terrain.noise.fractal.FractalSum; 44 import com.jme3.terrain.noise.modulator.NoiseModulator; 45 46 public class TerrainGridAlphaMapTest extends SimpleApplication { 47 48 private TerrainGrid terrain; 49 private float grassScale = 64; 50 private float dirtScale = 16; 51 private float rockScale = 128; 52 private boolean usePhysics = false; 53 main(final String[] args)54 public static void main(final String[] args) { 55 TerrainGridAlphaMapTest app = new TerrainGridAlphaMapTest(); 56 app.start(); 57 } 58 private CharacterControl player3; 59 private FractalSum base; 60 private PerturbFilter perturb; 61 private OptimizedErode therm; 62 private SmoothFilter smooth; 63 private IterativeFilter iterate; 64 private Material material; 65 private Material matWire; 66 67 @Override simpleInitApp()68 public void simpleInitApp() { 69 DirectionalLight sun = new DirectionalLight(); 70 sun.setColor(ColorRGBA.White); 71 sun.setDirection(new Vector3f(-1, -1, -1).normalizeLocal()); 72 rootNode.addLight(sun); 73 74 AmbientLight al = new AmbientLight(); 75 al.setColor(ColorRGBA.White.mult(1.3f)); 76 rootNode.addLight(al); 77 78 File file = new File("TerrainGridTestData.zip"); 79 if (!file.exists()) { 80 assetManager.registerLocator("http://jmonkeyengine.googlecode.com/files/TerrainGridTestData.zip", HttpZipLocator.class); 81 } else { 82 assetManager.registerLocator("TerrainGridTestData.zip", ZipLocator.class); 83 } 84 85 this.flyCam.setMoveSpeed(100f); 86 ScreenshotAppState state = new ScreenshotAppState(); 87 this.stateManager.attach(state); 88 89 // TERRAIN TEXTURE material 90 material = new Material(assetManager, "Common/MatDefs/Terrain/TerrainLighting.j3md"); 91 material.setBoolean("useTriPlanarMapping", false); 92 //material.setBoolean("isTerrainGrid", true); 93 material.setFloat("Shininess", 0.0f); 94 95 // GRASS texture 96 Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); 97 grass.setWrap(WrapMode.Repeat); 98 material.setTexture("DiffuseMap", grass); 99 material.setFloat("DiffuseMap_0_scale", grassScale); 100 101 // DIRT texture 102 Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg"); 103 dirt.setWrap(WrapMode.Repeat); 104 material.setTexture("DiffuseMap_1", dirt); 105 material.setFloat("DiffuseMap_1_scale", dirtScale); 106 107 // ROCK texture 108 Texture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg"); 109 rock.setWrap(WrapMode.Repeat); 110 material.setTexture("DiffuseMap_2", rock); 111 material.setFloat("DiffuseMap_2_scale", rockScale); 112 113 // WIREFRAME material 114 matWire = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); 115 matWire.getAdditionalRenderState().setWireframe(true); 116 matWire.setColor("Color", ColorRGBA.Green); 117 118 this.base = new FractalSum(); 119 this.base.setRoughness(0.7f); 120 this.base.setFrequency(1.0f); 121 this.base.setAmplitude(1.0f); 122 this.base.setLacunarity(2.12f); 123 this.base.setOctaves(8); 124 this.base.setScale(0.02125f); 125 this.base.addModulator(new NoiseModulator() { 126 127 @Override 128 public float value(float... in) { 129 return ShaderUtils.clamp(in[0] * 0.5f + 0.5f, 0, 1); 130 } 131 }); 132 133 FilteredBasis ground = new FilteredBasis(this.base); 134 135 this.perturb = new PerturbFilter(); 136 this.perturb.setMagnitude(0.119f); 137 138 this.therm = new OptimizedErode(); 139 this.therm.setRadius(5); 140 this.therm.setTalus(0.011f); 141 142 this.smooth = new SmoothFilter(); 143 this.smooth.setRadius(1); 144 this.smooth.setEffect(0.7f); 145 146 this.iterate = new IterativeFilter(); 147 this.iterate.addPreFilter(this.perturb); 148 this.iterate.addPostFilter(this.smooth); 149 this.iterate.setFilter(this.therm); 150 this.iterate.setIterations(1); 151 152 ground.addPreFilter(this.iterate); 153 154 this.terrain = new TerrainGrid("terrain", 33, 257, new FractalTileLoader(ground, 256)); 155 this.terrain.setMaterial(this.material); 156 157 this.terrain.setLocalTranslation(0, 0, 0); 158 this.terrain.setLocalScale(2f, 1f, 2f); 159 this.rootNode.attachChild(this.terrain); 160 161 List<Camera> cameras = new ArrayList<Camera>(); 162 cameras.add(this.getCamera()); 163 TerrainLodControl control = new TerrainLodControl(this.terrain, cameras); 164 control.setLodCalculator( new DistanceLodCalculator(33, 2.7f) ); // patch size, and a multiplier 165 this.terrain.addControl(control); 166 167 final BulletAppState bulletAppState = new BulletAppState(); 168 stateManager.attach(bulletAppState); 169 170 171 this.getCamera().setLocation(new Vector3f(0, 256, 0)); 172 173 this.viewPort.setBackgroundColor(new ColorRGBA(0.7f, 0.8f, 1f, 1f)); 174 175 if (usePhysics) { 176 CapsuleCollisionShape capsuleShape = new CapsuleCollisionShape(0.5f, 1.8f, 1); 177 player3 = new CharacterControl(capsuleShape, 0.5f); 178 player3.setJumpSpeed(20); 179 player3.setFallSpeed(10); 180 player3.setGravity(10); 181 182 player3.setPhysicsLocation(new Vector3f(cam.getLocation().x, 256, cam.getLocation().z)); 183 184 bulletAppState.getPhysicsSpace().add(player3); 185 186 } 187 terrain.addListener(new TerrainGridListener() { 188 189 public void gridMoved(Vector3f newCenter) { 190 } 191 192 public void tileAttached(Vector3f cell, TerrainQuad quad) { 193 Texture alpha = null; 194 try { 195 alpha = assetManager.loadTexture("TerrainAlphaTest/alpha_" + (int)cell.x+ "_" + (int)cell.z + ".png"); 196 } catch (Exception e) { 197 alpha = assetManager.loadTexture("TerrainAlphaTest/alpha_default.png"); 198 } 199 quad.getMaterial().setTexture("AlphaMap", alpha); 200 if (usePhysics) { 201 quad.addControl(new RigidBodyControl(new HeightfieldCollisionShape(quad.getHeightMap(), terrain.getLocalScale()), 0)); 202 bulletAppState.getPhysicsSpace().add(quad); 203 } 204 updateMarkerElevations(); 205 } 206 207 public void tileDetached(Vector3f cell, TerrainQuad quad) { 208 if (usePhysics) { 209 bulletAppState.getPhysicsSpace().remove(quad); 210 quad.removeControl(RigidBodyControl.class); 211 } 212 updateMarkerElevations(); 213 } 214 }); 215 216 this.initKeys(); 217 218 markers = new Node(); 219 rootNode.attachChild(markers); 220 createMarkerPoints(1); 221 } 222 223 Node markers; 224 225 createMarkerPoints(float count)226 private void createMarkerPoints(float count) { 227 Node center = createAxisMarker(10); 228 markers.attachChild(center); 229 230 float xS = (count-1)*terrain.getTerrainSize() - (terrain.getTerrainSize()/2); 231 float zS = (count-1)*terrain.getTerrainSize() - (terrain.getTerrainSize()/2); 232 float xSi = xS; 233 float zSi = zS; 234 for (int x=0; x<count*2; x++) { 235 for (int z=0; z<count*2; z++) { 236 Node m = createAxisMarker(5); 237 m.setLocalTranslation(xSi, 0, zSi); 238 markers.attachChild(m); 239 zSi += terrain.getTerrainSize(); 240 } 241 zSi = zS; 242 xSi += terrain.getTerrainSize(); 243 } 244 } 245 updateMarkerElevations()246 private void updateMarkerElevations() { 247 for (Spatial s : markers.getChildren()) { 248 float h = terrain.getHeight(new Vector2f(s.getLocalTranslation().x, s.getLocalTranslation().z)); 249 s.setLocalTranslation(s.getLocalTranslation().x, h+1, s.getLocalTranslation().z); 250 } 251 } 252 initKeys()253 private void initKeys() { 254 // You can map one or several inputs to one named action 255 this.inputManager.addMapping("Lefts", new KeyTrigger(KeyInput.KEY_A)); 256 this.inputManager.addMapping("Rights", new KeyTrigger(KeyInput.KEY_D)); 257 this.inputManager.addMapping("Ups", new KeyTrigger(KeyInput.KEY_W)); 258 this.inputManager.addMapping("Downs", new KeyTrigger(KeyInput.KEY_S)); 259 this.inputManager.addMapping("Jumps", new KeyTrigger(KeyInput.KEY_SPACE)); 260 this.inputManager.addListener(this.actionListener, "Lefts"); 261 this.inputManager.addListener(this.actionListener, "Rights"); 262 this.inputManager.addListener(this.actionListener, "Ups"); 263 this.inputManager.addListener(this.actionListener, "Downs"); 264 this.inputManager.addListener(this.actionListener, "Jumps"); 265 } 266 private boolean left; 267 private boolean right; 268 private boolean up; 269 private boolean down; 270 private final ActionListener actionListener = new ActionListener() { 271 272 @Override 273 public void onAction(final String name, final boolean keyPressed, final float tpf) { 274 if (name.equals("Lefts")) { 275 if (keyPressed) { 276 TerrainGridAlphaMapTest.this.left = true; 277 } else { 278 TerrainGridAlphaMapTest.this.left = false; 279 } 280 } else if (name.equals("Rights")) { 281 if (keyPressed) { 282 TerrainGridAlphaMapTest.this.right = true; 283 } else { 284 TerrainGridAlphaMapTest.this.right = false; 285 } 286 } else if (name.equals("Ups")) { 287 if (keyPressed) { 288 TerrainGridAlphaMapTest.this.up = true; 289 } else { 290 TerrainGridAlphaMapTest.this.up = false; 291 } 292 } else if (name.equals("Downs")) { 293 if (keyPressed) { 294 TerrainGridAlphaMapTest.this.down = true; 295 } else { 296 TerrainGridAlphaMapTest.this.down = false; 297 } 298 } else if (name.equals("Jumps")) { 299 TerrainGridAlphaMapTest.this.player3.jump(); 300 } 301 } 302 }; 303 private final Vector3f walkDirection = new Vector3f(); 304 305 @Override simpleUpdate(final float tpf)306 public void simpleUpdate(final float tpf) { 307 Vector3f camDir = this.cam.getDirection().clone().multLocal(0.6f); 308 Vector3f camLeft = this.cam.getLeft().clone().multLocal(0.4f); 309 this.walkDirection.set(0, 0, 0); 310 if (this.left) { 311 this.walkDirection.addLocal(camLeft); 312 } 313 if (this.right) { 314 this.walkDirection.addLocal(camLeft.negate()); 315 } 316 if (this.up) { 317 this.walkDirection.addLocal(camDir); 318 } 319 if (this.down) { 320 this.walkDirection.addLocal(camDir.negate()); 321 } 322 323 if (usePhysics) { 324 this.player3.setWalkDirection(this.walkDirection); 325 this.cam.setLocation(this.player3.getPhysicsLocation()); 326 } 327 } 328 createAxisMarker(float arrowSize)329 protected Node createAxisMarker(float arrowSize) { 330 331 Material redMat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); 332 redMat.getAdditionalRenderState().setWireframe(true); 333 redMat.setColor("Color", ColorRGBA.Red); 334 335 Material greenMat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); 336 greenMat.getAdditionalRenderState().setWireframe(true); 337 greenMat.setColor("Color", ColorRGBA.Green); 338 339 Material blueMat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); 340 blueMat.getAdditionalRenderState().setWireframe(true); 341 blueMat.setColor("Color", ColorRGBA.Blue); 342 343 Node axis = new Node(); 344 345 // create arrows 346 Geometry arrowX = new Geometry("arrowX", new Arrow(new Vector3f(arrowSize, 0, 0))); 347 arrowX.setMaterial(redMat); 348 Geometry arrowY = new Geometry("arrowY", new Arrow(new Vector3f(0, arrowSize, 0))); 349 arrowY.setMaterial(greenMat); 350 Geometry arrowZ = new Geometry("arrowZ", new Arrow(new Vector3f(0, 0, arrowSize))); 351 arrowZ.setMaterial(blueMat); 352 axis.attachChild(arrowX); 353 axis.attachChild(arrowY); 354 axis.attachChild(arrowZ); 355 356 //axis.setModelBound(new BoundingBox()); 357 return axis; 358 } 359 } 360