2011-03-14 12:55:32 +00:00
/ *
* 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 jme3test.terrain ;
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.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.Spatial ;
import com.jme3.terrain.geomipmap.TerrainLodControl ;
import com.jme3.terrain.geomipmap.TerrainQuad ;
2011-09-09 23:34:15 +00:00
import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator ;
2011-12-03 14:06:48 +00:00
import com.jme3.terrain.heightmap.AbstractHeightMap ;
import com.jme3.terrain.heightmap.ImageBasedHeightMap ;
2011-03-14 12:55:32 +00:00
import com.jme3.texture.Texture ;
import com.jme3.texture.Texture.WrapMode ;
import com.jme3.util.SkyFactory ;
2011-12-03 14:06:48 +00:00
import jme3tools.converters.ImageToAwt ;
2011-03-14 12:55:32 +00:00
/ * *
2011-06-18 00:10:36 +00:00
* Uses the terrain ' s lighting texture with normal maps and lights .
2011-03-14 12:55:32 +00:00
*
* @author bowens
* /
public class TerrainTestAdvanced extends SimpleApplication {
2011-06-12 02:35:19 +00:00
private TerrainQuad terrain ;
Material matTerrain ;
Material matWire ;
boolean wireframe = false ;
2011-03-14 12:55:32 +00:00
boolean triPlanar = false ;
boolean wardiso = false ;
boolean minnaert = false ;
2011-06-12 02:35:19 +00:00
protected BitmapText hintText ;
PointLight pl ;
Geometry lightMdl ;
2011-03-14 12:55:32 +00:00
private float grassScale = 64 ;
private float dirtScale = 16 ;
private float rockScale = 128 ;
2011-06-12 02:35:19 +00:00
public static void main ( String [ ] args ) {
TerrainTestAdvanced app = new TerrainTestAdvanced ( ) ;
app . start ( ) ;
}
2011-03-14 12:55:32 +00:00
2011-06-12 02:35:19 +00:00
@Override
public void initialize ( ) {
super . initialize ( ) ;
2011-03-14 12:55:32 +00:00
2011-06-12 02:35:19 +00:00
loadHintText ( ) ;
}
2011-03-14 12:55:32 +00:00
2011-06-12 02:35:19 +00:00
@Override
public void simpleInitApp ( ) {
setupKeys ( ) ;
2011-03-14 12:55:32 +00:00
// First, we load up our textures and the heightmap texture for the terrain
2011-06-12 02:35:19 +00:00
// TERRAIN TEXTURE material
matTerrain = new Material ( assetManager , " Common/MatDefs/Terrain/TerrainLighting.j3md " ) ;
2011-03-14 12:55:32 +00:00
matTerrain . setBoolean ( " useTriPlanarMapping " , false ) ;
2011-11-26 18:38:46 +00:00
matTerrain . setFloat ( " Shininess " , 0 . 0f ) ;
2011-03-14 12:55:32 +00:00
2011-06-12 02:35:19 +00:00
// ALPHA map (for splat textures)
2011-06-18 00:10:36 +00:00
matTerrain . setTexture ( " AlphaMap " , assetManager . loadTexture ( " Textures/Terrain/splat/alpha1.png " ) ) ;
matTerrain . setTexture ( " AlphaMap_1 " , assetManager . loadTexture ( " Textures/Terrain/splat/alpha2.png " ) ) ;
2011-03-14 12:55:32 +00:00
2011-06-12 02:35:19 +00:00
// HEIGHTMAP image (for the terrain heightmap)
Texture heightMapImage = assetManager . loadTexture ( " Textures/Terrain/splat/mountains512.png " ) ;
2011-03-14 12:55:32 +00:00
2011-06-12 02:35:19 +00:00
// GRASS texture
Texture grass = assetManager . loadTexture ( " Textures/Terrain/splat/grass.jpg " ) ;
grass . setWrap ( WrapMode . Repeat ) ;
2011-09-09 23:34:15 +00:00
matTerrain . setTexture ( " DiffuseMap_1 " , grass ) ;
matTerrain . setFloat ( " DiffuseMap_1_scale " , grassScale ) ;
2011-03-14 12:55:32 +00:00
2011-06-12 02:35:19 +00:00
// DIRT texture
Texture dirt = assetManager . loadTexture ( " Textures/Terrain/splat/dirt.jpg " ) ;
dirt . setWrap ( WrapMode . Repeat ) ;
2011-09-09 23:34:15 +00:00
matTerrain . setTexture ( " DiffuseMap " , dirt ) ;
matTerrain . setFloat ( " DiffuseMap_0_scale " , dirtScale ) ;
2011-06-12 02:35:19 +00:00
// ROCK texture
Texture rock = assetManager . loadTexture ( " Textures/Terrain/splat/road.jpg " ) ;
rock . setWrap ( WrapMode . Repeat ) ;
matTerrain . setTexture ( " DiffuseMap_2 " , rock ) ;
matTerrain . setFloat ( " DiffuseMap_2_scale " , rockScale ) ;
2011-06-18 00:10:36 +00:00
// BRICK texture
Texture brick = assetManager . loadTexture ( " Textures/Terrain/BrickWall/BrickWall.jpg " ) ;
brick . setWrap ( WrapMode . Repeat ) ;
matTerrain . setTexture ( " DiffuseMap_3 " , brick ) ;
matTerrain . setFloat ( " DiffuseMap_3_scale " , rockScale ) ;
// RIVER ROCK texture
2011-08-14 18:36:59 +00:00
Texture riverRock = assetManager . loadTexture ( " Textures/Terrain/Pond/Pond.jpg " ) ;
2011-06-18 00:10:36 +00:00
riverRock . setWrap ( WrapMode . Repeat ) ;
matTerrain . setTexture ( " DiffuseMap_4 " , riverRock ) ;
matTerrain . setFloat ( " DiffuseMap_4_scale " , rockScale ) ;
2011-03-14 12:55:32 +00:00
2011-06-13 04:04:57 +00:00
Texture normalMap0 = assetManager . loadTexture ( " Textures/Terrain/splat/grass_normal.jpg " ) ;
2011-03-14 12:55:32 +00:00
normalMap0 . setWrap ( WrapMode . Repeat ) ;
Texture normalMap1 = assetManager . loadTexture ( " Textures/Terrain/splat/dirt_normal.png " ) ;
normalMap1 . setWrap ( WrapMode . Repeat ) ;
Texture normalMap2 = assetManager . loadTexture ( " Textures/Terrain/splat/road_normal.png " ) ;
normalMap2 . setWrap ( WrapMode . Repeat ) ;
2011-09-09 23:34:15 +00:00
matTerrain . setTexture ( " NormalMap " , normalMap2 ) ;
2011-03-14 12:55:32 +00:00
matTerrain . setTexture ( " NormalMap_1 " , normalMap2 ) ;
matTerrain . setTexture ( " NormalMap_2 " , normalMap2 ) ;
2011-06-18 00:10:36 +00:00
matTerrain . setTexture ( " NormalMap_4 " , normalMap2 ) ;
2011-03-14 12:55:32 +00:00
2011-06-12 02:35:19 +00:00
// WIREFRAME material
matWire = new Material ( assetManager , " Common/MatDefs/Misc/Unshaded.j3md " ) ;
matWire . getAdditionalRenderState ( ) . setWireframe ( true ) ;
2011-03-14 12:55:32 +00:00
matWire . setColor ( " Color " , ColorRGBA . Green ) ;
createSky ( ) ;
2011-06-12 02:35:19 +00:00
// CREATE HEIGHTMAP
AbstractHeightMap heightmap = null ;
try {
2011-09-09 23:34:15 +00:00
heightmap = new ImageBasedHeightMap ( ImageToAwt . convert ( heightMapImage . getImage ( ) , false , true , 0 ) , 0 . 5f ) ;
2011-06-12 02:35:19 +00:00
heightmap . load ( ) ;
2011-09-09 23:34:15 +00:00
heightmap . smooth ( 0 . 9f , 1 ) ;
2011-06-12 02:35:19 +00:00
} catch ( Exception e ) {
e . printStackTrace ( ) ;
}
/ *
* Here we create the actual terrain . The tiles will be 65x65 , and the total size of the
* terrain will be 513x513 . It uses the heightmap we created to generate the height values .
* /
/ * *
* Optimal terrain patch size is 65 ( 64x64 ) .
* The total size is up to you . At 1025 it ran fine for me ( 200 + FPS ) , however at
* size = 2049 , it got really slow . But that is a jump from 2 million to 8 million triangles . . .
* /
terrain = new TerrainQuad ( " terrain " , 65 , 513 , heightmap . getHeightMap ( ) ) ; //, new LodPerspectiveCalculatorFactory(getCamera(), 4)); // add this in to see it use entropy for LOD calculations
2011-07-05 16:03:54 +00:00
TerrainLodControl control = new TerrainLodControl ( terrain , getCamera ( ) ) ;
2011-09-09 23:34:15 +00:00
control . setLodCalculator ( new DistanceLodCalculator ( 65 , 2 . 7f ) ) ; // patch size, and a multiplier
2011-06-12 02:35:19 +00:00
terrain . addControl ( control ) ;
terrain . setMaterial ( matTerrain ) ;
terrain . setModelBound ( new BoundingBox ( ) ) ;
terrain . updateModelBound ( ) ;
terrain . setLocalTranslation ( 0 , - 100 , 0 ) ;
terrain . setLocalScale ( 1f , 1f , 1f ) ;
rootNode . attachChild ( terrain ) ;
2011-09-09 23:34:15 +00:00
Material debugMat = assetManager . loadMaterial ( " Common/Materials/VertexColor.j3m " ) ;
//terrain.generateDebugTangents(debugMat);
2011-03-14 12:55:32 +00:00
DirectionalLight light = new DirectionalLight ( ) ;
2011-09-09 23:34:15 +00:00
light . setDirection ( ( new Vector3f ( - 0 . 25f , - 1f , - 0 . 25f ) ) . normalize ( ) ) ;
2011-03-14 12:55:32 +00:00
rootNode . addLight ( light ) ;
2011-06-13 04:04:57 +00:00
cam . setLocation ( new Vector3f ( 0 , 10 , - 10 ) ) ;
cam . lookAtDirection ( new Vector3f ( 0 , - 1 . 5f , - 1 ) . normalizeLocal ( ) , Vector3f . UNIT_Y ) ;
2011-09-09 23:34:15 +00:00
flyCam . setMoveSpeed ( 400 ) ;
2011-06-12 02:35:19 +00:00
}
public void loadHintText ( ) {
hintText = new BitmapText ( guiFont , false ) ;
hintText . setSize ( guiFont . getCharSet ( ) . getRenderedSize ( ) ) ;
hintText . setLocalTranslation ( 0 , getCamera ( ) . getHeight ( ) , 0 ) ;
hintText . setText ( " Hit T to switch to wireframe, P to switch to tri-planar texturing " ) ;
guiNode . attachChild ( hintText ) ;
}
private void setupKeys ( ) {
flyCam . setMoveSpeed ( 50 ) ;
inputManager . addMapping ( " wireframe " , new KeyTrigger ( KeyInput . KEY_T ) ) ;
inputManager . addListener ( actionListener , " wireframe " ) ;
2011-03-14 12:55:32 +00:00
inputManager . addMapping ( " triPlanar " , new KeyTrigger ( KeyInput . KEY_P ) ) ;
2011-06-12 02:35:19 +00:00
inputManager . addListener ( actionListener , " triPlanar " ) ;
2011-03-14 12:55:32 +00:00
inputManager . addMapping ( " WardIso " , new KeyTrigger ( KeyInput . KEY_9 ) ) ;
2011-06-12 02:35:19 +00:00
inputManager . addListener ( actionListener , " WardIso " ) ;
2011-03-14 12:55:32 +00:00
inputManager . addMapping ( " Minnaert " , new KeyTrigger ( KeyInput . KEY_0 ) ) ;
2011-06-12 02:35:19 +00:00
inputManager . addListener ( actionListener , " Minnaert " ) ;
}
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 ) ;
}
} else if ( name . equals ( " triPlanar " ) & & ! pressed ) {
2011-03-14 12:55:32 +00:00
triPlanar = ! triPlanar ;
if ( triPlanar ) {
matTerrain . setBoolean ( " useTriPlanarMapping " , true ) ;
// planar textures don't use the mesh's texture coordinates but real world coordinates,
// so we need to convert these texture coordinate scales into real world scales so it looks
// the same when we switch to/from tr-planar mode
2011-06-12 02:35:19 +00:00
matTerrain . setFloat ( " DiffuseMap_0_scale " , 1f / ( float ) ( 512f / grassScale ) ) ;
matTerrain . setFloat ( " DiffuseMap_1_scale " , 1f / ( float ) ( 512f / dirtScale ) ) ;
matTerrain . setFloat ( " DiffuseMap_2_scale " , 1f / ( float ) ( 512f / rockScale ) ) ;
2011-06-18 00:10:36 +00:00
matTerrain . setFloat ( " DiffuseMap_3_scale " , 1f / ( float ) ( 512f / rockScale ) ) ;
matTerrain . setFloat ( " DiffuseMap_4_scale " , 1f / ( float ) ( 512f / rockScale ) ) ;
2011-06-12 02:35:19 +00:00
} else {
2011-03-14 12:55:32 +00:00
matTerrain . setBoolean ( " useTriPlanarMapping " , false ) ;
matTerrain . setFloat ( " DiffuseMap_0_scale " , grassScale ) ;
matTerrain . setFloat ( " DiffuseMap_1_scale " , dirtScale ) ;
matTerrain . setFloat ( " DiffuseMap_2_scale " , rockScale ) ;
2011-06-18 00:10:36 +00:00
matTerrain . setFloat ( " DiffuseMap_3_scale " , rockScale ) ;
matTerrain . setFloat ( " DiffuseMap_4_scale " , rockScale ) ;
2011-03-14 12:55:32 +00:00
}
}
2011-06-12 02:35:19 +00:00
}
} ;
2011-03-14 12:55:32 +00:00
private void createSky ( ) {
Texture west = assetManager . loadTexture ( " Textures/Sky/Lagoon/lagoon_west.jpg " ) ;
Texture east = assetManager . loadTexture ( " Textures/Sky/Lagoon/lagoon_east.jpg " ) ;
Texture north = assetManager . loadTexture ( " Textures/Sky/Lagoon/lagoon_north.jpg " ) ;
Texture south = assetManager . loadTexture ( " Textures/Sky/Lagoon/lagoon_south.jpg " ) ;
Texture up = assetManager . loadTexture ( " Textures/Sky/Lagoon/lagoon_up.jpg " ) ;
Texture down = assetManager . loadTexture ( " Textures/Sky/Lagoon/lagoon_down.jpg " ) ;
Spatial sky = SkyFactory . createSky ( assetManager , west , east , north , south , up , down ) ;
rootNode . attachChild ( sky ) ;
}
}