2 * Copyright (c) 2003-2009 jMonkeyEngine
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 package jmetest.terrain;
35 import java.nio.FloatBuffer;
37 import jmetest.effects.water.TestQuadWater;
39 import com.jme.app.SimplePassGame;
40 import com.jme.image.Texture;
41 import com.jme.math.Plane;
42 import com.jme.math.Vector3f;
43 import com.jme.renderer.ColorRGBA;
44 import com.jme.renderer.pass.RenderPass;
45 import com.jme.scene.PassNode;
46 import com.jme.scene.PassNodeState;
47 import com.jme.scene.Skybox;
48 import com.jme.scene.Spatial;
49 import com.jme.scene.Spatial.TextureCombineMode;
50 import com.jme.scene.shape.Quad;
51 import com.jme.scene.state.BlendState;
52 import com.jme.scene.state.CullState;
53 import com.jme.scene.state.FogState;
54 import com.jme.scene.state.TextureState;
55 import com.jme.scene.state.ZBufferState;
56 import com.jme.util.TextureManager;
57 import com.jmex.effects.water.WaterRenderPass;
58 import com.jmex.terrain.TerrainPage;
59 import com.jmex.terrain.util.RawHeightMap;
62 * TestIsland shows multipass texturesplatting(6 passes) through usage
63 * of the PassNode together with jME's water effect and a skybox. A simpler
64 * version of the terrain without splatting is created and used for rendering
65 * into the reflection/refraction of the water.
67 * @author Heightmap and textures originally from Jadestone(but heavily
69 * @author Rikard Herlitz (MrCoder)
72 public class TestIsland extends SimplePassGame {
74 private WaterRenderPass waterEffectRenderPass;
75 private Quad waterQuad;
76 private Spatial splatTerrain;
77 private Spatial reflectionTerrain;
78 private Skybox skybox;
80 private float farPlane = 10000.0f;
81 private float textureScale = 0.07f;
82 private float globalSplatScale = 90.0f;
84 public static void main(String[] args) {
85 TestIsland app = new TestIsland();
86 app.setConfigShowMode(ConfigShowMode.AlwaysShow);
90 protected void simpleUpdate() {
91 skybox.getLocalTranslation().set(cam.getLocation());
92 skybox.updateGeometricState(0.0f, true);
94 Vector3f transVec = new Vector3f(cam.getLocation().x,
95 waterEffectRenderPass.getWaterHeight(), cam.getLocation().z);
96 setTextureCoords(0, transVec.x, -transVec.z, textureScale);
97 setVertexCoords(transVec.x, transVec.y, transVec.z);
100 protected void simpleInitGame() {
101 display.setTitle("Test Island");
106 createReflectionTerrain();
110 rootNode.attachChild(skybox);
111 rootNode.attachChild(splatTerrain);
113 waterEffectRenderPass = new WaterRenderPass(cam, 6, false, true);
114 waterEffectRenderPass.setWaterPlane(new Plane(new Vector3f(0.0f, 1.0f,
116 waterEffectRenderPass.setClipBias(-1.0f);
117 waterEffectRenderPass.setReflectionThrottle(0.0f);
118 waterEffectRenderPass.setRefractionThrottle(0.0f);
120 waterQuad = new Quad("waterQuad", 1, 1);
121 FloatBuffer normBuf = waterQuad.getNormalBuffer();
123 normBuf.put(0).put(1).put(0);
124 normBuf.put(0).put(1).put(0);
125 normBuf.put(0).put(1).put(0);
126 normBuf.put(0).put(1).put(0);
128 waterEffectRenderPass.setWaterEffectOnSpatial(waterQuad);
129 rootNode.attachChild(waterQuad);
131 waterEffectRenderPass.setReflectedScene(skybox);
132 waterEffectRenderPass.addReflectedScene(reflectionTerrain);
133 waterEffectRenderPass.setSkybox(skybox);
134 pManager.add(waterEffectRenderPass);
136 RenderPass rootPass = new RenderPass();
137 rootPass.add(rootNode);
138 pManager.add(rootPass);
140 // BloomRenderPass bloomRenderPass = new BloomRenderPass(cam, 4);
141 // if (!bloomRenderPass.isSupported()) {
142 // Text t = new Text("Text", "GLSL Not supported on this computer.");
143 // t.setRenderQueueMode(Renderer.QUEUE_ORTHO);
144 // t.setLightCombineMode(Spatial.LightCombineMode.Off);
145 // t.setLocalTranslation(new Vector3f(0, 20, 0));
146 // fpsNode.attachChild(t);
148 // bloomRenderPass.setExposurePow(2.0f);
149 // bloomRenderPass.setBlurIntensityMultiplier(0.5f);
151 // bloomRenderPass.add(rootNode);
152 // bloomRenderPass.setUseCurrentScene(true);
153 // pManager.add(bloomRenderPass);
156 RenderPass statPass = new RenderPass();
157 statPass.add(statNode);
158 pManager.add(statPass);
160 rootNode.setCullHint(Spatial.CullHint.Never);
161 rootNode.setCullHint(Spatial.CullHint.Never);
164 private void createTerrain() {
165 RawHeightMap heightMap = new RawHeightMap(TestIsland.class
166 .getClassLoader().getResource(
167 "jmetest/data/texture/terrain/heights.raw"),
168 129, RawHeightMap.FORMAT_16BITLE, false);
170 Vector3f terrainScale = new Vector3f(5, 0.003f, 6);
171 heightMap.setHeightScale(0.001f);
172 TerrainPage page = new TerrainPage("Terrain", 33, heightMap.getSize(),
173 terrainScale, heightMap.getHeightMap());
174 page.getLocalTranslation().set(0, -9.5f, 0);
175 page.setDetailTexture(1, 1);
177 // create some interesting texturestates for splatting
178 TextureState ts1 = createSplatTextureState(
179 "jmetest/data/texture/terrain/baserock.jpg", null);
181 TextureState ts2 = createSplatTextureState(
182 "jmetest/data/texture/terrain/darkrock.jpg",
183 "jmetest/data/texture/terrain/darkrockalpha.png");
185 TextureState ts3 = createSplatTextureState(
186 "jmetest/data/texture/terrain/deadgrass.jpg",
187 "jmetest/data/texture/terrain/deadalpha.png");
189 TextureState ts4 = createSplatTextureState(
190 "jmetest/data/texture/terrain/nicegrass.jpg",
191 "jmetest/data/texture/terrain/grassalpha.png");
193 TextureState ts5 = createSplatTextureState(
194 "jmetest/data/texture/terrain/road.jpg",
195 "jmetest/data/texture/terrain/roadalpha.png");
197 TextureState ts6 = createLightmapTextureState("jmetest/data/texture/terrain/lightmap.jpg");
199 // alpha used for blending the passnodestates together
200 BlendState as = display.getRenderer().createBlendState();
201 as.setBlendEnabled(true);
202 as.setSourceFunction(BlendState.SourceFunction.SourceAlpha);
203 as.setDestinationFunction(BlendState.DestinationFunction.OneMinusSourceAlpha);
204 as.setTestEnabled(true);
205 as.setTestFunction(BlendState.TestFunction.GreaterThan);
208 // alpha used for blending the lightmap
209 BlendState as2 = display.getRenderer().createBlendState();
210 as2.setBlendEnabled(true);
211 as2.setSourceFunction(BlendState.SourceFunction.DestinationColor);
212 as2.setDestinationFunction(BlendState.DestinationFunction.SourceColor);
213 as2.setTestEnabled(true);
214 as2.setTestFunction(BlendState.TestFunction.GreaterThan);
215 as2.setEnabled(true);
217 // //////////////////// PASS STUFF START
218 // try out a passnode to use for splatting
219 PassNode splattingPassNode = new PassNode("SplatPassNode");
220 splattingPassNode.attachChild(page);
222 PassNodeState passNodeState = new PassNodeState();
223 passNodeState.setPassState(ts1);
224 splattingPassNode.addPass(passNodeState);
226 passNodeState = new PassNodeState();
227 passNodeState.setPassState(ts2);
228 passNodeState.setPassState(as);
229 splattingPassNode.addPass(passNodeState);
231 passNodeState = new PassNodeState();
232 passNodeState.setPassState(ts3);
233 passNodeState.setPassState(as);
234 splattingPassNode.addPass(passNodeState);
236 passNodeState = new PassNodeState();
237 passNodeState.setPassState(ts4);
238 passNodeState.setPassState(as);
239 splattingPassNode.addPass(passNodeState);
241 passNodeState = new PassNodeState();
242 passNodeState.setPassState(ts5);
243 passNodeState.setPassState(as);
244 splattingPassNode.addPass(passNodeState);
246 passNodeState = new PassNodeState();
247 passNodeState.setPassState(ts6);
248 passNodeState.setPassState(as2);
249 splattingPassNode.addPass(passNodeState);
250 // //////////////////// PASS STUFF END
252 // lock some things to increase the performance
253 splattingPassNode.lockBounds();
254 splattingPassNode.lockTransforms();
255 splattingPassNode.lockShadows();
257 splatTerrain = splattingPassNode;
258 splatTerrain.setCullHint(Spatial.CullHint.Dynamic);
262 private void createReflectionTerrain() {
263 RawHeightMap heightMap = new RawHeightMap(TestIsland.class
264 .getClassLoader().getResource(
265 "jmetest/data/texture/terrain/heights.raw"),
266 129, RawHeightMap.FORMAT_16BITLE, false);
268 Vector3f terrainScale = new Vector3f(5, 0.003f, 6);
269 heightMap.setHeightScale(0.001f);
270 TerrainPage page = new TerrainPage("Terrain", 33, heightMap.getSize(),
271 terrainScale, heightMap.getHeightMap());
272 page.getLocalTranslation().set(0, -9.5f, 0);
273 page.setDetailTexture(1, 1);
275 // create some interesting texturestates for splatting
276 TextureState ts1 = display.getRenderer().createTextureState();
277 Texture t0 = TextureManager.loadTexture(TestIsland.class
278 .getClassLoader().getResource(
279 "jmetest/data/texture/terrain/terrainlod.jpg"),
280 Texture.MinificationFilter.Trilinear,
281 Texture.MagnificationFilter.Bilinear);
282 t0.setWrap(Texture.WrapMode.Repeat);
283 t0.setApply(Texture.ApplyMode.Modulate);
284 t0.setScale(new Vector3f(1.0f, 1.0f, 1.0f));
285 ts1.setTexture(t0, 0);
287 // //////////////////// PASS STUFF START
288 // try out a passnode to use for splatting
289 PassNode splattingPassNode = new PassNode("SplatPassNode");
290 splattingPassNode.attachChild(page);
292 PassNodeState passNodeState = new PassNodeState();
293 passNodeState.setPassState(ts1);
294 splattingPassNode.addPass(passNodeState);
295 // //////////////////// PASS STUFF END
297 // lock some things to increase the performance
298 splattingPassNode.lockBounds();
299 splattingPassNode.lockTransforms();
300 splattingPassNode.lockShadows();
302 reflectionTerrain = splattingPassNode;
304 initSpatial(reflectionTerrain);
307 private void setupEnvironment() {
308 cam.setFrustumPerspective(45.0f, (float) display.getWidth()
309 / (float) display.getHeight(), 1f, farPlane);
310 cam.setLocation(new Vector3f(-320, 80, -270));
311 cam.lookAt(new Vector3f(0, 0, 0), Vector3f.UNIT_Y);
314 CullState cs = display.getRenderer().createCullState();
315 cs.setCullFace(CullState.Face.Back);
316 rootNode.setRenderState(cs);
318 lightState.detachAll();
319 rootNode.setLightCombineMode(Spatial.LightCombineMode.Off);
321 FogState fogState = display.getRenderer().createFogState();
322 fogState.setDensity(1.0f);
323 fogState.setEnabled(true);
324 fogState.setColor(new ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f));
325 fogState.setEnd(farPlane);
326 fogState.setStart(farPlane / 10.0f);
327 fogState.setDensityFunction(FogState.DensityFunction.Linear);
328 fogState.setQuality(FogState.Quality.PerVertex);
329 rootNode.setRenderState(fogState);
332 private void addAlphaSplat(TextureState ts, String alpha) {
333 Texture t1 = TextureManager.loadTexture(TestIsland.class
334 .getClassLoader().getResource(alpha),
335 Texture.MinificationFilter.Trilinear,
336 Texture.MagnificationFilter.Bilinear);
337 t1.setWrap(Texture.WrapMode.Repeat);
338 t1.setApply(Texture.ApplyMode.Combine);
339 t1.setCombineFuncRGB(Texture.CombinerFunctionRGB.Replace);
340 t1.setCombineSrc0RGB(Texture.CombinerSource.Previous);
341 t1.setCombineOp0RGB(Texture.CombinerOperandRGB.SourceColor);
342 t1.setCombineFuncAlpha(Texture.CombinerFunctionAlpha.Replace);
343 ts.setTexture(t1, ts.getNumberOfSetTextures());
346 private TextureState createSplatTextureState(String texture, String alpha) {
347 TextureState ts = display.getRenderer().createTextureState();
349 Texture t0 = TextureManager.loadTexture(TestIsland.class
350 .getClassLoader().getResource(texture),
351 Texture.MinificationFilter.Trilinear,
352 Texture.MagnificationFilter.Bilinear);
353 t0.setWrap(Texture.WrapMode.Repeat);
354 t0.setApply(Texture.ApplyMode.Modulate);
355 t0.setScale(new Vector3f(globalSplatScale, globalSplatScale, 1.0f));
356 ts.setTexture(t0, 0);
359 addAlphaSplat(ts, alpha);
365 private TextureState createLightmapTextureState(String texture) {
366 TextureState ts = display.getRenderer().createTextureState();
368 Texture t0 = TextureManager.loadTexture(TestIsland.class
369 .getClassLoader().getResource(texture),
370 Texture.MinificationFilter.Trilinear,
371 Texture.MagnificationFilter.Bilinear);
372 t0.setWrap(Texture.WrapMode.Repeat);
373 ts.setTexture(t0, 0);
378 private void buildSkyBox() {
379 skybox = new Skybox("skybox", 10, 10, 10);
381 String dir = "jmetest/data/skybox1/";
382 Texture north = TextureManager.loadTexture(TestQuadWater.class
383 .getClassLoader().getResource(dir + "1.jpg"),
384 Texture.MinificationFilter.BilinearNearestMipMap,
385 Texture.MagnificationFilter.Bilinear);
386 Texture south = TextureManager.loadTexture(TestQuadWater.class
387 .getClassLoader().getResource(dir + "3.jpg"),
388 Texture.MinificationFilter.BilinearNearestMipMap,
389 Texture.MagnificationFilter.Bilinear);
390 Texture east = TextureManager.loadTexture(TestQuadWater.class
391 .getClassLoader().getResource(dir + "2.jpg"),
392 Texture.MinificationFilter.BilinearNearestMipMap,
393 Texture.MagnificationFilter.Bilinear);
394 Texture west = TextureManager.loadTexture(TestQuadWater.class
395 .getClassLoader().getResource(dir + "4.jpg"),
396 Texture.MinificationFilter.BilinearNearestMipMap,
397 Texture.MagnificationFilter.Bilinear);
398 Texture up = TextureManager.loadTexture(TestQuadWater.class
399 .getClassLoader().getResource(dir + "6.jpg"),
400 Texture.MinificationFilter.BilinearNearestMipMap,
401 Texture.MagnificationFilter.Bilinear);
402 Texture down = TextureManager.loadTexture(TestQuadWater.class
403 .getClassLoader().getResource(dir + "5.jpg"),
404 Texture.MinificationFilter.BilinearNearestMipMap,
405 Texture.MagnificationFilter.Bilinear);
407 skybox.setTexture(Skybox.Face.North, north);
408 skybox.setTexture(Skybox.Face.West, west);
409 skybox.setTexture(Skybox.Face.South, south);
410 skybox.setTexture(Skybox.Face.East, east);
411 skybox.setTexture(Skybox.Face.Up, up);
412 skybox.setTexture(Skybox.Face.Down, down);
413 skybox.preloadTextures();
415 CullState cullState = display.getRenderer().createCullState();
416 cullState.setCullFace(CullState.Face.None);
417 cullState.setEnabled(true);
418 skybox.setRenderState(cullState);
420 ZBufferState zState = display.getRenderer().createZBufferState();
421 zState.setEnabled(false);
422 skybox.setRenderState(zState);
424 FogState fs = display.getRenderer().createFogState();
425 fs.setEnabled(false);
426 skybox.setRenderState(fs);
428 skybox.setLightCombineMode(Spatial.LightCombineMode.Off);
429 skybox.setCullHint(Spatial.CullHint.Never);
430 skybox.setTextureCombineMode(TextureCombineMode.Replace);
431 skybox.updateRenderState();
437 private void setVertexCoords(float x, float y, float z) {
438 FloatBuffer vertBuf = waterQuad.getVertexBuffer();
441 vertBuf.put(x - farPlane).put(y).put(z - farPlane);
442 vertBuf.put(x - farPlane).put(y).put(z + farPlane);
443 vertBuf.put(x + farPlane).put(y).put(z + farPlane);
444 vertBuf.put(x + farPlane).put(y).put(z - farPlane);
447 private void setTextureCoords(int buffer, float x, float y,
448 float textureScale) {
449 x *= textureScale * 0.5f;
450 y *= textureScale * 0.5f;
451 textureScale = farPlane * textureScale;
453 texBuf = waterQuad.getTextureCoords(buffer).coords;
455 texBuf.put(x).put(textureScale + y);
456 texBuf.put(x).put(y);
457 texBuf.put(textureScale + x).put(y);
458 texBuf.put(textureScale + x).put(textureScale + y);
461 private void initSpatial(Spatial spatial) {
462 ZBufferState buf = display.getRenderer().createZBufferState();
463 buf.setEnabled(true);
464 buf.setFunction(ZBufferState.TestFunction.LessThanOrEqualTo);
465 spatial.setRenderState(buf);
467 CullState cs = display.getRenderer().createCullState();
468 cs.setCullFace(CullState.Face.Back);
469 spatial.setRenderState(cs);
471 spatial.setCullHint(Spatial.CullHint.Never);
473 spatial.updateGeometricState(0.0f, true);
474 spatial.updateRenderState();