Added my own noise library as jar (noise-0.0.1-SNAPSHOT.jar), hope I didn't break any build stuff
TerrainGridTest is running smooth now using 257x257 sized TerrainQuads with fractal based heightmaps calculated on the fly. git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@7529 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
This commit is contained in:
parent
1fbd05a3ca
commit
58ede837c6
@ -54,4 +54,6 @@ libs.swing-layout.javadoc=\
|
|||||||
${base}/swing-layout/swing-layout-1.0.4-doc.zip
|
${base}/swing-layout/swing-layout-1.0.4-doc.zip
|
||||||
libs.swing-layout.src=\
|
libs.swing-layout.src=\
|
||||||
${base}/swing-layout/swing-layout-1.0.4-src.zip
|
${base}/swing-layout/swing-layout-1.0.4-src.zip
|
||||||
|
libs.noise.classpath=\
|
||||||
|
${base}/noise/noise-0.0.1-SNAPSHOT.jar
|
||||||
|
|
||||||
|
BIN
engine/lib/noise/noise-0.0.1-SNAPSHOT.jar
Normal file
BIN
engine/lib/noise/noise-0.0.1-SNAPSHOT.jar
Normal file
Binary file not shown.
@ -1,112 +1,113 @@
|
|||||||
annotation.processing.enabled=false
|
annotation.processing.enabled=false
|
||||||
annotation.processing.enabled.in.editor=false
|
annotation.processing.enabled.in.editor=false
|
||||||
annotation.processing.run.all.processors=true
|
annotation.processing.run.all.processors=true
|
||||||
ant.customtasks.libs=JWSAntTasks
|
ant.customtasks.libs=JWSAntTasks
|
||||||
application.homepage=http://www.jmonkeyengine.com/
|
application.homepage=http://www.jmonkeyengine.com/
|
||||||
application.title=jMonkeyEngine 3.0
|
application.title=jMonkeyEngine 3.0
|
||||||
application.vendor=jMonkeyEngine
|
application.vendor=jMonkeyEngine
|
||||||
build.classes.dir=${build.dir}/classes
|
build.classes.dir=${build.dir}/classes
|
||||||
build.classes.excludes=**/*.java,**/*.form
|
build.classes.excludes=**/*.java,**/*.form
|
||||||
# This directory is removed when the project is cleaned:
|
# This directory is removed when the project is cleaned:
|
||||||
build.dir=build
|
build.dir=build
|
||||||
build.generated.dir=${build.dir}/generated
|
build.generated.dir=${build.dir}/generated
|
||||||
build.generated.sources.dir=${build.dir}/generated-sources
|
build.generated.sources.dir=${build.dir}/generated-sources
|
||||||
# Only compile against the classpath explicitly listed here:
|
# Only compile against the classpath explicitly listed here:
|
||||||
build.sysclasspath=ignore
|
build.sysclasspath=ignore
|
||||||
build.test.classes.dir=${build.dir}/test/classes
|
build.test.classes.dir=${build.dir}/test/classes
|
||||||
build.test.results.dir=${build.dir}/test/results
|
build.test.results.dir=${build.dir}/test/results
|
||||||
# Uncomment to specify the preferred debugger connection transport:
|
# Uncomment to specify the preferred debugger connection transport:
|
||||||
#debug.transport=dt_socket
|
#debug.transport=dt_socket
|
||||||
debug.classpath=\
|
debug.classpath=\
|
||||||
${run.classpath}
|
${run.classpath}
|
||||||
debug.test.classpath=\
|
debug.test.classpath=\
|
||||||
${run.test.classpath}
|
${run.test.classpath}
|
||||||
# This directory is removed when the project is cleaned:
|
# This directory is removed when the project is cleaned:
|
||||||
dist.dir=dist
|
dist.dir=dist
|
||||||
dist.jar=${dist.dir}/jMonkeyEngine3.jar
|
dist.jar=${dist.dir}/jMonkeyEngine3.jar
|
||||||
dist.javadoc.dir=${dist.dir}/javadoc
|
dist.javadoc.dir=${dist.dir}/javadoc
|
||||||
endorsed.classpath=
|
endorsed.classpath=
|
||||||
excludes=
|
excludes=
|
||||||
file.reference.src-test-data=src/test-data
|
file.reference.src-test-data=src/test-data
|
||||||
includes=**
|
includes=**
|
||||||
jar.archive.disabled=${jnlp.enabled}
|
jar.archive.disabled=${jnlp.enabled}
|
||||||
jar.compress=true
|
jar.compress=true
|
||||||
jar.index=${jnlp.enabled}
|
jar.index=${jnlp.enabled}
|
||||||
javac.classpath=\
|
javac.classpath=\
|
||||||
${libs.jogg.classpath}:\
|
${libs.jogg.classpath}:\
|
||||||
${libs.jbullet.classpath}:\
|
${libs.jbullet.classpath}:\
|
||||||
${libs.bullet.classpath}:\
|
${libs.bullet.classpath}:\
|
||||||
${libs.lwjgl.classpath}:\
|
${libs.lwjgl.classpath}:\
|
||||||
${libs.jheora.classpath}:\
|
${libs.jheora.classpath}:\
|
||||||
${libs.niftygui1.3.classpath}:\
|
${libs.niftygui1.3.classpath}:\
|
||||||
${libs.jme3-test-data.classpath}
|
${libs.jme3-test-data.classpath}:\
|
||||||
# Space-separated list of extra javac options
|
${libs.noise.classpath}
|
||||||
javac.compilerargs=
|
# Space-separated list of extra javac options
|
||||||
javac.deprecation=false
|
javac.compilerargs=
|
||||||
javac.processorpath=\
|
javac.deprecation=false
|
||||||
${javac.classpath}
|
javac.processorpath=\
|
||||||
javac.source=1.5
|
${javac.classpath}
|
||||||
javac.target=1.5
|
javac.source=1.5
|
||||||
javac.test.classpath=\
|
javac.target=1.5
|
||||||
${javac.classpath}:\
|
javac.test.classpath=\
|
||||||
${build.classes.dir}:\
|
${javac.classpath}:\
|
||||||
${libs.junit_4.classpath}
|
${build.classes.dir}:\
|
||||||
javadoc.additionalparam=
|
${libs.junit_4.classpath}
|
||||||
javadoc.author=false
|
javadoc.additionalparam=
|
||||||
javadoc.encoding=${source.encoding}
|
javadoc.author=false
|
||||||
javadoc.noindex=false
|
javadoc.encoding=${source.encoding}
|
||||||
javadoc.nonavbar=false
|
javadoc.noindex=false
|
||||||
javadoc.notree=false
|
javadoc.nonavbar=false
|
||||||
javadoc.private=false
|
javadoc.notree=false
|
||||||
javadoc.splitindex=true
|
javadoc.private=false
|
||||||
javadoc.use=true
|
javadoc.splitindex=true
|
||||||
javadoc.version=false
|
javadoc.use=true
|
||||||
javadoc.windowtitle=jMonkeyEngine3
|
javadoc.version=false
|
||||||
jaxbwiz.endorsed.dirs="${netbeans.home}/../ide12/modules/ext/jaxb/api"
|
javadoc.windowtitle=jMonkeyEngine3
|
||||||
jnlp.applet.class=jme3test.awt.AppHarness
|
jaxbwiz.endorsed.dirs="${netbeans.home}/../ide12/modules/ext/jaxb/api"
|
||||||
jnlp.applet.height=300
|
jnlp.applet.class=jme3test.awt.AppHarness
|
||||||
jnlp.applet.width=300
|
jnlp.applet.height=300
|
||||||
jnlp.codebase.type=user
|
jnlp.applet.width=300
|
||||||
jnlp.codebase.user=http://jmonkeyengine.com/javawebstart/
|
jnlp.codebase.type=user
|
||||||
jnlp.descriptor=application
|
jnlp.codebase.user=http://jmonkeyengine.com/javawebstart/
|
||||||
jnlp.enabled=false
|
jnlp.descriptor=application
|
||||||
jnlp.icon=/Users/normenhansen/Pictures/jme/icons/jme-logo48.png
|
jnlp.enabled=false
|
||||||
jnlp.mixed.code=default
|
jnlp.icon=/Users/normenhansen/Pictures/jme/icons/jme-logo48.png
|
||||||
jnlp.offline-allowed=true
|
jnlp.mixed.code=default
|
||||||
jnlp.signed=true
|
jnlp.offline-allowed=true
|
||||||
jnlp.signing=generated
|
jnlp.signed=true
|
||||||
jnlp.signing.alias=
|
jnlp.signing=generated
|
||||||
jnlp.signing.keystore=
|
jnlp.signing.alias=
|
||||||
main.class=jme3test.TestChooser
|
jnlp.signing.keystore=
|
||||||
manifest.file=MANIFEST.MF
|
main.class=jme3test.TestChooser
|
||||||
meta.inf.dir=${src.dir}/META-INF
|
manifest.file=MANIFEST.MF
|
||||||
mkdist.disabled=false
|
meta.inf.dir=${src.dir}/META-INF
|
||||||
platform.active=default_platform
|
mkdist.disabled=false
|
||||||
run.classpath=\
|
platform.active=default_platform
|
||||||
${javac.classpath}:\
|
run.classpath=\
|
||||||
${build.classes.dir}
|
${javac.classpath}:\
|
||||||
run.jvmargs=-Xms40m -Xmx40m -XX:MaxDirectMemorySize=256M
|
${build.classes.dir}
|
||||||
run.test.classpath=\
|
run.jvmargs=-Xms40m -Xmx40m -XX:MaxDirectMemorySize=256M
|
||||||
${javac.test.classpath}:\
|
run.test.classpath=\
|
||||||
${build.test.classes.dir}
|
${javac.test.classpath}:\
|
||||||
source.encoding=UTF-8
|
${build.test.classes.dir}
|
||||||
src.core-data.dir=src/core-data
|
source.encoding=UTF-8
|
||||||
src.core-plugins.dir=src/core-plugins
|
src.core-data.dir=src/core-data
|
||||||
src.core.dir=src/core
|
src.core-plugins.dir=src/core-plugins
|
||||||
src.desktop-fx.dir=src/desktop-fx
|
src.core.dir=src/core
|
||||||
src.desktop.dir=src/desktop
|
src.desktop-fx.dir=src/desktop-fx
|
||||||
src.games.dir=src/games
|
src.desktop.dir=src/desktop
|
||||||
src.jbullet.dir=src/jbullet
|
src.games.dir=src/games
|
||||||
src.jheora.dir=src/jheora
|
src.jbullet.dir=src/jbullet
|
||||||
src.jogg.dir=src/jogg
|
src.jheora.dir=src/jheora
|
||||||
src.lwjgl-oal.dir=src/lwjgl-oal
|
src.jogg.dir=src/jogg
|
||||||
src.lwjgl-ogl.dir=src/lwjgl-ogl
|
src.lwjgl-oal.dir=src/lwjgl-oal
|
||||||
src.networking.dir=src\\networking
|
src.lwjgl-ogl.dir=src/lwjgl-ogl
|
||||||
src.niftygui.dir=src/niftygui
|
src.networking.dir=src\\networking
|
||||||
src.ogre.dir=src/ogre
|
src.niftygui.dir=src/niftygui
|
||||||
src.pack.dir=src/pack
|
src.ogre.dir=src/ogre
|
||||||
src.terrain.dir=src/terrain
|
src.pack.dir=src/pack
|
||||||
src.test.dir=src/test
|
src.terrain.dir=src/terrain
|
||||||
src.tools.dir=src/tools
|
src.test.dir=src/test
|
||||||
src.xml.dir=src/xml
|
src.tools.dir=src/tools
|
||||||
test.test.dir=test
|
src.xml.dir=src/xml
|
||||||
|
test.test.dir=test
|
||||||
|
62
engine/src/terrain/com/jme3/terrain/MapUtils.java
Normal file
62
engine/src/terrain/com/jme3/terrain/MapUtils.java
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
package com.jme3.terrain;
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.FloatBuffer;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
|
import org.novyon.noise.ShaderUtils;
|
||||||
|
|
||||||
|
public class MapUtils {
|
||||||
|
|
||||||
|
public static FloatBuffer clip(FloatBuffer src, int origSize, int newSize, int offset) {
|
||||||
|
FloatBuffer result = FloatBuffer.allocate(newSize * newSize);
|
||||||
|
|
||||||
|
float[] orig = src.array();
|
||||||
|
for (int i = offset; i < offset + newSize; i++) {
|
||||||
|
result.put(orig, i * origSize + offset, newSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BufferedImage toGrayscale16Image(FloatBuffer buff, int size) {
|
||||||
|
BufferedImage retval = new BufferedImage(size, size, BufferedImage.TYPE_USHORT_GRAY);
|
||||||
|
buff.rewind();
|
||||||
|
for (int y = 0; y < size; y++) {
|
||||||
|
for (int x = 0; x < size; x++) {
|
||||||
|
short c = (short) (ShaderUtils.clamp(buff.get(), 0, 1) * 65532);
|
||||||
|
retval.getRaster().setDataElements(x, y, new short[] { c });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BufferedImage toGrayscaleRGBImage(FloatBuffer buff, int size) {
|
||||||
|
BufferedImage retval = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);
|
||||||
|
buff.rewind();
|
||||||
|
for (int y = 0; y < size; y++) {
|
||||||
|
for (int x = 0; x < size; x++) {
|
||||||
|
int c = (int) (ShaderUtils.clamp(buff.get(), 0, 1) * 255);
|
||||||
|
retval.setRGB(x, y, 0xFF000000 | c << 16 | c << 8 | c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void saveImage(BufferedImage im, String file) {
|
||||||
|
MapUtils.saveImage(im, new File(file));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void saveImage(BufferedImage im, File file) {
|
||||||
|
try {
|
||||||
|
ImageIO.write(im, "PNG", file);
|
||||||
|
Logger.getLogger(MapUtils.class.getCanonicalName()).info("Saved image as : " + file.getAbsolutePath());
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
124
engine/src/terrain/com/jme3/terrain/geomipmap/LRUCache.java
Normal file
124
engine/src/terrain/com/jme3/terrain/geomipmap/LRUCache.java
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
package com.jme3.terrain.geomipmap;
|
||||||
|
|
||||||
|
// Copyright 2007 Christian d'Heureuse, Inventec Informatik AG, Zurich,
|
||||||
|
// Switzerland
|
||||||
|
// www.source-code.biz, www.inventec.ch/chdh
|
||||||
|
//
|
||||||
|
// This module is multi-licensed and may be used under the terms
|
||||||
|
// of any of the following licenses:
|
||||||
|
//
|
||||||
|
// EPL, Eclipse Public License, V1.0 or later, http://www.eclipse.org/legal
|
||||||
|
// LGPL, GNU Lesser General Public License, V2 or later,
|
||||||
|
// http://www.gnu.org/licenses/lgpl.html
|
||||||
|
// GPL, GNU General Public License, V2 or later,
|
||||||
|
// http://www.gnu.org/licenses/gpl.html
|
||||||
|
// AL, Apache License, V2.0 or later, http://www.apache.org/licenses
|
||||||
|
// BSD, BSD License, http://www.opensource.org/licenses/bsd-license.php
|
||||||
|
//
|
||||||
|
// Please contact the author if you need another license.
|
||||||
|
// This module is provided "as is", without warranties of any kind.
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An LRU cache, based on <code>LinkedHashMap</code>.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This cache has a fixed maximum number of elements (<code>cacheSize</code>).
|
||||||
|
* If the cache is full and another entry is added, the LRU (least recently
|
||||||
|
* used) entry is dropped.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This class is thread-safe. All methods of this class are synchronized.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Author: Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland<br>
|
||||||
|
* Multi-licensed: EPL / LGPL / GPL / AL / BSD.
|
||||||
|
*/
|
||||||
|
public class LRUCache<K, V> {
|
||||||
|
|
||||||
|
private static final float hashTableLoadFactor = 0.75f;
|
||||||
|
|
||||||
|
private LinkedHashMap<K, V> map;
|
||||||
|
private int cacheSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new LRU cache.
|
||||||
|
*
|
||||||
|
* @param cacheSize
|
||||||
|
* the maximum number of entries that will be kept in this cache.
|
||||||
|
*/
|
||||||
|
public LRUCache(int cacheSize) {
|
||||||
|
this.cacheSize = cacheSize;
|
||||||
|
int hashTableCapacity = (int) Math.ceil(cacheSize / LRUCache.hashTableLoadFactor) + 1;
|
||||||
|
this.map = new LinkedHashMap<K, V>(hashTableCapacity, LRUCache.hashTableLoadFactor, true) {
|
||||||
|
// (an anonymous inner class)
|
||||||
|
private static final long serialVersionUID = 1;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
|
||||||
|
return this.size() > LRUCache.this.cacheSize;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves an entry from the cache.<br>
|
||||||
|
* The retrieved entry becomes the MRU (most recently used) entry.
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* the key whose associated value is to be returned.
|
||||||
|
* @return the value associated to this key, or null if no value with this
|
||||||
|
* key exists in the cache.
|
||||||
|
*/
|
||||||
|
public synchronized V get(K key) {
|
||||||
|
return this.map.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an entry to this cache.
|
||||||
|
* The new entry becomes the MRU (most recently used) entry.
|
||||||
|
* If an entry with the specified key already exists in the cache, it is
|
||||||
|
* replaced by the new entry.
|
||||||
|
* If the cache is full, the LRU (least recently used) entry is removed from
|
||||||
|
* the cache.
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* the key with which the specified value is to be associated.
|
||||||
|
* @param value
|
||||||
|
* a value to be associated with the specified key.
|
||||||
|
*/
|
||||||
|
public synchronized void put(K key, V value) {
|
||||||
|
this.map.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the cache.
|
||||||
|
*/
|
||||||
|
public synchronized void clear() {
|
||||||
|
this.map.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of used entries in the cache.
|
||||||
|
*
|
||||||
|
* @return the number of entries currently in the cache.
|
||||||
|
*/
|
||||||
|
public synchronized int usedEntries() {
|
||||||
|
return this.map.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a <code>Collection</code> that contains a copy of all cache
|
||||||
|
* entries.
|
||||||
|
*
|
||||||
|
* @return a <code>Collection</code> with a copy of the cache content.
|
||||||
|
*/
|
||||||
|
public synchronized Collection<Map.Entry<K, V>> getAll() {
|
||||||
|
return new ArrayList<Map.Entry<K, V>>(this.map.entrySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end class LRUCache
|
@ -11,6 +11,8 @@ import com.jme3.terrain.heightmap.HeightMap;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import com.jme3.material.Material;
|
import com.jme3.material.Material;
|
||||||
@ -35,6 +37,37 @@ public class TerrainGrid extends TerrainQuad {
|
|||||||
private Vector3f[] quadIndex;
|
private Vector3f[] quadIndex;
|
||||||
private Map<String, TerrainGridListener> listeners = new HashMap<String, TerrainGridListener>();
|
private Map<String, TerrainGridListener> listeners = new HashMap<String, TerrainGridListener>();
|
||||||
private Material material;
|
private Material material;
|
||||||
|
private LRUCache<Vector3f, TerrainQuad> cache = new LRUCache<Vector3f, TerrainQuad>(16);
|
||||||
|
|
||||||
|
private class UpdateQuadCache implements Runnable {
|
||||||
|
|
||||||
|
private final Vector3f location;
|
||||||
|
private final boolean centerOnly;
|
||||||
|
|
||||||
|
public UpdateQuadCache(Vector3f location) {
|
||||||
|
this.location = location;
|
||||||
|
this.centerOnly = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UpdateQuadCache(Vector3f location, boolean centerOnly) {
|
||||||
|
this.location = location;
|
||||||
|
this.centerOnly = centerOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
for (int i = centerOnly ? 1 : 0; i < (centerOnly ? 3 : 4); i++) {
|
||||||
|
for (int j = centerOnly ? 1 : 0; j < (centerOnly ? 3 : 4); j++) {
|
||||||
|
Vector3f temp = location.add(quadIndex[i * 4 + j]);
|
||||||
|
if (cache.get(temp) == null) {
|
||||||
|
HeightMap heightMapAt = heightMapGrid.getHeightMapAt(temp);
|
||||||
|
TerrainQuad q = new TerrainQuad(getName() + "Quad" + temp, patchSize, quadSize, heightMapAt == null ? null : heightMapAt.getHeightMap(), lodCalculatorFactory);
|
||||||
|
cache.put(temp, q);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public TerrainGrid(String name, int patchSize, int size, Vector3f stepScale, HeightMapGrid heightMapGrid, int totalSize,
|
public TerrainGrid(String name, int patchSize, int size, Vector3f stepScale, HeightMapGrid heightMapGrid, int totalSize,
|
||||||
Vector2f offset, float offsetAmount, LodCalculatorFactory lodCalculatorFactory) {
|
Vector2f offset, float offsetAmount, LodCalculatorFactory lodCalculatorFactory) {
|
||||||
@ -57,7 +90,11 @@ public class TerrainGrid extends TerrainQuad {
|
|||||||
new Vector3f(-this.quarterSize, 0, this.quarterSize).mult(this.stepScale),
|
new Vector3f(-this.quarterSize, 0, this.quarterSize).mult(this.stepScale),
|
||||||
new Vector3f(this.quarterSize, 0, -this.quarterSize).mult(this.stepScale),
|
new Vector3f(this.quarterSize, 0, -this.quarterSize).mult(this.stepScale),
|
||||||
new Vector3f(this.quarterSize, 0, this.quarterSize).mult(this.stepScale)};
|
new Vector3f(this.quarterSize, 0, this.quarterSize).mult(this.stepScale)};
|
||||||
this.quadIndex = new Vector3f[]{new Vector3f(0, 0, 0), new Vector3f(0, 0, 1), new Vector3f(1, 0, 0), new Vector3f(1, 0, 1)};
|
this.quadIndex = new Vector3f[]{
|
||||||
|
new Vector3f(-1, 0, -1), new Vector3f(-1, 0, 0), new Vector3f(-1, 0, 1), new Vector3f(-1, 0, 2),
|
||||||
|
new Vector3f(0, 0, -1), new Vector3f(0, 0, 0), new Vector3f(0, 0, 1), new Vector3f(0, 0, 2),
|
||||||
|
new Vector3f(1, 0, -1), new Vector3f(1, 0, 0), new Vector3f(1, 0, 1), new Vector3f(1, 0, 2),
|
||||||
|
new Vector3f(2, 0, -1), new Vector3f(2, 0, 0), new Vector3f(2, 0, 1), new Vector3f(2, 0, 2)};
|
||||||
|
|
||||||
updateChildrens(Vector3f.ZERO);
|
updateChildrens(Vector3f.ZERO);
|
||||||
}
|
}
|
||||||
@ -93,7 +130,7 @@ public class TerrainGrid extends TerrainQuad {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//super.update(locations);
|
super.update(locations);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vector3f getCell(Vector3f location) {
|
public Vector3f getCell(Vector3f location) {
|
||||||
@ -102,7 +139,9 @@ public class TerrainGrid extends TerrainQuad {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void removeQuad(int idx) {
|
protected void removeQuad(int idx) {
|
||||||
this.detachChild(this.getQuad(idx));
|
if (this.getQuad(idx) != null) {
|
||||||
|
this.detachChild(this.getQuad(idx));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void moveQuad(int from, int to) {
|
protected void moveQuad(int from, int to) {
|
||||||
@ -112,16 +151,66 @@ public class TerrainGrid extends TerrainQuad {
|
|||||||
fq.setLocalTranslation(this.quadOrigins[to - 1]);
|
fq.setLocalTranslation(this.quadOrigins[to - 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected TerrainQuad createQuadAt(Vector3f location, int quadrant) {
|
protected void attachQuadAt(TerrainQuad q, int quadrant) {
|
||||||
final HeightMap heightMapAt = this.heightMapGrid.getHeightMapAt(location);
|
|
||||||
TerrainQuad q = new TerrainQuad(this.getName() + "Quad" + location, this.patchSize, this.quadSize, heightMapAt == null ? null : heightMapAt.getHeightMap(), this.lodCalculatorFactory);
|
|
||||||
q.setLocalTranslation(this.quadOrigins[quadrant - 1]);
|
|
||||||
q.setMaterial(this.material);
|
q.setMaterial(this.material);
|
||||||
|
q.setLocalTranslation(quadOrigins[quadrant - 1]);
|
||||||
q.setQuadrant((short) quadrant);
|
q.setQuadrant((short) quadrant);
|
||||||
return q;
|
this.attachChild(q);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateChildrens(Vector3f cam) {
|
private void updateChildrens(Vector3f cam) {
|
||||||
|
TerrainQuad q1 = cache.get(cam.add(quadIndex[5]));
|
||||||
|
TerrainQuad q2 = cache.get(cam.add(quadIndex[6]));
|
||||||
|
TerrainQuad q3 = cache.get(cam.add(quadIndex[9]));
|
||||||
|
TerrainQuad q4 = cache.get(cam.add(quadIndex[10]));
|
||||||
|
|
||||||
|
int dx = 0;
|
||||||
|
int dy = 0;
|
||||||
|
if (currentCell != null) {
|
||||||
|
dx = (int) (cam.x - currentCell.x);
|
||||||
|
dy = (int) (cam.z - currentCell.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
int kxm = 0;
|
||||||
|
int kxM = 4;
|
||||||
|
int kym = 0;
|
||||||
|
int kyM = 4;
|
||||||
|
if (dx == -1) {
|
||||||
|
kxM = 3;
|
||||||
|
} else if (dx == 1) {
|
||||||
|
kxm = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dy == -1) {
|
||||||
|
kyM = 3;
|
||||||
|
} else if (dy == 1) {
|
||||||
|
kym = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i=kym; i<kyM; i++) {
|
||||||
|
for (int j = kxm; j < kxM; j++) {
|
||||||
|
cache.get(cam.add(quadIndex[i * 4 + j]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (q1 == null || q2 == null || q3 == null || q4 == null) {
|
||||||
|
try {
|
||||||
|
executor.submit(new UpdateQuadCache(cam, true)).get();
|
||||||
|
q1 = cache.get(cam.add(quadIndex[5]));
|
||||||
|
q2 = cache.get(cam.add(quadIndex[6]));
|
||||||
|
q3 = cache.get(cam.add(quadIndex[9]));
|
||||||
|
q4 = cache.get(cam.add(quadIndex[10]));
|
||||||
|
} catch (InterruptedException ex) {
|
||||||
|
Logger.getLogger(TerrainGrid.class.getName()).log(Level.SEVERE, null, ex);
|
||||||
|
return;
|
||||||
|
} catch (ExecutionException ex) {
|
||||||
|
Logger.getLogger(TerrainGrid.class.getName()).log(Level.SEVERE, null, ex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
executor.execute(new UpdateQuadCache(cam));
|
||||||
|
|
||||||
RigidBodyControl control = getControl(RigidBodyControl.class);
|
RigidBodyControl control = getControl(RigidBodyControl.class);
|
||||||
PhysicsSpace space = null;
|
PhysicsSpace space = null;
|
||||||
if (control != null) {
|
if (control != null) {
|
||||||
@ -129,59 +218,16 @@ public class TerrainGrid extends TerrainQuad {
|
|||||||
space.remove(this);
|
space.remove(this);
|
||||||
this.removeControl(control);
|
this.removeControl(control);
|
||||||
}
|
}
|
||||||
int dx = (int) cam.x;
|
|
||||||
int dz = (int) cam.z;
|
this.removeQuad(1);
|
||||||
if (this.currentCell != null) {
|
this.removeQuad(2);
|
||||||
dx -= (int) (this.currentCell.x);
|
this.removeQuad(3);
|
||||||
dz -= (int) (this.currentCell.z);
|
this.removeQuad(4);
|
||||||
}
|
attachQuadAt(q1, 1);
|
||||||
if (this.currentCell == null || FastMath.abs(dx) > 1 || FastMath.abs(dz) > 1 || (dx != 0 && dz != 0)) {
|
attachQuadAt(q2, 2);
|
||||||
if (this.currentCell != null) {
|
attachQuadAt(q3, 3);
|
||||||
// in case of teleport, otherwise the FastMath.abs(delta) should
|
attachQuadAt(q4, 4);
|
||||||
// never be greater than 1
|
|
||||||
this.removeQuad(1);
|
|
||||||
this.removeQuad(2);
|
|
||||||
this.removeQuad(3);
|
|
||||||
this.removeQuad(4);
|
|
||||||
}
|
|
||||||
this.attachChild(this.createQuadAt(cam.add(this.quadIndex[0]).mult(this.quadSize - 1), 1));
|
|
||||||
this.attachChild(this.createQuadAt(cam.add(this.quadIndex[1]).mult(this.quadSize - 1), 2));
|
|
||||||
this.attachChild(this.createQuadAt(cam.add(this.quadIndex[2]).mult(this.quadSize - 1), 3));
|
|
||||||
this.attachChild(this.createQuadAt(cam.add(this.quadIndex[3]).mult(this.quadSize - 1), 4));
|
|
||||||
} else if (dx == 0) {
|
|
||||||
if (dz < 0) {
|
|
||||||
// move north
|
|
||||||
this.moveQuad(1, 2);
|
|
||||||
this.moveQuad(3, 4);
|
|
||||||
this.attachChild(this.createQuadAt(cam.add(this.quadIndex[0]).mult(this.quadSize - 1), 1));
|
|
||||||
this.attachChild(this.createQuadAt(cam.add(this.quadIndex[2]).mult(this.quadSize - 1), 3));
|
|
||||||
} else {
|
|
||||||
// move south
|
|
||||||
this.moveQuad(2, 1);
|
|
||||||
this.moveQuad(4, 3);
|
|
||||||
this.attachChild(this.createQuadAt(cam.add(this.quadIndex[1]).mult(this.quadSize - 1), 2));
|
|
||||||
this.attachChild(this.createQuadAt(cam.add(this.quadIndex[3]).mult(this.quadSize - 1), 4));
|
|
||||||
}
|
|
||||||
} else if (dz == 0) {
|
|
||||||
if (dx < 0) {
|
|
||||||
// move west
|
|
||||||
this.moveQuad(1, 3);
|
|
||||||
this.moveQuad(2, 4);
|
|
||||||
this.attachChild(this.createQuadAt(cam.add(this.quadIndex[0]).mult(this.quadSize - 1), 1));
|
|
||||||
this.attachChild(this.createQuadAt(cam.add(this.quadIndex[1]).mult(this.quadSize - 1), 2));
|
|
||||||
} else {
|
|
||||||
// move east
|
|
||||||
this.moveQuad(3, 1);
|
|
||||||
this.moveQuad(4, 2);
|
|
||||||
this.attachChild(this.createQuadAt(cam.add(this.quadIndex[2]).mult(this.quadSize - 1), 3));
|
|
||||||
this.attachChild(this.createQuadAt(cam.add(this.quadIndex[3]).mult(this.quadSize - 1), 4));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// rare situation to enter into a diagonally placed cell
|
|
||||||
// could not get into this part while testing, as it is handled by moving first
|
|
||||||
// in either horizontally or vertically than the other way
|
|
||||||
// I handle it in the first IF
|
|
||||||
}
|
|
||||||
this.currentCell = cam;
|
this.currentCell = cam;
|
||||||
this.setLocalTranslation(cam.mult(2 * this.quadSize));
|
this.setLocalTranslation(cam.mult(2 * this.quadSize));
|
||||||
|
|
||||||
|
@ -29,7 +29,6 @@
|
|||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.jme3.terrain.geomipmap;
|
package com.jme3.terrain.geomipmap;
|
||||||
|
|
||||||
import com.jme3.export.InputCapsule;
|
import com.jme3.export.InputCapsule;
|
||||||
@ -57,7 +56,7 @@ import java.util.ArrayList;
|
|||||||
* NOTE: right now it just uses the first camera passed in,
|
* NOTE: right now it just uses the first camera passed in,
|
||||||
* in the future it will use all of them to determine what
|
* in the future it will use all of them to determine what
|
||||||
* LOD to set.
|
* LOD to set.
|
||||||
*
|
*
|
||||||
* @author Brent Owens
|
* @author Brent Owens
|
||||||
*/
|
*/
|
||||||
public class TerrainLodControl extends AbstractControl {
|
public class TerrainLodControl extends AbstractControl {
|
||||||
@ -68,8 +67,8 @@ public class TerrainLodControl extends AbstractControl {
|
|||||||
|
|
||||||
public TerrainLodControl() {
|
public TerrainLodControl() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Only uses the first camera right now.
|
* Only uses the first camera right now.
|
||||||
* @param terrain to act upon (must be a Spatial)
|
* @param terrain to act upon (must be a Spatial)
|
||||||
* @param cameras one or more cameras to reference for LOD calc
|
* @param cameras one or more cameras to reference for LOD calc
|
||||||
@ -80,52 +79,55 @@ public class TerrainLodControl extends AbstractControl {
|
|||||||
}
|
}
|
||||||
this.cameras = cameras;
|
this.cameras = cameras;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void controlRender(RenderManager rm, ViewPort vp) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void controlUpdate(float tpf) {
|
protected void controlRender(RenderManager rm, ViewPort vp) {
|
||||||
//list of cameras for when terrain supports multiple cameras (ie split screen)
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void controlUpdate(float tpf) {
|
||||||
|
//list of cameras for when terrain supports multiple cameras (ie split screen)
|
||||||
|
|
||||||
if (cameras != null) {
|
if (cameras != null) {
|
||||||
if (cameraLocations.isEmpty() && !cameras.isEmpty()) {
|
if (cameraLocations.isEmpty() && !cameras.isEmpty()) {
|
||||||
for (Camera c : cameras) // populate them
|
for (Camera c : cameras) // populate them
|
||||||
|
{
|
||||||
cameraLocations.add(c.getLocation());
|
cameraLocations.add(c.getLocation());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
terrain.update(cameraLocations);
|
terrain.update(cameraLocations);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Control cloneForSpatial(Spatial spatial) {
|
public Control cloneForSpatial(Spatial spatial) {
|
||||||
if (spatial instanceof Terrain) {
|
if (spatial instanceof Terrain) {
|
||||||
List<Camera> cameraClone = new ArrayList<Camera>();
|
List<Camera> cameraClone = new ArrayList<Camera>();
|
||||||
if (cameras != null) {
|
if (cameras != null) {
|
||||||
for (Camera c : cameras)
|
for (Camera c : cameras) {
|
||||||
cameraClone.add(c);
|
cameraClone.add(c);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return new TerrainLodControl((TerrainQuad)spatial, cameraClone);
|
return new TerrainLodControl((TerrainQuad) spatial, cameraClone);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void setCameras(List<Camera> cameras) {
|
public void setCameras(List<Camera> cameras) {
|
||||||
this.cameras = cameras;
|
this.cameras = cameras;
|
||||||
cameraLocations.clear();
|
cameraLocations.clear();
|
||||||
for (Camera c : cameras)
|
for (Camera c : cameras) {
|
||||||
cameraLocations.add(c.getLocation());
|
cameraLocations.add(c.getLocation());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setSpatial(Spatial spatial) {
|
public void setSpatial(Spatial spatial) {
|
||||||
super.setSpatial(spatial);
|
super.setSpatial(spatial);
|
||||||
if (spatial instanceof TerrainQuad)
|
if (spatial instanceof TerrainQuad) {
|
||||||
this.terrain = (TerrainQuad)spatial;
|
this.terrain = (TerrainQuad) spatial;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTerrain(TerrainQuad terrain) {
|
public void setTerrain(TerrainQuad terrain) {
|
||||||
this.terrain = terrain;
|
this.terrain = terrain;
|
||||||
}
|
}
|
||||||
|
@ -73,9 +73,9 @@ import java.util.logging.Logger;
|
|||||||
* A terrain quad is a node in the quad tree of the terrain system.
|
* A terrain quad is a node in the quad tree of the terrain system.
|
||||||
* The root terrain quad will be the only one that receives the update() call every frame
|
* The root terrain quad will be the only one that receives the update() call every frame
|
||||||
* and it will determine if there has been any LOD change.
|
* and it will determine if there has been any LOD change.
|
||||||
*
|
*
|
||||||
* The leaves of the terrain quad tree are Terrain Patches. These have the real geometry mesh.
|
* The leaves of the terrain quad tree are Terrain Patches. These have the real geometry mesh.
|
||||||
*
|
*
|
||||||
* @author Brent Owens
|
* @author Brent Owens
|
||||||
*/
|
*/
|
||||||
public class TerrainQuad extends Node implements Terrain {
|
public class TerrainQuad extends Node implements Terrain {
|
||||||
@ -93,7 +93,7 @@ public class TerrainQuad extends Node implements Terrain {
|
|||||||
protected float offsetAmount;
|
protected float offsetAmount;
|
||||||
|
|
||||||
protected int quadrant = 1; // 1=upper left, 2=lower left, 3=upper right, 4=lower right
|
protected int quadrant = 1; // 1=upper left, 2=lower left, 3=upper right, 4=lower right
|
||||||
|
|
||||||
protected LodCalculatorFactory lodCalculatorFactory;
|
protected LodCalculatorFactory lodCalculatorFactory;
|
||||||
|
|
||||||
|
|
||||||
@ -107,20 +107,20 @@ public class TerrainQuad extends Node implements Terrain {
|
|||||||
|
|
||||||
private TerrainPicker picker;
|
private TerrainPicker picker;
|
||||||
|
|
||||||
|
|
||||||
private ExecutorService executor = Executors.newSingleThreadExecutor(new ThreadFactory() {
|
protected ExecutorService executor = Executors.newSingleThreadExecutor(new ThreadFactory() {
|
||||||
public Thread newThread(Runnable r) {
|
public Thread newThread(Runnable r) {
|
||||||
Thread th = new Thread(r);
|
Thread th = new Thread(r);
|
||||||
th.setDaemon(true);
|
th.setDaemon(true);
|
||||||
return th;
|
return th;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
public TerrainQuad() {
|
public TerrainQuad() {
|
||||||
super("Terrain");
|
super("Terrain");
|
||||||
}
|
}
|
||||||
|
|
||||||
public TerrainQuad(String name, int patchSize, int totalSize, float[] heightMap) {
|
public TerrainQuad(String name, int patchSize, int totalSize, float[] heightMap) {
|
||||||
this(name, patchSize, totalSize, heightMap, null);
|
this(name, patchSize, totalSize, heightMap, null);
|
||||||
}
|
}
|
||||||
@ -135,7 +135,7 @@ public class TerrainQuad extends Node implements Terrain {
|
|||||||
fixNormalEdges(affectedAreaBBox);
|
fixNormalEdges(affectedAreaBBox);
|
||||||
addControl(new NormalRecalcControl(this));
|
addControl(new NormalRecalcControl(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected TerrainQuad(String name, int patchSize, int size,
|
protected TerrainQuad(String name, int patchSize, int size,
|
||||||
Vector3f stepScale, float[] heightMap, int totalSize,
|
Vector3f stepScale, float[] heightMap, int totalSize,
|
||||||
Vector2f offset, float offsetAmount,
|
Vector2f offset, float offsetAmount,
|
||||||
@ -145,7 +145,7 @@ public class TerrainQuad extends Node implements Terrain {
|
|||||||
if (!FastMath.isPowerOfTwo(size - 1)) {
|
if (!FastMath.isPowerOfTwo(size - 1)) {
|
||||||
throw new RuntimeException("size given: " + size + " Terrain quad sizes may only be (2^N + 1)");
|
throw new RuntimeException("size given: " + size + " Terrain quad sizes may only be (2^N + 1)");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (heightMap == null)
|
if (heightMap == null)
|
||||||
heightMap = generateDefaultHeightMap(size);
|
heightMap = generateDefaultHeightMap(size);
|
||||||
|
|
||||||
@ -158,7 +158,7 @@ public class TerrainQuad extends Node implements Terrain {
|
|||||||
this.lodCalculatorFactory = lodCalculatorFactory;
|
this.lodCalculatorFactory = lodCalculatorFactory;
|
||||||
split(patchSize, heightMap);
|
split(patchSize, heightMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLodCalculatorFactory(LodCalculatorFactory lodCalculatorFactory) {
|
public void setLodCalculatorFactory(LodCalculatorFactory lodCalculatorFactory) {
|
||||||
if (children != null) {
|
if (children != null) {
|
||||||
for (int i = children.size(); --i >= 0;) {
|
for (int i = children.size(); --i >= 0;) {
|
||||||
@ -171,8 +171,8 @@ public class TerrainQuad extends Node implements Terrain {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create just a flat heightmap
|
* Create just a flat heightmap
|
||||||
*/
|
*/
|
||||||
@ -184,11 +184,11 @@ public class TerrainQuad extends Node implements Terrain {
|
|||||||
/**
|
/**
|
||||||
* Call from the update() method of a terrain controller to update
|
* Call from the update() method of a terrain controller to update
|
||||||
* the LOD values of each patch.
|
* the LOD values of each patch.
|
||||||
* This will perform the geometry calculation in a background thread and
|
* This will perform the geometry calculation in a background thread and
|
||||||
* do the actual update on the opengl thread.
|
* do the actual update on the opengl thread.
|
||||||
*/
|
*/
|
||||||
public void update(List<Vector3f> locations) {
|
public void update(List<Vector3f> locations) {
|
||||||
|
|
||||||
updateLOD(locations);
|
updateLOD(locations);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,12 +197,12 @@ public class TerrainQuad extends Node implements Terrain {
|
|||||||
* Should only be called on the root quad
|
* Should only be called on the root quad
|
||||||
*/
|
*/
|
||||||
protected void updateNormals() {
|
protected void updateNormals() {
|
||||||
|
|
||||||
if (needToRecalculateNormals()) {
|
if (needToRecalculateNormals()) {
|
||||||
//TODO background-thread this if it ends up being expensive
|
//TODO background-thread this if it ends up being expensive
|
||||||
fixNormals(affectedAreaBBox); // the affected patches
|
fixNormals(affectedAreaBBox); // the affected patches
|
||||||
fixNormalEdges(affectedAreaBBox); // the edges between the patches
|
fixNormalEdges(affectedAreaBBox); // the edges between the patches
|
||||||
|
|
||||||
setNormalRecalcNeeded(null); // set to false
|
setNormalRecalcNeeded(null); // set to false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -234,11 +234,11 @@ public class TerrainQuad extends Node implements Terrain {
|
|||||||
UpdateLOD updateLodThread = new UpdateLOD(locations);
|
UpdateLOD updateLodThread = new UpdateLOD(locations);
|
||||||
executor.execute(updateLodThread);
|
executor.execute(updateLodThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized boolean isLodCalcRunning() {
|
private synchronized boolean isLodCalcRunning() {
|
||||||
return lodCalcRunning;
|
return lodCalcRunning;
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void setLodCalcRunning(boolean running) {
|
private synchronized void setLodCalcRunning(boolean running) {
|
||||||
lodCalcRunning = running;
|
lodCalcRunning = running;
|
||||||
}
|
}
|
||||||
@ -327,17 +327,17 @@ public class TerrainQuad extends Node implements Terrain {
|
|||||||
public float getTextureCoordinateScale() {
|
public float getTextureCoordinateScale() {
|
||||||
return 1f/(float)totalSize;
|
return 1f/(float)totalSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates the LOD of all child terrain patches.
|
* Calculates the LOD of all child terrain patches.
|
||||||
*/
|
*/
|
||||||
private class UpdateLOD implements Runnable {
|
private class UpdateLOD implements Runnable {
|
||||||
private List<Vector3f> camLocations;
|
private List<Vector3f> camLocations;
|
||||||
|
|
||||||
UpdateLOD(List<Vector3f> location) {
|
UpdateLOD(List<Vector3f> location) {
|
||||||
camLocations = location;
|
camLocations = location;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void run() {
|
public void run() {
|
||||||
long start = System.currentTimeMillis();
|
long start = System.currentTimeMillis();
|
||||||
if (isLodCalcRunning()) {
|
if (isLodCalcRunning()) {
|
||||||
@ -346,11 +346,11 @@ public class TerrainQuad extends Node implements Terrain {
|
|||||||
}
|
}
|
||||||
//System.out.println("spawned thread "+toString());
|
//System.out.println("spawned thread "+toString());
|
||||||
setLodCalcRunning(true);
|
setLodCalcRunning(true);
|
||||||
|
|
||||||
// go through each patch and calculate its LOD based on camera distance
|
// go through each patch and calculate its LOD based on camera distance
|
||||||
HashMap<String,UpdatedTerrainPatch> updated = new HashMap<String,UpdatedTerrainPatch>();
|
HashMap<String,UpdatedTerrainPatch> updated = new HashMap<String,UpdatedTerrainPatch>();
|
||||||
boolean lodChanged = calculateLod(camLocations, updated); // 'updated' gets populated here
|
boolean lodChanged = calculateLod(camLocations, updated); // 'updated' gets populated here
|
||||||
|
|
||||||
if (!lodChanged) {
|
if (!lodChanged) {
|
||||||
// not worth updating anything else since no one's LOD changed
|
// not worth updating anything else since no one's LOD changed
|
||||||
setLodCalcRunning(false);
|
setLodCalcRunning(false);
|
||||||
@ -358,28 +358,28 @@ public class TerrainQuad extends Node implements Terrain {
|
|||||||
}
|
}
|
||||||
// then calculate its neighbour LOD values for seaming in the shader
|
// then calculate its neighbour LOD values for seaming in the shader
|
||||||
findNeighboursLod(updated);
|
findNeighboursLod(updated);
|
||||||
|
|
||||||
fixEdges(updated); // 'updated' can get added to here
|
fixEdges(updated); // 'updated' can get added to here
|
||||||
|
|
||||||
reIndexPages(updated);
|
reIndexPages(updated);
|
||||||
|
|
||||||
setUpdateQuadLODs(updated); // set back to main ogl thread
|
setUpdateQuadLODs(updated); // set back to main ogl thread
|
||||||
|
|
||||||
setLodCalcRunning(false);
|
setLodCalcRunning(false);
|
||||||
//double duration = (System.currentTimeMillis()-start);
|
//double duration = (System.currentTimeMillis()-start);
|
||||||
//System.out.println("terminated in "+duration);
|
//System.out.println("terminated in "+duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setUpdateQuadLODs(HashMap<String,UpdatedTerrainPatch> updated) {
|
private void setUpdateQuadLODs(HashMap<String,UpdatedTerrainPatch> updated) {
|
||||||
synchronized (updatePatchesLock) {
|
synchronized (updatePatchesLock) {
|
||||||
updatedPatches = updated;
|
updatedPatches = updated;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Back on the ogl thread: update the terrain patch geometries
|
* Back on the ogl thread: update the terrain patch geometries
|
||||||
* @param updatedPatches to be updated
|
* @param updatedPatches to be updated
|
||||||
@ -390,20 +390,20 @@ public class TerrainQuad extends Node implements Terrain {
|
|||||||
// return;
|
// return;
|
||||||
if (updatedPatches == null || updatedPatches.isEmpty())
|
if (updatedPatches == null || updatedPatches.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
//TODO do the actual geometry update here
|
//TODO do the actual geometry update here
|
||||||
for (UpdatedTerrainPatch utp : updatedPatches.values()) {
|
for (UpdatedTerrainPatch utp : updatedPatches.values()) {
|
||||||
utp.updateAll();
|
utp.updateAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
updatedPatches.clear();
|
updatedPatches.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean calculateLod(List<Vector3f> location, HashMap<String,UpdatedTerrainPatch> updates) {
|
protected boolean calculateLod(List<Vector3f> location, HashMap<String,UpdatedTerrainPatch> updates) {
|
||||||
|
|
||||||
boolean lodChanged = false;
|
boolean lodChanged = false;
|
||||||
|
|
||||||
if (children != null) {
|
if (children != null) {
|
||||||
for (int i = children.size(); --i >= 0;) {
|
for (int i = children.size(); --i >= 0;) {
|
||||||
Spatial child = children.get(i);
|
Spatial child = children.get(i);
|
||||||
@ -418,10 +418,10 @@ public class TerrainQuad extends Node implements Terrain {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return lodChanged;
|
return lodChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected synchronized void findNeighboursLod(HashMap<String,UpdatedTerrainPatch> updated) {
|
protected synchronized void findNeighboursLod(HashMap<String,UpdatedTerrainPatch> updated) {
|
||||||
if (children != null) {
|
if (children != null) {
|
||||||
for (int x = children.size(); --x >= 0;) {
|
for (int x = children.size(); --x >= 0;) {
|
||||||
@ -441,20 +441,20 @@ public class TerrainQuad extends Node implements Terrain {
|
|||||||
}
|
}
|
||||||
TerrainPatch right = patch.rightNeighbour;
|
TerrainPatch right = patch.rightNeighbour;
|
||||||
TerrainPatch down = patch.bottomNeighbour;
|
TerrainPatch down = patch.bottomNeighbour;
|
||||||
|
|
||||||
UpdatedTerrainPatch utp = updated.get(patch.getName());
|
UpdatedTerrainPatch utp = updated.get(patch.getName());
|
||||||
if (utp == null) {
|
if (utp == null) {
|
||||||
utp = new UpdatedTerrainPatch(patch, patch.lod);
|
utp = new UpdatedTerrainPatch(patch, patch.lod);
|
||||||
updated.put(utp.getName(), utp);
|
updated.put(utp.getName(), utp);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (right != null) {
|
if (right != null) {
|
||||||
UpdatedTerrainPatch utpR = updated.get(right.getName());
|
UpdatedTerrainPatch utpR = updated.get(right.getName());
|
||||||
if (utpR == null) {
|
if (utpR == null) {
|
||||||
utpR = new UpdatedTerrainPatch(right, right.lod);
|
utpR = new UpdatedTerrainPatch(right, right.lod);
|
||||||
updated.put(utpR.getName(), utpR);
|
updated.put(utpR.getName(), utpR);
|
||||||
}
|
}
|
||||||
|
|
||||||
utp.setRightLod(utpR.getNewLod());
|
utp.setRightLod(utpR.getNewLod());
|
||||||
utpR.setLeftLod(utp.getNewLod());
|
utpR.setLeftLod(utp.getNewLod());
|
||||||
}
|
}
|
||||||
@ -464,16 +464,16 @@ public class TerrainQuad extends Node implements Terrain {
|
|||||||
utpD = new UpdatedTerrainPatch(down, down.lod);
|
utpD = new UpdatedTerrainPatch(down, down.lod);
|
||||||
updated.put(utpD.getName(), utpD);
|
updated.put(utpD.getName(), utpD);
|
||||||
}
|
}
|
||||||
|
|
||||||
utp.setBottomLod(utpD.getNewLod());
|
utp.setBottomLod(utpD.getNewLod());
|
||||||
utpD.setTopLod(utp.getNewLod());
|
utpD.setTopLod(utp.getNewLod());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find any neighbours that should have their edges seamed because another neighbour
|
* Find any neighbours that should have their edges seamed because another neighbour
|
||||||
* changed its LOD to a greater value (less detailed)
|
* changed its LOD to a greater value (less detailed)
|
||||||
@ -487,7 +487,7 @@ public class TerrainQuad extends Node implements Terrain {
|
|||||||
} else if (child instanceof TerrainPatch) {
|
} else if (child instanceof TerrainPatch) {
|
||||||
TerrainPatch patch = (TerrainPatch) child;
|
TerrainPatch patch = (TerrainPatch) child;
|
||||||
UpdatedTerrainPatch utp = updated.get(patch.getName());
|
UpdatedTerrainPatch utp = updated.get(patch.getName());
|
||||||
|
|
||||||
if(utp.lodChanged()) {
|
if(utp.lodChanged()) {
|
||||||
if (!patch.searchedForNeighboursAlready) {
|
if (!patch.searchedForNeighboursAlready) {
|
||||||
// set the references to the neighbours
|
// set the references to the neighbours
|
||||||
@ -538,7 +538,7 @@ public class TerrainQuad extends Node implements Terrain {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected synchronized void reIndexPages(HashMap<String,UpdatedTerrainPatch> updated) {
|
protected synchronized void reIndexPages(HashMap<String,UpdatedTerrainPatch> updated) {
|
||||||
if (children != null) {
|
if (children != null) {
|
||||||
for (int i = children.size(); --i >= 0;) {
|
for (int i = children.size(); --i >= 0;) {
|
||||||
@ -557,7 +557,7 @@ public class TerrainQuad extends Node implements Terrain {
|
|||||||
* children are either pages or blocks. This is dependent on the size of the
|
* children are either pages or blocks. This is dependent on the size of the
|
||||||
* children. If the child's size is less than or equal to the set block
|
* children. If the child's size is less than or equal to the set block
|
||||||
* size, then blocks are created, otherwise, pages are created.
|
* size, then blocks are created, otherwise, pages are created.
|
||||||
*
|
*
|
||||||
* @param blockSize
|
* @param blockSize
|
||||||
* the blocks size to test against.
|
* the blocks size to test against.
|
||||||
* @param heightMap
|
* @param heightMap
|
||||||
@ -784,7 +784,7 @@ public class TerrainQuad extends Node implements Terrain {
|
|||||||
patch4.setLodCalculator(lodCalculatorFactory);
|
patch4.setLodCalculator(lodCalculatorFactory);
|
||||||
TangentBinormalGenerator.generate(patch4);
|
TangentBinormalGenerator.generate(patch4);
|
||||||
}
|
}
|
||||||
|
|
||||||
public float[] createHeightSubBlock(float[] heightMap, int x,
|
public float[] createHeightSubBlock(float[] heightMap, int x,
|
||||||
int y, int side) {
|
int y, int side) {
|
||||||
float[] rVal = new float[side * side];
|
float[] rVal = new float[side * side];
|
||||||
@ -804,7 +804,7 @@ public class TerrainQuad extends Node implements Terrain {
|
|||||||
* A handy method that will attach all bounding boxes of this terrain
|
* A handy method that will attach all bounding boxes of this terrain
|
||||||
* to the node you supply.
|
* to the node you supply.
|
||||||
* Useful to visualize the bounding boxes when debugging.
|
* Useful to visualize the bounding boxes when debugging.
|
||||||
*
|
*
|
||||||
* @param parent that will get the bounding box shapes of the terrain attached to
|
* @param parent that will get the bounding box shapes of the terrain attached to
|
||||||
*/
|
*/
|
||||||
public void attachBoundChildren(Node parent) {
|
public void attachBoundChildren(Node parent) {
|
||||||
@ -1025,7 +1025,7 @@ public class TerrainQuad extends Node implements Terrain {
|
|||||||
|
|
||||||
if (!isPointOnTerrain(x,z))
|
if (!isPointOnTerrain(x,z))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
adjustHeight(x, z,delta);
|
adjustHeight(x, z,delta);
|
||||||
|
|
||||||
setNormalRecalcNeeded(xz);
|
setNormalRecalcNeeded(xz);
|
||||||
@ -1075,7 +1075,7 @@ public class TerrainQuad extends Node implements Terrain {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// a position can be in multiple quadrants, so use a bit anded value.
|
// a position can be in multiple quadrants, so use a bit anded value.
|
||||||
private int findQuadrant(int x, int y) {
|
private int findQuadrant(int x, int y) {
|
||||||
int split = (size + 1) >> 1;
|
int split = (size + 1) >> 1;
|
||||||
@ -1109,7 +1109,7 @@ public class TerrainQuad extends Node implements Terrain {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public int getQuadrant() {
|
public int getQuadrant() {
|
||||||
return quadrant;
|
return quadrant;
|
||||||
}
|
}
|
||||||
@ -1117,7 +1117,7 @@ public class TerrainQuad extends Node implements Terrain {
|
|||||||
public void setQuadrant(short quadrant) {
|
public void setQuadrant(short quadrant) {
|
||||||
this.quadrant = quadrant;
|
this.quadrant = quadrant;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected TerrainPatch getPatch(int quad) {
|
protected TerrainPatch getPatch(int quad) {
|
||||||
if (children != null)
|
if (children != null)
|
||||||
@ -1183,8 +1183,8 @@ public class TerrainQuad extends Node implements Terrain {
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected TerrainPatch findTopPatch(TerrainPatch tp) {
|
protected TerrainPatch findTopPatch(TerrainPatch tp) {
|
||||||
if (tp.getQuadrant() == 2)
|
if (tp.getQuadrant() == 2)
|
||||||
return getPatch(1);
|
return getPatch(1);
|
||||||
@ -1203,7 +1203,7 @@ public class TerrainQuad extends Node implements Terrain {
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected TerrainPatch findLeftPatch(TerrainPatch tp) {
|
protected TerrainPatch findLeftPatch(TerrainPatch tp) {
|
||||||
if (tp.getQuadrant() == 3)
|
if (tp.getQuadrant() == 3)
|
||||||
return getPatch(1);
|
return getPatch(1);
|
||||||
@ -1268,7 +1268,7 @@ public class TerrainQuad extends Node implements Terrain {
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected TerrainQuad findTopQuad() {
|
protected TerrainQuad findTopQuad() {
|
||||||
if (getParent() == null || !(getParent() instanceof TerrainQuad))
|
if (getParent() == null || !(getParent() instanceof TerrainQuad))
|
||||||
return null;
|
return null;
|
||||||
@ -1291,7 +1291,7 @@ public class TerrainQuad extends Node implements Terrain {
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected TerrainQuad findLeftQuad() {
|
protected TerrainQuad findLeftQuad() {
|
||||||
if (getParent() == null || !(getParent() instanceof TerrainQuad))
|
if (getParent() == null || !(getParent() instanceof TerrainQuad))
|
||||||
return null;
|
return null;
|
||||||
@ -1336,7 +1336,7 @@ public class TerrainQuad extends Node implements Terrain {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* fix the normals on the edge of the terrain patches.
|
* fix the normals on the edge of the terrain patches.
|
||||||
*/
|
*/
|
||||||
@ -1372,13 +1372,13 @@ public class TerrainQuad extends Node implements Terrain {
|
|||||||
bottomLeft = findDownPatch(left);
|
bottomLeft = findDownPatch(left);
|
||||||
|
|
||||||
tp.fixNormalEdges(right, bottom, top, left, bottomRight, bottomLeft, topRight, topLeft);
|
tp.fixNormalEdges(right, bottom, top, left, bottomRight, bottomLeft, topRight, topLeft);
|
||||||
|
|
||||||
}
|
}
|
||||||
} // for each child
|
} // for each child
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int collideWith(Collidable other, CollisionResults results){
|
public int collideWith(Collidable other, CollisionResults results){
|
||||||
@ -1397,7 +1397,7 @@ public class TerrainQuad extends Node implements Terrain {
|
|||||||
}
|
}
|
||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gather the terrain patches that intersect the given ray (toTest).
|
* Gather the terrain patches that intersect the given ray (toTest).
|
||||||
* This only tests the bounding boxes
|
* This only tests the bounding boxes
|
||||||
@ -1405,7 +1405,7 @@ public class TerrainQuad extends Node implements Terrain {
|
|||||||
* @param results
|
* @param results
|
||||||
*/
|
*/
|
||||||
public void findPick(Ray toTest, List<TerrainPickData> results) {
|
public void findPick(Ray toTest, List<TerrainPickData> results) {
|
||||||
|
|
||||||
if (getWorldBound() != null) {
|
if (getWorldBound() != null) {
|
||||||
if (getWorldBound().intersects(toTest)) {
|
if (getWorldBound().intersects(toTest)) {
|
||||||
// further checking needed.
|
// further checking needed.
|
||||||
@ -1460,7 +1460,7 @@ public class TerrainQuad extends Node implements Terrain {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void read(JmeImporter e) throws IOException {
|
public void read(JmeImporter e) throws IOException {
|
||||||
super.read(e);
|
super.read(e);
|
||||||
@ -1472,7 +1472,7 @@ public class TerrainQuad extends Node implements Terrain {
|
|||||||
quadrant = c.readInt("quadrant", 0);
|
quadrant = c.readInt("quadrant", 0);
|
||||||
totalSize = c.readInt("totalSize", 0);
|
totalSize = c.readInt("totalSize", 0);
|
||||||
lodCalculatorFactory = (LodCalculatorFactory) c.readSavable("lodCalculatorFactory", null);
|
lodCalculatorFactory = (LodCalculatorFactory) c.readSavable("lodCalculatorFactory", null);
|
||||||
|
|
||||||
// the terrain is re-built on load, so we need to run this once
|
// the terrain is re-built on load, so we need to run this once
|
||||||
//affectedAreaBBox = new BoundingBox(new Vector3f(0,0,0), size, Float.MAX_VALUE, size);
|
//affectedAreaBBox = new BoundingBox(new Vector3f(0,0,0), size, Float.MAX_VALUE, size);
|
||||||
//updateNormals();
|
//updateNormals();
|
||||||
@ -1541,13 +1541,13 @@ public class TerrainQuad extends Node implements Terrain {
|
|||||||
public int getTotalSize() {
|
public int getTotalSize() {
|
||||||
return totalSize;
|
return totalSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public float[] getHeightMap() {
|
public float[] getHeightMap() {
|
||||||
|
|
||||||
//if (true)
|
//if (true)
|
||||||
// return heightMap;
|
// return heightMap;
|
||||||
|
|
||||||
float[] hm = null;
|
float[] hm = null;
|
||||||
int length = ((size-1)/2)+1;
|
int length = ((size-1)/2)+1;
|
||||||
int area = size*size;
|
int area = size*size;
|
||||||
@ -1576,7 +1576,7 @@ public class TerrainQuad extends Node implements Terrain {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// combine them into a single heightmap
|
// combine them into a single heightmap
|
||||||
|
|
||||||
|
|
||||||
// first upper blocks
|
// first upper blocks
|
||||||
for (int y=0; y<length; y++) { // rows
|
for (int y=0; y<length; y++) { // rows
|
||||||
|
@ -0,0 +1,75 @@
|
|||||||
|
package com.jme3.terrain.heightmap;
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.FloatBuffer;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
|
import org.novyon.noise.Basis;
|
||||||
|
|
||||||
|
import com.jme3.math.Vector3f;
|
||||||
|
import com.jme3.terrain.MapUtils;
|
||||||
|
|
||||||
|
public class FractalHeightMapGrid implements HeightMapGrid {
|
||||||
|
|
||||||
|
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 size;
|
||||||
|
private final Basis base;
|
||||||
|
private final String cacheDir;
|
||||||
|
private final float heightScale;
|
||||||
|
|
||||||
|
public FractalHeightMapGrid(Basis base, String cacheDir, float heightScale) {
|
||||||
|
this.base = base;
|
||||||
|
this.cacheDir = cacheDir;
|
||||||
|
this.heightScale = heightScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HeightMap getHeightMapAt(Vector3f location) {
|
||||||
|
AbstractHeightMap heightmap = null;
|
||||||
|
if (this.cacheDir != null && new File(this.cacheDir, "terrain_" + (int) location.x + "_" + (int) location.z + ".png").exists()) {
|
||||||
|
try {
|
||||||
|
BufferedImage im = null;
|
||||||
|
im = ImageIO.read(new File(this.cacheDir, "terrain_" + (int) location.x + "_" + (int) location.z + ".png"));
|
||||||
|
heightmap = new Grayscale16BitHeightMap(im);
|
||||||
|
heightmap.setHeightScale(256);
|
||||||
|
} catch (IOException e) {}
|
||||||
|
} else {
|
||||||
|
FloatBuffer buffer = this.base.getBuffer(location.x * (this.size - 1), location.z * (this.size - 1), 0, this.size);
|
||||||
|
if (this.cacheDir != null) {
|
||||||
|
MapUtils.saveImage(MapUtils.toGrayscale16Image(buffer, this.size), new File(this.cacheDir, "terrain_" + (int) location.x
|
||||||
|
+ "_" + (int) location.z + ".png"));
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setSize(int size) {
|
||||||
|
this.size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -12,6 +12,8 @@ import com.jme3.texture.Texture;
|
|||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
import jme3tools.converters.ImageToAwt;
|
import jme3tools.converters.ImageToAwt;
|
||||||
|
|
||||||
@ -34,16 +36,18 @@ public class ImageBasedHeightMapGrid implements HeightMapGrid {
|
|||||||
|
|
||||||
public HeightMap getHeightMapAt(Vector3f location) {
|
public HeightMap getHeightMapAt(Vector3f location) {
|
||||||
// HEIGHTMAP image (for the terrain heightmap)
|
// HEIGHTMAP image (for the terrain heightmap)
|
||||||
int x = (int) (FastMath.floor(location.x / this.size) * this.size);
|
int x = (int) location.x;
|
||||||
int z = (int) (FastMath.floor(location.z / this.size) * this.size);
|
int z = (int) location.z;
|
||||||
AbstractHeightMap heightmap = null;
|
AbstractHeightMap heightmap = null;
|
||||||
try {
|
try {
|
||||||
|
Logger.getLogger(ImageBasedHeightMapGrid.class.getCanonicalName()).log(Level.INFO, "Loading heightmap from file: " + textureBase + "_" + x + "_" + z + "." + textureExt);
|
||||||
final InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(textureBase + "_" + x + "_" + z + "." + textureExt);
|
final InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(textureBase + "_" + x + "_" + z + "." + textureExt);
|
||||||
BufferedImage im = null;
|
BufferedImage im = null;
|
||||||
if (stream != null) {
|
if (stream != null) {
|
||||||
im = ImageIO.read(stream);
|
im = ImageIO.read(stream);
|
||||||
} else {
|
} else {
|
||||||
im = new BufferedImage(size, size, BufferedImage.TYPE_USHORT_GRAY);
|
im = new BufferedImage(size, size, BufferedImage.TYPE_USHORT_GRAY);
|
||||||
|
Logger.getLogger(ImageBasedHeightMapGrid.class.getCanonicalName()).log(Level.INFO, "File: " + textureBase + "_" + x + "_" + z + "." + textureExt + " not found, loading zero heightmap instead");
|
||||||
}
|
}
|
||||||
// CREATE HEIGHTMAP
|
// CREATE HEIGHTMAP
|
||||||
heightmap = new Grayscale16BitHeightMap(im);
|
heightmap = new Grayscale16BitHeightMap(im);
|
||||||
|
@ -20,9 +20,18 @@ import com.jme3.renderer.Camera;
|
|||||||
import com.jme3.terrain.geomipmap.TerrainGrid;
|
import com.jme3.terrain.geomipmap.TerrainGrid;
|
||||||
import com.jme3.terrain.geomipmap.TerrainLodControl;
|
import com.jme3.terrain.geomipmap.TerrainLodControl;
|
||||||
import com.jme3.terrain.geomipmap.TerrainQuad;
|
import com.jme3.terrain.geomipmap.TerrainQuad;
|
||||||
|
import com.jme3.terrain.heightmap.FractalHeightMapGrid;
|
||||||
import com.jme3.terrain.heightmap.ImageBasedHeightMapGrid;
|
import com.jme3.terrain.heightmap.ImageBasedHeightMapGrid;
|
||||||
import com.jme3.texture.Texture;
|
import com.jme3.texture.Texture;
|
||||||
import com.jme3.texture.Texture.WrapMode;
|
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 TerrainGridTest extends SimpleApplication {
|
public class TerrainGridTest extends SimpleApplication {
|
||||||
|
|
||||||
@ -31,13 +40,18 @@ public class TerrainGridTest extends SimpleApplication {
|
|||||||
private float grassScale = 64;
|
private float grassScale = 64;
|
||||||
private float dirtScale = 16;
|
private float dirtScale = 16;
|
||||||
private float rockScale = 128;
|
private float rockScale = 128;
|
||||||
private boolean usePhysics = false;
|
private boolean usePhysics = true;
|
||||||
|
|
||||||
public static void main(final String[] args) {
|
public static void main(final String[] args) {
|
||||||
TerrainGridTest app = new TerrainGridTest();
|
TerrainGridTest app = new TerrainGridTest();
|
||||||
app.start();
|
app.start();
|
||||||
}
|
}
|
||||||
private CharacterControl player3;
|
private CharacterControl player3;
|
||||||
|
private FractalSum base;
|
||||||
|
private PerturbFilter perturb;
|
||||||
|
private OptimizedErode therm;
|
||||||
|
private SmoothFilter smooth;
|
||||||
|
private IterativeFilter iterate;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void simpleInitApp() {
|
public void simpleInitApp() {
|
||||||
@ -70,8 +84,43 @@ public class TerrainGridTest extends SimpleApplication {
|
|||||||
mat_terrain.setTexture("Tex3", rock);
|
mat_terrain.setTexture("Tex3", rock);
|
||||||
mat_terrain.setFloat("Tex3Scale", rockScale);
|
mat_terrain.setFloat("Tex3Scale", rockScale);
|
||||||
|
|
||||||
this.terrain = new TerrainGrid("terrain", 65, 1025, new ImageBasedHeightMapGrid("Textures/Terrain/grid/mountains", "png",
|
this.base = new FractalSum();
|
||||||
this.assetManager));
|
this.base.setRoughness(0.7f);
|
||||||
|
this.base.setFrequency(1.0f);
|
||||||
|
this.base.setAmplitude(1.0f);
|
||||||
|
this.base.setLacunarity(2.12f);
|
||||||
|
this.base.setOctaves(8);
|
||||||
|
this.base.setScale(0.02125f);
|
||||||
|
this.base.addModulator(new NoiseModulator() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float value(float... in) {
|
||||||
|
return ShaderUtils.clamp(in[0] * 0.5f + 0.5f, 0, 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
FilteredBasis ground = new FilteredBasis(this.base);
|
||||||
|
|
||||||
|
this.perturb = new PerturbFilter();
|
||||||
|
this.perturb.setMagnitude(0.119f);
|
||||||
|
|
||||||
|
this.therm = new OptimizedErode();
|
||||||
|
this.therm.setRadius(5);
|
||||||
|
this.therm.setTalus(0.011f);
|
||||||
|
|
||||||
|
this.smooth = new SmoothFilter();
|
||||||
|
this.smooth.setRadius(1);
|
||||||
|
this.smooth.setEffect(0.7f);
|
||||||
|
|
||||||
|
this.iterate = new IterativeFilter();
|
||||||
|
this.iterate.addPreFilter(this.perturb);
|
||||||
|
this.iterate.addPostFilter(this.smooth);
|
||||||
|
this.iterate.setFilter(this.therm);
|
||||||
|
this.iterate.setIterations(1);
|
||||||
|
|
||||||
|
ground.addPreFilter(this.iterate);
|
||||||
|
|
||||||
|
this.terrain = new TerrainGrid("terrain", 65, 257, new FractalHeightMapGrid(ground, null, 256f));
|
||||||
|
|
||||||
this.terrain.setMaterial(this.mat_terrain);
|
this.terrain.setMaterial(this.mat_terrain);
|
||||||
this.terrain.setLocalTranslation(0, 0, 0);
|
this.terrain.setLocalTranslation(0, 0, 0);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user