diff options
5 files changed, 67 insertions, 31 deletions
diff --git a/trunk/ardor3d-examples/src/main/java/com/ardor3d/example/terrain/InMemoryTerrainExample.java b/trunk/ardor3d-examples/src/main/java/com/ardor3d/example/terrain/InMemoryTerrainExample.java index 1463e4c..4215b18 100644 --- a/trunk/ardor3d-examples/src/main/java/com/ardor3d/example/terrain/InMemoryTerrainExample.java +++ b/trunk/ardor3d-examples/src/main/java/com/ardor3d/example/terrain/InMemoryTerrainExample.java @@ -1 +1 @@ -/**
* Copyright (c) 2008-2012 Ardor Labs, Inc.
*
* This file is part of Ardor3D.
*
* Ardor3D is free software: you can redistribute it and/or modify it
* under the terms of its license which may be found in the accompanying
* LICENSE file or at <http://www.ardor3d.com/LICENSE>.
*/
package com.ardor3d.example.terrain;
import com.ardor3d.example.ExampleBase;
import com.ardor3d.example.Purpose;
import com.ardor3d.extension.terrain.client.Terrain;
import com.ardor3d.extension.terrain.client.TerrainBuilder;
import com.ardor3d.extension.terrain.client.TerrainDataProvider;
import com.ardor3d.extension.terrain.providers.inmemory.InMemoryTerrainDataProvider;
import com.ardor3d.extension.terrain.providers.inmemory.data.InMemoryTerrainData;
import com.ardor3d.framework.Canvas;
import com.ardor3d.image.Texture;
import com.ardor3d.input.Key;
import com.ardor3d.input.logical.InputTrigger;
import com.ardor3d.input.logical.KeyPressedCondition;
import com.ardor3d.input.logical.TriggerAction;
import com.ardor3d.input.logical.TwoInputStates;
import com.ardor3d.intersection.PickingUtil;
import com.ardor3d.intersection.PrimitivePickResults;
import com.ardor3d.light.DirectionalLight;
import com.ardor3d.math.ColorRGBA;
import com.ardor3d.math.Ray3;
import com.ardor3d.math.Vector3;
import com.ardor3d.renderer.Camera;
import com.ardor3d.renderer.queue.RenderBucketType;
import com.ardor3d.renderer.state.CullState;
import com.ardor3d.renderer.state.FogState;
import com.ardor3d.renderer.state.FogState.DensityFunction;
import com.ardor3d.scenegraph.Node;
import com.ardor3d.scenegraph.extension.Skybox;
import com.ardor3d.scenegraph.hint.CullHint;
import com.ardor3d.scenegraph.hint.LightCombineMode;
import com.ardor3d.scenegraph.shape.Sphere;
import com.ardor3d.ui.text.BasicText;
import com.ardor3d.util.ReadOnlyTimer;
import com.ardor3d.util.TextureManager;
/**
* Example showing the Geometry Clipmap Terrain system with 'MegaTextures' streaming from an in-memory data source.
* Requires GLSL support.
*/
@Purpose(htmlDescriptionKey = "com.ardor3d.example.terrain.InMemoryTerrainExample", //
thumbnailPath = "com/ardor3d/example/media/thumbnails/terrain_InMemoryTerrainExample.jpg", //
maxHeapMemory = 128)
public class InMemoryTerrainExample extends ExampleBase {
private boolean updateTerrain = true;
private final float farPlane = 8000.0f;
private Terrain terrain;
private final Sphere sphere = new Sphere("sp", 16, 16, 1);
private final Ray3 pickRay = new Ray3();
private boolean groundCamera = false;
private Camera terrainCamera;
private Skybox skybox;
private InMemoryTerrainData inMemoryTerrainData;
/** Text fields used to present info about the example. */
private final BasicText _exampleInfo[] = new BasicText[6];
public static void main(final String[] args) {
ExampleBase.start(InMemoryTerrainExample.class);
}
@Override
protected void updateExample(final ReadOnlyTimer timer) {
final Camera camera = _canvas.getCanvasRenderer().getCamera();
// Make sure camera is above terrain
final double height = terrain.getHeightAt(camera.getLocation().getX(), camera.getLocation().getZ());
if (height > -Float.MAX_VALUE && (groundCamera || camera.getLocation().getY() < height + 3)) {
camera.setLocation(new Vector3(camera.getLocation().getX(), height + 3, camera.getLocation().getZ()));
}
if (updateTerrain) {
terrainCamera.set(camera);
}
skybox.setTranslation(camera.getLocation());
// if we're picking...
if (sphere.getSceneHints().getCullHint() == CullHint.Dynamic) {
// Set up our pick ray
pickRay.setOrigin(camera.getLocation());
pickRay.setDirection(camera.getDirection());
// do pick and move the sphere
final PrimitivePickResults pickResults = new PrimitivePickResults();
pickResults.setCheckDistance(true);
PickingUtil.findPick(_root, pickRay, pickResults);
if (pickResults.getNumber() != 0) {
final Vector3 intersectionPoint = pickResults.getPickData(0).getIntersectionRecord()
.getIntersectionPoint(0);
sphere.setTranslation(intersectionPoint);
// XXX: maybe change the color of the ball for valid vs. invalid?
}
}
}
/**
* Initialize pssm pass and scene.
*/
@Override
protected void initExample() {
// Setup main camera.
_canvas.setTitle("Terrain Example");
_canvas.getCanvasRenderer().getCamera().setLocation(new Vector3(0, 300, 0));
_canvas.getCanvasRenderer().getCamera().lookAt(new Vector3(1, 300, 1), Vector3.UNIT_Y);
_canvas.getCanvasRenderer().getCamera().setFrustumPerspective(
70.0,
(float) _canvas.getCanvasRenderer().getCamera().getWidth()
/ _canvas.getCanvasRenderer().getCamera().getHeight(), 1.0f, farPlane);
_canvas.getCanvasRenderer().getRenderer().setBackgroundColor(ColorRGBA.GRAY);
_controlHandle.setMoveSpeed(200);
setupDefaultStates();
sphere.getSceneHints().setAllPickingHints(false);
sphere.getSceneHints().setCullHint(CullHint.Always);
_root.attachChild(sphere);
try {
// Keep a separate camera to be able to freeze terrain update
final Camera camera = _canvas.getCanvasRenderer().getCamera();
terrainCamera = new Camera(camera);
inMemoryTerrainData = new InMemoryTerrainData(2048, 9, 128, new Vector3(1, 200, 1));
final TerrainDataProvider terrainDataProvider = new InMemoryTerrainDataProvider(inMemoryTerrainData);
terrain = new TerrainBuilder(terrainDataProvider, terrainCamera).setShowDebugPanels(true).build();
_root.attachChild(terrain);
} catch (final Exception ex1) {
System.out.println("Problem setting up terrain...");
ex1.printStackTrace();
}
skybox = buildSkyBox();
skybox.getSceneHints().setAllPickingHints(false);
_root.attachChild(skybox);
// Setup labels for presenting example info.
final Node textNodes = new Node("Text");
_root.attachChild(textNodes);
textNodes.getSceneHints().setRenderBucketType(RenderBucketType.Ortho);
textNodes.getSceneHints().setLightCombineMode(LightCombineMode.Off);
final double infoStartY = _canvas.getCanvasRenderer().getCamera().getHeight() / 2;
for (int i = 0; i < _exampleInfo.length; i++) {
_exampleInfo[i] = BasicText.createDefaultTextLabel("Text", "", 16);
_exampleInfo[i].setTranslation(new Vector3(10, infoStartY - i * 20, 0));
textNodes.attachChild(_exampleInfo[i]);
}
textNodes.updateGeometricState(0.0);
updateText();
_logicalLayer.registerTrigger(new InputTrigger(new KeyPressedCondition(Key.V), new TriggerAction() {
public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) {
if (!inMemoryTerrainData.isRunning()) {
inMemoryTerrainData.startUpdates();
} else {
inMemoryTerrainData.stopUpdates();
}
updateText();
}
}));
_logicalLayer.registerTrigger(new InputTrigger(new KeyPressedCondition(Key.U), new TriggerAction() {
public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) {
updateTerrain = !updateTerrain;
updateText();
}
}));
_logicalLayer.registerTrigger(new InputTrigger(new KeyPressedCondition(Key.ONE), new TriggerAction() {
public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) {
_controlHandle.setMoveSpeed(5);
updateText();
}
}));
_logicalLayer.registerTrigger(new InputTrigger(new KeyPressedCondition(Key.TWO), new TriggerAction() {
public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) {
_controlHandle.setMoveSpeed(50);
updateText();
}
}));
_logicalLayer.registerTrigger(new InputTrigger(new KeyPressedCondition(Key.THREE), new TriggerAction() {
public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) {
_controlHandle.setMoveSpeed(400);
updateText();
}
}));
_logicalLayer.registerTrigger(new InputTrigger(new KeyPressedCondition(Key.FOUR), new TriggerAction() {
public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) {
_controlHandle.setMoveSpeed(1000);
updateText();
}
}));
_logicalLayer.registerTrigger(new InputTrigger(new KeyPressedCondition(Key.SPACE), new TriggerAction() {
public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) {
groundCamera = !groundCamera;
updateText();
}
}));
_logicalLayer.registerTrigger(new InputTrigger(new KeyPressedCondition(Key.P), new TriggerAction() {
public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) {
if (sphere.getSceneHints().getCullHint() == CullHint.Dynamic) {
sphere.getSceneHints().setCullHint(CullHint.Always);
} else if (sphere.getSceneHints().getCullHint() == CullHint.Always) {
sphere.getSceneHints().setCullHint(CullHint.Dynamic);
}
updateText();
}
}));
_logicalLayer.registerTrigger(new InputTrigger(new KeyPressedCondition(Key.R), new TriggerAction() {
public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) {
terrain.getTextureClipmap().setShowDebug(!terrain.getTextureClipmap().isShowDebug());
terrain.reloadShader();
updateText();
}
}));
_logicalLayer.registerTrigger(new InputTrigger(new KeyPressedCondition(Key.G), new TriggerAction() {
public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) {
terrain.reloadShader();
}
}));
_logicalLayer.registerTrigger(new InputTrigger(new KeyPressedCondition(Key.FIVE), new TriggerAction() {
public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) {
terrain.getTextureClipmap().setScale(terrain.getTextureClipmap().getScale() / 2);
terrain.reloadShader();
updateText();
}
}));
_logicalLayer.registerTrigger(new InputTrigger(new KeyPressedCondition(Key.SIX), new TriggerAction() {
public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) {
terrain.getTextureClipmap().setScale(terrain.getTextureClipmap().getScale() * 2);
terrain.reloadShader();
updateText();
}
}));
_logicalLayer.registerTrigger(new InputTrigger(new KeyPressedCondition(Key.SEVEN), new TriggerAction() {
public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) {
final Camera camera = _canvas.getCanvasRenderer().getCamera();
camera.setLocation(camera.getLocation().getX() + 500.0, camera.getLocation().getY(), camera
.getLocation().getZ());
}
}));
_logicalLayer.registerTrigger(new InputTrigger(new KeyPressedCondition(Key.EIGHT), new TriggerAction() {
public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) {
final Camera camera = _canvas.getCanvasRenderer().getCamera();
camera.setLocation(camera.getLocation().getX() - 500.0, camera.getLocation().getY(), camera
.getLocation().getZ());
}
}));
_logicalLayer.registerTrigger(new InputTrigger(new KeyPressedCondition(Key.NINE), new TriggerAction() {
public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) {
final Camera camera = _canvas.getCanvasRenderer().getCamera();
camera.setLocation(camera.getLocation().getX(), camera.getLocation().getY(), camera.getLocation()
.getZ() + 1500.0);
}
}));
_logicalLayer.registerTrigger(new InputTrigger(new KeyPressedCondition(Key.ZERO), new TriggerAction() {
public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) {
final Camera camera = _canvas.getCanvasRenderer().getCamera();
camera.setLocation(camera.getLocation().getX(), camera.getLocation().getY(), camera.getLocation()
.getZ() - 1500.0);
}
}));
}
private void setupDefaultStates() {
_lightState.detachAll();
final DirectionalLight dLight = new DirectionalLight();
dLight.setEnabled(true);
dLight.setAmbient(new ColorRGBA(0.4f, 0.4f, 0.5f, 1));
dLight.setDiffuse(new ColorRGBA(0.6f, 0.6f, 0.5f, 1));
dLight.setSpecular(new ColorRGBA(0.3f, 0.3f, 0.2f, 1));
dLight.setDirection(new Vector3(-1, -1, -1).normalizeLocal());
_lightState.attach(dLight);
_lightState.setEnabled(true);
final CullState cs = new CullState();
cs.setEnabled(true);
cs.setCullFace(CullState.Face.Back);
_root.setRenderState(cs);
final FogState fs = new FogState();
fs.setStart(farPlane / 2.0f);
fs.setEnd(farPlane);
fs.setColor(new ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f));
fs.setDensityFunction(DensityFunction.Linear);
_root.setRenderState(fs);
}
/**
* Builds the sky box.
*/
private Skybox buildSkyBox() {
final Skybox skybox = new Skybox("skybox", 10, 10, 10);
final String dir = "images/skybox/";
final Texture north = TextureManager
.load(dir + "1.jpg", Texture.MinificationFilter.BilinearNearestMipMap, true);
final Texture south = TextureManager
.load(dir + "3.jpg", Texture.MinificationFilter.BilinearNearestMipMap, true);
final Texture east = TextureManager.load(dir + "2.jpg", Texture.MinificationFilter.BilinearNearestMipMap, true);
final Texture west = TextureManager.load(dir + "4.jpg", Texture.MinificationFilter.BilinearNearestMipMap, true);
final Texture up = TextureManager.load(dir + "6.jpg", Texture.MinificationFilter.BilinearNearestMipMap, true);
final Texture down = TextureManager.load(dir + "5.jpg", Texture.MinificationFilter.BilinearNearestMipMap, true);
skybox.setTexture(Skybox.Face.North, north);
skybox.setTexture(Skybox.Face.West, west);
skybox.setTexture(Skybox.Face.South, south);
skybox.setTexture(Skybox.Face.East, east);
skybox.setTexture(Skybox.Face.Up, up);
skybox.setTexture(Skybox.Face.Down, down);
return skybox;
}
/**
* Update text information.
*/
private void updateText() {
_exampleInfo[0].setText("[1/2/3] Moving speed: " + _controlHandle.getMoveSpeed() * 3.6 + " km/h");
_exampleInfo[1].setText("[P] Do picking: " + (sphere.getSceneHints().getCullHint() == CullHint.Dynamic));
_exampleInfo[2].setText("[SPACE] Toggle fly/walk: " + (groundCamera ? "walk" : "fly"));
_exampleInfo[3].setText("[J] Regenerate heightmap/texture");
_exampleInfo[4].setText("[U] Freeze terrain(debug): " + !updateTerrain);
_exampleInfo[5].setText("[V] Updating terrain data: " + inMemoryTerrainData.isRunning());
}
}
\ No newline at end of file +/**
* Copyright (c) 2008-2012 Ardor Labs, Inc.
*
* This file is part of Ardor3D.
*
* Ardor3D is free software: you can redistribute it and/or modify it
* under the terms of its license which may be found in the accompanying
* LICENSE file or at <http://www.ardor3d.com/LICENSE>.
*/
package com.ardor3d.example.terrain;
import com.ardor3d.example.ExampleBase;
import com.ardor3d.example.Purpose;
import com.ardor3d.extension.terrain.client.Terrain;
import com.ardor3d.extension.terrain.client.TerrainBuilder;
import com.ardor3d.extension.terrain.client.TerrainDataProvider;
import com.ardor3d.extension.terrain.providers.inmemory.InMemoryTerrainDataProvider;
import com.ardor3d.extension.terrain.providers.inmemory.data.InMemoryTerrainData;
import com.ardor3d.framework.Canvas;
import com.ardor3d.image.Texture;
import com.ardor3d.input.Key;
import com.ardor3d.input.logical.InputTrigger;
import com.ardor3d.input.logical.KeyPressedCondition;
import com.ardor3d.input.logical.TriggerAction;
import com.ardor3d.input.logical.TwoInputStates;
import com.ardor3d.intersection.PickingUtil;
import com.ardor3d.intersection.PrimitivePickResults;
import com.ardor3d.light.DirectionalLight;
import com.ardor3d.math.ColorRGBA;
import com.ardor3d.math.Ray3;
import com.ardor3d.math.Vector3;
import com.ardor3d.renderer.Camera;
import com.ardor3d.renderer.queue.RenderBucketType;
import com.ardor3d.renderer.state.CullState;
import com.ardor3d.renderer.state.FogState;
import com.ardor3d.renderer.state.FogState.DensityFunction;
import com.ardor3d.scenegraph.Node;
import com.ardor3d.scenegraph.extension.Skybox;
import com.ardor3d.scenegraph.hint.CullHint;
import com.ardor3d.scenegraph.hint.LightCombineMode;
import com.ardor3d.scenegraph.shape.Sphere;
import com.ardor3d.ui.text.BasicText;
import com.ardor3d.util.ReadOnlyTimer;
import com.ardor3d.util.TextureManager;
/**
* Example showing the Geometry Clipmap Terrain system with 'MegaTextures' streaming from an in-memory data source.
* Requires GLSL support.
*/
@Purpose(htmlDescriptionKey = "com.ardor3d.example.terrain.InMemoryTerrainExample", //
thumbnailPath = "com/ardor3d/example/media/thumbnails/terrain_InMemoryTerrainExample.jpg", //
maxHeapMemory = 128)
public class InMemoryTerrainExample extends ExampleBase {
private boolean updateTerrain = true;
private final float farPlane = 8000.0f;
private Terrain terrain;
private final Sphere sphere = new Sphere("sp", 16, 16, 1);
private final Ray3 pickRay = new Ray3();
private boolean groundCamera = false;
private Camera terrainCamera;
private Skybox skybox;
private InMemoryTerrainData inMemoryTerrainData;
/** Text fields used to present info about the example. */
private final BasicText _exampleInfo[] = new BasicText[6];
public static void main(final String[] args) {
ExampleBase.start(InMemoryTerrainExample.class);
}
@Override
protected void updateExample(final ReadOnlyTimer timer) {
final Camera camera = _canvas.getCanvasRenderer().getCamera();
// Make sure camera is above terrain
final double height = terrain.getHeightAt(camera.getLocation().getX(), camera.getLocation().getZ());
if (height > -Float.MAX_VALUE && (groundCamera || camera.getLocation().getY() < height + 3)) {
camera.setLocation(new Vector3(camera.getLocation().getX(), height + 3, camera.getLocation().getZ()));
}
if (updateTerrain) {
terrainCamera.set(camera);
}
skybox.setTranslation(camera.getLocation());
// if we're picking...
if (sphere.getSceneHints().getCullHint() == CullHint.Dynamic) {
// Set up our pick ray
pickRay.setOrigin(camera.getLocation());
pickRay.setDirection(camera.getDirection());
// do pick and move the sphere
final PrimitivePickResults pickResults = new PrimitivePickResults();
pickResults.setCheckDistance(true);
PickingUtil.findPick(_root, pickRay, pickResults);
if (pickResults.getNumber() != 0) {
final Vector3 intersectionPoint = pickResults.getPickData(0).getIntersectionRecord()
.getIntersectionPoint(0);
sphere.setTranslation(intersectionPoint);
// XXX: maybe change the color of the ball for valid vs. invalid?
}
}
}
/**
* Initialize pssm pass and scene.
*/
@Override
protected void initExample() {
// Setup main camera.
_canvas.setTitle("Terrain Example");
_canvas.getCanvasRenderer().getCamera().setLocation(new Vector3(0, 300, 0));
_canvas.getCanvasRenderer().getCamera().lookAt(new Vector3(1, 300, 1), Vector3.UNIT_Y);
_canvas.getCanvasRenderer()
.getCamera()
.setFrustumPerspective(
70.0,
(float) _canvas.getCanvasRenderer().getCamera().getWidth()
/ _canvas.getCanvasRenderer().getCamera().getHeight(), 1.0f, farPlane);
_canvas.getCanvasRenderer().getRenderer().setBackgroundColor(ColorRGBA.GRAY);
_controlHandle.setMoveSpeed(200);
setupDefaultStates();
sphere.getSceneHints().setAllPickingHints(false);
sphere.getSceneHints().setCullHint(CullHint.Always);
_root.attachChild(sphere);
try {
// Keep a separate camera to be able to freeze terrain update
final Camera camera = _canvas.getCanvasRenderer().getCamera();
terrainCamera = new Camera(camera);
inMemoryTerrainData = new InMemoryTerrainData(2048, 9, 128, new Vector3(1, 200, 1));
final TerrainDataProvider terrainDataProvider = new InMemoryTerrainDataProvider(inMemoryTerrainData, true);
terrain = new TerrainBuilder(terrainDataProvider, terrainCamera).setShowDebugPanels(true).build();
_root.attachChild(terrain);
} catch (final Exception ex1) {
System.out.println("Problem setting up terrain...");
ex1.printStackTrace();
}
skybox = buildSkyBox();
skybox.getSceneHints().setAllPickingHints(false);
_root.attachChild(skybox);
// Setup labels for presenting example info.
final Node textNodes = new Node("Text");
_root.attachChild(textNodes);
textNodes.getSceneHints().setRenderBucketType(RenderBucketType.Ortho);
textNodes.getSceneHints().setLightCombineMode(LightCombineMode.Off);
final double infoStartY = _canvas.getCanvasRenderer().getCamera().getHeight() / 2;
for (int i = 0; i < _exampleInfo.length; i++) {
_exampleInfo[i] = BasicText.createDefaultTextLabel("Text", "", 16);
_exampleInfo[i].setTranslation(new Vector3(10, infoStartY - i * 20, 0));
textNodes.attachChild(_exampleInfo[i]);
}
textNodes.updateGeometricState(0.0);
updateText();
_logicalLayer.registerTrigger(new InputTrigger(new KeyPressedCondition(Key.V), new TriggerAction() {
public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) {
if (!inMemoryTerrainData.isRunning()) {
inMemoryTerrainData.startUpdates();
} else {
inMemoryTerrainData.stopUpdates();
}
updateText();
}
}));
_logicalLayer.registerTrigger(new InputTrigger(new KeyPressedCondition(Key.U), new TriggerAction() {
public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) {
updateTerrain = !updateTerrain;
updateText();
}
}));
_logicalLayer.registerTrigger(new InputTrigger(new KeyPressedCondition(Key.ONE), new TriggerAction() {
public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) {
_controlHandle.setMoveSpeed(5);
updateText();
}
}));
_logicalLayer.registerTrigger(new InputTrigger(new KeyPressedCondition(Key.TWO), new TriggerAction() {
public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) {
_controlHandle.setMoveSpeed(50);
updateText();
}
}));
_logicalLayer.registerTrigger(new InputTrigger(new KeyPressedCondition(Key.THREE), new TriggerAction() {
public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) {
_controlHandle.setMoveSpeed(400);
updateText();
}
}));
_logicalLayer.registerTrigger(new InputTrigger(new KeyPressedCondition(Key.FOUR), new TriggerAction() {
public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) {
_controlHandle.setMoveSpeed(1000);
updateText();
}
}));
_logicalLayer.registerTrigger(new InputTrigger(new KeyPressedCondition(Key.SPACE), new TriggerAction() {
public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) {
groundCamera = !groundCamera;
updateText();
}
}));
_logicalLayer.registerTrigger(new InputTrigger(new KeyPressedCondition(Key.P), new TriggerAction() {
public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) {
if (sphere.getSceneHints().getCullHint() == CullHint.Dynamic) {
sphere.getSceneHints().setCullHint(CullHint.Always);
} else if (sphere.getSceneHints().getCullHint() == CullHint.Always) {
sphere.getSceneHints().setCullHint(CullHint.Dynamic);
}
updateText();
}
}));
_logicalLayer.registerTrigger(new InputTrigger(new KeyPressedCondition(Key.R), new TriggerAction() {
public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) {
terrain.getTextureClipmap().setShowDebug(!terrain.getTextureClipmap().isShowDebug());
terrain.reloadShader();
updateText();
}
}));
_logicalLayer.registerTrigger(new InputTrigger(new KeyPressedCondition(Key.G), new TriggerAction() {
public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) {
terrain.reloadShader();
}
}));
_logicalLayer.registerTrigger(new InputTrigger(new KeyPressedCondition(Key.FIVE), new TriggerAction() {
public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) {
terrain.getTextureClipmap().setScale(terrain.getTextureClipmap().getScale() / 2);
terrain.reloadShader();
updateText();
}
}));
_logicalLayer.registerTrigger(new InputTrigger(new KeyPressedCondition(Key.SIX), new TriggerAction() {
public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) {
terrain.getTextureClipmap().setScale(terrain.getTextureClipmap().getScale() * 2);
terrain.reloadShader();
updateText();
}
}));
_logicalLayer.registerTrigger(new InputTrigger(new KeyPressedCondition(Key.SEVEN), new TriggerAction() {
public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) {
final Camera camera = _canvas.getCanvasRenderer().getCamera();
camera.setLocation(camera.getLocation().getX() + 500.0, camera.getLocation().getY(), camera
.getLocation().getZ());
}
}));
_logicalLayer.registerTrigger(new InputTrigger(new KeyPressedCondition(Key.EIGHT), new TriggerAction() {
public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) {
final Camera camera = _canvas.getCanvasRenderer().getCamera();
camera.setLocation(camera.getLocation().getX() - 500.0, camera.getLocation().getY(), camera
.getLocation().getZ());
}
}));
_logicalLayer.registerTrigger(new InputTrigger(new KeyPressedCondition(Key.NINE), new TriggerAction() {
public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) {
final Camera camera = _canvas.getCanvasRenderer().getCamera();
camera.setLocation(camera.getLocation().getX(), camera.getLocation().getY(), camera.getLocation()
.getZ() + 1500.0);
}
}));
_logicalLayer.registerTrigger(new InputTrigger(new KeyPressedCondition(Key.ZERO), new TriggerAction() {
public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) {
final Camera camera = _canvas.getCanvasRenderer().getCamera();
camera.setLocation(camera.getLocation().getX(), camera.getLocation().getY(), camera.getLocation()
.getZ() - 1500.0);
}
}));
}
private void setupDefaultStates() {
_lightState.detachAll();
final DirectionalLight dLight = new DirectionalLight();
dLight.setEnabled(true);
dLight.setAmbient(new ColorRGBA(0.4f, 0.4f, 0.5f, 1));
dLight.setDiffuse(new ColorRGBA(0.6f, 0.6f, 0.5f, 1));
dLight.setSpecular(new ColorRGBA(0.3f, 0.3f, 0.2f, 1));
dLight.setDirection(new Vector3(-1, -1, -1).normalizeLocal());
_lightState.attach(dLight);
_lightState.setEnabled(true);
final CullState cs = new CullState();
cs.setEnabled(true);
cs.setCullFace(CullState.Face.Back);
_root.setRenderState(cs);
final FogState fs = new FogState();
fs.setStart(farPlane / 2.0f);
fs.setEnd(farPlane);
fs.setColor(new ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f));
fs.setDensityFunction(DensityFunction.Linear);
_root.setRenderState(fs);
}
/**
* Builds the sky box.
*/
private Skybox buildSkyBox() {
final Skybox skybox = new Skybox("skybox", 10, 10, 10);
final String dir = "images/skybox/";
final Texture north = TextureManager
.load(dir + "1.jpg", Texture.MinificationFilter.BilinearNearestMipMap, true);
final Texture south = TextureManager
.load(dir + "3.jpg", Texture.MinificationFilter.BilinearNearestMipMap, true);
final Texture east = TextureManager.load(dir + "2.jpg", Texture.MinificationFilter.BilinearNearestMipMap, true);
final Texture west = TextureManager.load(dir + "4.jpg", Texture.MinificationFilter.BilinearNearestMipMap, true);
final Texture up = TextureManager.load(dir + "6.jpg", Texture.MinificationFilter.BilinearNearestMipMap, true);
final Texture down = TextureManager.load(dir + "5.jpg", Texture.MinificationFilter.BilinearNearestMipMap, true);
skybox.setTexture(Skybox.Face.North, north);
skybox.setTexture(Skybox.Face.West, west);
skybox.setTexture(Skybox.Face.South, south);
skybox.setTexture(Skybox.Face.East, east);
skybox.setTexture(Skybox.Face.Up, up);
skybox.setTexture(Skybox.Face.Down, down);
return skybox;
}
/**
* Update text information.
*/
private void updateText() {
_exampleInfo[0].setText("[1/2/3] Moving speed: " + _controlHandle.getMoveSpeed() * 3.6 + " km/h");
_exampleInfo[1].setText("[P] Do picking: " + (sphere.getSceneHints().getCullHint() == CullHint.Dynamic));
_exampleInfo[2].setText("[SPACE] Toggle fly/walk: " + (groundCamera ? "walk" : "fly"));
_exampleInfo[3].setText("[J] Regenerate heightmap/texture");
_exampleInfo[4].setText("[U] Freeze terrain(debug): " + !updateTerrain);
_exampleInfo[5].setText("[V] Updating terrain data: " + inMemoryTerrainData.isRunning());
}
}
\ No newline at end of file diff --git a/trunk/ardor3d-terrain/src/main/java/com/ardor3d/extension/terrain/providers/array/ArrayTerrainDataProvider.java b/trunk/ardor3d-terrain/src/main/java/com/ardor3d/extension/terrain/providers/array/ArrayTerrainDataProvider.java index 2664abd..3e3b6ae 100644 --- a/trunk/ardor3d-terrain/src/main/java/com/ardor3d/extension/terrain/providers/array/ArrayTerrainDataProvider.java +++ b/trunk/ardor3d-terrain/src/main/java/com/ardor3d/extension/terrain/providers/array/ArrayTerrainDataProvider.java @@ -19,10 +19,8 @@ import com.ardor3d.extension.terrain.client.TerrainSource; import com.ardor3d.extension.terrain.client.TextureSource;
import com.ardor3d.extension.terrain.providers.image.ImageTextureSource;
import com.ardor3d.extension.terrain.util.NormalMapUtil;
-import com.ardor3d.image.Texture;
-import com.ardor3d.image.Texture.MinificationFilter;
+import com.ardor3d.image.Image;
import com.ardor3d.math.type.ReadOnlyVector3;
-import com.ardor3d.util.TextureManager;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
@@ -97,9 +95,9 @@ public class ArrayTerrainDataProvider implements TerrainDataProvider { try {
final float[] data = heightMaps.get(heightMaps.size() - 1);
final int size = heightMapSizes.get(heightMapSizes.size() - 1);
- final Texture normal = TextureManager.loadFromImage(NormalMapUtil.constructNormalMap(data,
- scale.getX(), scale.getY(), getHeightMax(), size), MinificationFilter.BilinearNoMipMaps);
- return new ImageTextureSource(tileSize, normal.getImage(), heightMapSizes);
+ final Image normalImage = NormalMapUtil.constructNormalMap(data, size, scale.getY() / heightMax,
+ scale.getX(), scale.getZ());
+ return new ImageTextureSource(tileSize, normalImage, heightMapSizes);
} catch (final Exception e) {
e.printStackTrace();
}
diff --git a/trunk/ardor3d-terrain/src/main/java/com/ardor3d/extension/terrain/providers/inmemory/InMemoryTerrainDataProvider.java b/trunk/ardor3d-terrain/src/main/java/com/ardor3d/extension/terrain/providers/inmemory/InMemoryTerrainDataProvider.java index e8ae40e..e737243 100644 --- a/trunk/ardor3d-terrain/src/main/java/com/ardor3d/extension/terrain/providers/inmemory/InMemoryTerrainDataProvider.java +++ b/trunk/ardor3d-terrain/src/main/java/com/ardor3d/extension/terrain/providers/inmemory/InMemoryTerrainDataProvider.java @@ -19,9 +19,7 @@ import com.ardor3d.extension.terrain.client.TextureSource; import com.ardor3d.extension.terrain.providers.image.ImageTextureSource;
import com.ardor3d.extension.terrain.providers.inmemory.data.InMemoryTerrainData;
import com.ardor3d.extension.terrain.util.NormalMapUtil;
-import com.ardor3d.image.Texture;
-import com.ardor3d.image.Texture.MinificationFilter;
-import com.ardor3d.util.TextureManager;
+import com.ardor3d.image.Image;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
@@ -62,10 +60,10 @@ public class InMemoryTerrainDataProvider implements TerrainDataProvider { public TextureSource getNormalMapSource(final int mapId) {
if (generateNormalMap) {
try {
- final Texture normal = TextureManager.loadFromImage(NormalMapUtil.constructNormalMap(
- inMemoryTerrainData.getHeightData(), inMemoryTerrainData.getScale().getX(), inMemoryTerrainData
- .getScale().getY(), inMemoryTerrainData.getMaxHeight(), inMemoryTerrainData.getSide()),
- MinificationFilter.BilinearNoMipMaps);
+ final Image normalImage = NormalMapUtil.constructNormalMap(inMemoryTerrainData.getHeightData(),
+ inMemoryTerrainData.getSide(), inMemoryTerrainData.getMaxHeight(), inMemoryTerrainData
+ .getScale().getX(), inMemoryTerrainData.getScale().getY());
+
final List<Integer> heightMapSizes = Lists.newArrayList();
int currentSize = inMemoryTerrainData.getSide();
heightMapSizes.add(currentSize);
@@ -73,7 +71,7 @@ public class InMemoryTerrainDataProvider implements TerrainDataProvider { currentSize /= 2;
heightMapSizes.add(currentSize);
}
- return new ImageTextureSource(tileSize, normal.getImage(), heightMapSizes);
+ return new ImageTextureSource(tileSize, normalImage, heightMapSizes);
} catch (final Exception e) {
e.printStackTrace();
}
diff --git a/trunk/ardor3d-terrain/src/main/java/com/ardor3d/extension/terrain/providers/simplearray/SimpleArrayTerrainDataProvider.java b/trunk/ardor3d-terrain/src/main/java/com/ardor3d/extension/terrain/providers/simplearray/SimpleArrayTerrainDataProvider.java index 888838c..3d7878b 100644 --- a/trunk/ardor3d-terrain/src/main/java/com/ardor3d/extension/terrain/providers/simplearray/SimpleArrayTerrainDataProvider.java +++ b/trunk/ardor3d-terrain/src/main/java/com/ardor3d/extension/terrain/providers/simplearray/SimpleArrayTerrainDataProvider.java @@ -18,9 +18,7 @@ import com.ardor3d.extension.terrain.client.TerrainSource; import com.ardor3d.extension.terrain.client.TextureSource;
import com.ardor3d.extension.terrain.providers.image.ImageTextureSource;
import com.ardor3d.extension.terrain.util.NormalMapUtil;
-import com.ardor3d.image.Texture;
-import com.ardor3d.image.Texture.MinificationFilter;
-import com.ardor3d.util.TextureManager;
+import com.ardor3d.image.Image;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
@@ -67,8 +65,7 @@ public class SimpleArrayTerrainDataProvider implements TerrainDataProvider { public TextureSource getNormalMapSource(final int mapId) {
if (generateNormalMap) {
try {
- final Texture normal = TextureManager.loadFromImage(NormalMapUtil.constructNormalMap(heightData, 1, 1,
- 1, side), MinificationFilter.BilinearNoMipMaps);
+ final Image normalImage = NormalMapUtil.constructNormalMap(heightData, side, 1, 1, 1);
final List<Integer> heightMapSizes = Lists.newArrayList();
int currentSize = side;
heightMapSizes.add(currentSize);
@@ -76,7 +73,7 @@ public class SimpleArrayTerrainDataProvider implements TerrainDataProvider { currentSize /= 2;
heightMapSizes.add(currentSize);
}
- return new ImageTextureSource(tileSize, normal.getImage(), heightMapSizes);
+ return new ImageTextureSource(tileSize, normalImage, heightMapSizes);
} catch (final Exception e) {
e.printStackTrace();
}
diff --git a/trunk/ardor3d-terrain/src/main/java/com/ardor3d/extension/terrain/util/NormalMapUtil.java b/trunk/ardor3d-terrain/src/main/java/com/ardor3d/extension/terrain/util/NormalMapUtil.java index 53df22d..89e012b 100644 --- a/trunk/ardor3d-terrain/src/main/java/com/ardor3d/extension/terrain/util/NormalMapUtil.java +++ b/trunk/ardor3d-terrain/src/main/java/com/ardor3d/extension/terrain/util/NormalMapUtil.java @@ -19,24 +19,67 @@ import com.ardor3d.math.Vector3; public class NormalMapUtil { - public static Image constructNormalMap(final float[] heightmap, final double spacing, final double zScale, - final double mapScale, final int side) { + /** + * Generate an image from the given terrain height data to be used as a source for terrain normal maps. + * + * @param heightmap + * the base height data. Generally this is the most detailed height data available. It must be a square + * heightmap, with a side of "side" as passed below. + * @param side + * the number of samples on a side of the heightmap. This could be calculated by taking the squareroot of + * heightmap.length, but generally this number is well known by the caller. + * @param heightScale + * the scaling factor applied to the heightMap values to get real world height. + * @param xGridSpacing + * real world spacing between grid in the x direction + * @param zGridSpacing + * real world spacing between grid in the z direction + * @return the normal image. + */ + public static Image constructNormalMap(final float[] heightmap, final int side, final double heightScale, + final double xGridSpacing, final double zGridSpacing) { int x, z; final Vector3 n = new Vector3(); + final Vector3 n2 = new Vector3(); final ByteBuffer data = ByteBuffer.allocateDirect(side * side * 3); final Image normalMap = new Image(ImageDataFormat.RGB, PixelDataType.UnsignedByte, side, side, data, null); for (z = 0; z < side; ++z) { for (x = 0; x < side; ++x) { - n.setZ(1); if (x == 0 || z == 0 || x == side - 1 || z == side - 1) { - n.setX(0); - n.setY(0); + n.set(0, 0, 1); } else { - n.setX(zScale * (heightmap[z * side + x - 1] - heightmap[z * side + x + 1]) / 2 - / (mapScale * spacing)); - n.setY(zScale * (heightmap[(z - 1) * side + x] - heightmap[(z + 1) * side + x]) / 2 - / (mapScale * spacing)); - n.normalizeLocal(); + // change across "x" from point to our "left" to point on our "right" + double dXh = heightmap[z * side + x - 1] - heightmap[z * side + x + 1]; + if (dXh != 0) { + // alter by our height scale + dXh *= heightScale; + // determine slope of perpendicular line + final double slopeX = 2.0 * xGridSpacing / dXh; + // now plug into cos(arctan(x)) to get unit length vector + n.setX(Math.copySign(1.0 / Math.sqrt(1 + slopeX * slopeX), dXh)); + n.setY(0); + n.setZ(Math.abs(slopeX * n.getX())); + } else { + n.set(0, 0, 1); + } + + // change across "z" from point "above" us to point "below" us + double dZh = heightmap[(z - 1) * side + x] - heightmap[(z + 1) * side + x]; + if (dZh != 0) { + // alter by our height scale + dZh *= heightScale; + // determine slope of perpendicular line + final double slopeZ = 2.0 * zGridSpacing / dZh; + // now plug into cos(arctan(x)) to get unit length vector + n2.setX(0); + n2.setY(Math.copySign(1.0 / Math.sqrt(1 + slopeZ * slopeZ), dZh)); + n2.setZ(Math.abs(slopeZ * n2.getY())); + } else { + n2.set(0, 0, 1); + } + + // add together the vectors across X and Z and normalize to get final normal + n.addLocal(n2).normalizeLocal(); } // System.err.println(n); data.put(3 * (z * side + x) + 0, (byte) ((int) (127 * n.getX()) + 128)); |