Thanks rickard for porting this to jme3. I put your jme3 source code to <a href=http://code.google.com/p/cave3d/’>http://code.google.com/p/cave3d/. There is now jme2 and jme3 folders in the trunk. I wanted to test the jme3 port by myself, but I haven’t succeed to get jme3 setup working.
mazander said:Thanks @mazander, this'll make it a lot easier for everyone to find their way around the Cave3D project. Maybe add @rickard as a committer though in case some updates should be applied? According to his last post there seems to be a few outstanding issues around still.
Thanks rickard for porting this to jme3. I put your jme3 source code to http://code.google.com/p/cave3d/. There is now jme2 and jme3 folders in the trunk. I wanted to test the jme3 port by myself, but I haven't succeed to get jme3 setup working.
Yesterday I finally got jme3 setup running in eclipse (Ubuntu). I checkout the latest version of jme3 from svn, then I have to drop lwgl2.5, android, jogl things away and fix few compiler errors related to use of @Override tag with interfaces.
Today I will try to improve the jme3 version to match the latest jme2 version.
To get texturing of jme3 version look as good, I think a custom shader for tri-planar texturing is still needed. Tri-planar blends the texture from three directions. Each three directions should have own texture coordinates. The blending factors depend on the fragment normal. This way texturing don’t stretch and have discontinuous. I will also try get around concurrency problems found by @rickard.
I made a small update to CaveGenerator.java yesterday, in attempt to fix the concurrency issue (which i’m leaning towards believing is either to do with the bounds check, or my computer simply being to slow to keep up).
Here’s the latest CaveGenerator.java
Two changes: I dropped the list of instances to add, and replaced it with a Future.get(), and replaced box.getCenter().set()… with box.setCenter()
[java]public class CaveGenerator extends SimpleApplication {
private static CaveGenerator app;
public static void main(String[] args) {
app = new CaveGenerator();
app.start();
}
private final static long SEED = “jme”.hashCode();
private final static float MESH_SIZE = 64f;
private final static float HALF_MESH_SIZE = MESH_SIZE / 2f;
private final HashMap<Vector3f, CaveTriMesh> caveMeshes = new HashMap<Vector3f, CaveTriMesh>();
private int lastMeshCount = 0;
private Node caveNode = new Node(“cave node”);
private PointLight pl = new PointLight();
private CaveScalarField scalarField;
private ScalarFieldPolygonisator polygonisator;
private final Vector3f key = new Vector3f();
private final Vector3f camGridCoord = new Vector3f();
private final PolygonizationThread generatorThread = new PolygonizationThread();
private final BoundingBox box = new BoundingBox(new Vector3f(), HALF_MESH_SIZE, HALF_MESH_SIZE, HALF_MESH_SIZE);
private Stack<CaveTriMesh> instancesToAdd = new Stack<CaveTriMesh>();
private Material caveMaterial;
@Override
public void simpleUpdate(float tpf) {
// TODO: set pl config
pl.getPosition().set(cam.getLocation());
camGridCoord.set(cam.getLocation()).divideLocal(MESH_SIZE);
camGridCoord.set(Math.round(camGridCoord.x), Math.round(camGridCoord.y), Math.round(camGridCoord.z));
final int d = 3;
for(int x = -d; x <= d; x++) {
for(int y = -d; y <= d; y++) {
for(int z = -d; z <= d; z++) {
key.set(camGridCoord).addLocal(x,y,z);
//box.getCenter().set(key).multLocal(MESH_SIZE);
box.setCenter(key);
box.getCenter().multLocal(MESH_SIZE);
if(cam.contains(box) != Camera.FrustumIntersect.Outside) {
generatorThread.addcaveMesh(key);
}
}
}
}
/while(!instancesToAdd.isEmpty()){
// get the first element, to make sure nothing gets “buried” in the stack
caveNode.attachChild(instancesToAdd.pop());
//instancesToAdd.remove(0);
System.out.println("stack size " + instancesToAdd.size());
}/
long time = System.currentTimeMillis();
int quantity = caveNode.getQuantity();
for (int i = quantity - 1; i >= 0; i–) {
CaveTriMesh caveInstance = (CaveTriMesh) caveNode.getChild(i);
Vector3f worldCenter = caveInstance.getWorldBound().getCenter();
if(worldCenter.distance(cam.getLocation()) > MESH_SIZE * 6) {
caveNode.detachChildAt(i);
caveMeshes.remove(caveInstance.getCenter());
}
}
if(caveMeshes.size() != lastMeshCount) {
lastMeshCount = caveMeshes.size();
//caveNode.updateGeometricState();
System.out.println("Mesh count: " + caveNode.getQuantity());
}
}
public void simpleInitApp() {
settings.setTitle(“Cave Generator”);
/lightState.setLocalViewer(true);
lightState.setEnabled(true);
PointLight pl = (PointLight)lightState.get(0);/
pl = new PointLight();
pl.setRadius(210);
rootNode.addLight(pl);
caveNode = new Node();
scalarField = new CaveScalarField(SEED, 128f, 4f);
polygonisator = new ScalarFieldPolygonisator(MESH_SIZE, 8, scalarField, true, false);
/FilterPostProcessor fpp=new FilterPostProcessor(assetManager);
SSAOFilter ssaoFilter= new SSAOFilter(0.92f,2.2f,0.46f,0.4f);
fpp.addFilter(ssaoFilter);
FogFilter fog=new FogFilter();
fog.setFogColor(new ColorRGBA(0.0f, 0.0f, 0.0f, 1.0f));
fog.setFogDistance(505);
fog.setFogDensity(2.0f);
fpp.addFilter(fog);
viewPort.addProcessor(fpp);/
//caveMaterial = new Material(assetManager, “Common/MatDefs/Misc/SolidColor.j3md”);
//caveMaterial.setColor(“m_Color”, ColorRGBA.Blue);
caveMaterial = new Material(assetManager, “Common/MatDefs/Light/Lighting.j3md”);
Texture diffuse = assetManager.loadTexture(“Textures/stone.jpg”);
diffuse.setWrap(Texture.WrapMode.Repeat);
diffuse.setMagFilter(Texture.MagFilter.Bilinear);
diffuse.setMinFilter(Texture.MinFilter.Trilinear);
Texture normal = assetManager.loadTexture(“Textures/stone-normal.jpg”);
normal.setMagFilter(Texture.MagFilter.Bilinear);
normal.setMinFilter(Texture.MinFilter.Trilinear);
normal.setWrap(Texture.WrapMode.Repeat);
caveMaterial.setTexture(“m_DiffuseMap”, diffuse);
caveMaterial.setTexture(“m_NormalMap”, normal);
caveMaterial.setColor(“m_Specular”, ColorRGBA.White);
caveMaterial.setFloat(“m_Shininess”, 0.25f);
rootNode.attachChild(caveNode);
float aspect = (float) settings.getWidth() / (float) settings.getHeight();
cam.setFrustumPerspective(75f, aspect, 1, 4 * MESH_SIZE);
getFlyByCamera().setMoveSpeed(50f);
Thread thread = new Thread(generatorThread, “Generator Thread”);
thread.start();
}
private final class PolygonizationThread implements Runnable {
private final Stack<CaveTriMesh> stack = new Stack<CaveTriMesh>();
private final Object lock = new Object();
private void addcaveMesh(final Vector3f center) {
if (caveMeshes.get(center) == null) {
CaveTriMesh caveInstance = new CaveTriMesh(center, MESH_SIZE);
caveInstance.setMaterial(caveMaterial);
stack.add(caveInstance);
caveMeshes.put(caveInstance.getCenter(), caveInstance);
}
synchronized (lock) {
lock.notify();
}
}
public void run() {
while (true) {
while (!stack.isEmpty()) {
final CaveTriMesh caveInstance = stack.pop();
polygonisator.calculate(caveInstance, caveInstance.getWorldCenter(), 0f);
caveInstance.setModelBound(new BoundingBox());
caveInstance.updateModelBound();
Future fut = app.enqueue(new Callable() {
public Object call() throws Exception {
//this is where you modify your object, you can return a result value
caveNode.attachChild( caveInstance );
return null;
}
});
try {
//to retrieve return value (waits for call to finish, fire&forget otherwise):
fut.get();
} catch (InterruptedException ex) {
Logger.getLogger(CaveGenerator.class.getName()).log(Level.SEVERE, null, ex);
} catch (ExecutionException ex) {
Logger.getLogger(CaveGenerator.class.getName()).log(Level.SEVERE, null, ex);
}
//instancesToAdd.add(0, caveInstance);
}
try {
synchronized (lock) {
lock.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}[/java]
Captured a new video today with Windows 7 and Fraps. The video shows how the cave geometry get more complicated when more 3d noise frequencies are interpolated together. It is still the jme2 version.
http://www.youtube.com/watch?v=Kpw9AsMgfQE
Nice!
I’ve sorted out the tri planar texturing now, but something feels wrong about the lighting. Will have a look at it tomorrow evening.
Here’s the source in case anyone wants to have a look:
source
There are bits of floating rocks in the video… Can you fix that? Just being helpful =].
nomnom said:
There are bits of floating rocks in the video... Can you fix that? Just being helpful =].
Floating rocks that are totally inside one mesh block is easy to remove. Rocks located in two meshes are a lot harder to remove. The cave is divided to sub meshes. I modifed the ScalarFieldPolygonisator to first calculate iso values for mesh borders. If the all borders of a box are air (below iso value) then the whole is box air. Inaddition this was a good speedup.
The next things I'm planning to add to the cave3d are collision detection, crystals and water pools. Collision detection for scalar fileds is explained in http://http.developer.nvidia.com/GPUGems3/gpugems3_ch01.html Chapter: "1.6.2 Collisions and Lighting of Foreign Objects". Crystals and water pools are harder to do. Any help is valuable.
I don’t mean to recklessly bump, but how is collision detecting going? Any plans to integrate with JME’s in-house physics? And the issue with the cave generator “bugging out” is still prevalent. I’ll try to help with that. My current theory pits this bit of code as the culprit.
[java]int quantity = caveNode.getQuantity();
for (int i = quantity - 1; i >= 0; i–) {
CaveTriMesh caveInstance = (CaveTriMesh) caveNode.getChild(i);
Vector3f worldCenter = caveInstance.getWorldBound().getCenter();
if(worldCenter.distance(cam.getLocation()) > MESH_SIZE * 6) {
caveNode.detachChildAt(i);
caveMeshes.remove(caveInstance.getCenter());
}
}[/java]
I’ll let you know of any progress.
Looks fantastic!
I did a simple collision detection for particles http://code.google.com/p/cave3d/source/detail?r=12. With the jme2 version is now possible shoot small spheres that collide with cave walls. I don’t know if it is even possible to implement scalar field collisions into JME’s in-house physics (bullet). It is sad that have so little time for coding currently :(. I try next fix “bugging out” issue.
I added a fun little Shooter to your existing code. Check it out.
http://www.youtube.com/watch?v=Z3-KG0gw_pY
@mazander thank you for your project!!!
I’m trying to run jme3 part but i have an exception:
[patch]
…
stack size 0
авг 31, 2012 1:12:45 AM com.jme3.scene.Node attachChild
INFO: Child ((3.0, 2.0, 2.0)) attached to this node (null)
stack size 0
авг 31, 2012 1:12:45 AM com.jme3.scene.Node attachChild
INFO: Child ((3.0, 2.0, 1.0)) attached to this node (null)
stack size 1
авг 31, 2012 1:12:45 AM com.jme3.scene.Node attachChild
INFO: Child ((3.0, 2.0, 0.0)) attached to this node (null)
stack size 0
авг 31, 2012 1:12:45 AM com.jme3.scene.Node attachChild
INFO: Child ((3.0, 2.0, -1.0)) attached to this node (null)
stack size 2
авг 31, 2012 1:12:45 AM com.jme3.scene.Node attachChild
INFO: Child ((3.0, 2.0, -2.0)) attached to this node (null)
stack size 1
авг 31, 2012 1:12:45 AM com.jme3.scene.Node attachChild
INFO: Child ((3.0, 2.0, -3.0)) attached to this node (null)
stack size 1
авг 31, 2012 1:12:45 AM com.jme3.scene.Node attachChild
INFO: Child ((3.0, 1.0, 3.0)) attached to this node (null)
stack size 1
авг 31, 2012 1:12:45 AM com.jme3.scene.Node attachChild
INFO: Child ((3.0, 1.0, 2.0)) attached to this node (null)
stack size 0
авг 31, 2012 1:12:45 AM com.jme3.scene.Node attachChild
INFO: Child ((3.0, 1.0, 1.0)) attached to this node (null)
stack size 0
авг 31, 2012 1:12:45 AM com.jme3.scene.Node attachChild
INFO: Child ((3.0, 1.0, 0.0)) attached to this node (null)
stack size 0
авг 31, 2012 1:12:45 AM com.jme3.scene.Node attachChild
INFO: Child ((3.0, 1.0, -1.0)) attached to this node (null)
stack size 1
авг 31, 2012 1:12:45 AM com.jme3.scene.Node attachChild
INFO: Child ((3.0, 1.0, -2.0)) attached to this node (null)
stack size 0
авг 31, 2012 1:12:45 AM com.jme3.scene.Node attachChild
INFO: Child ((3.0, 1.0, -3.0)) attached to this node (null)
stack size 0
авг 31, 2012 1:12:45 AM com.jme3.scene.Node attachChild
INFO: Child ((3.0, 0.0, 3.0)) attached to this node (null)
stack size 0
Mesh count: 23
авг 31, 2012 1:12:45 AM com.jme3.scene.Node attachChild
INFO: Child ((3.0, 0.0, 2.0)) attached to this node (null)
stack size 3
авг 31, 2012 1:12:45 AM com.jme3.scene.Node attachChild
INFO: Child ((-3.0, -2.0, 3.0)) attached to this node (null)
stack size 2
авг 31, 2012 1:12:45 AM com.jme3.scene.Node attachChild
INFO: Child ((-3.0, -2.0, 2.0)) attached to this node (null)
stack size 1
авг 31, 2012 1:12:45 AM com.jme3.scene.Node attachChild
INFO: Child ((-3.0, -2.0, 1.0)) attached to this node (null)
stack size 0
авг 31, 2012 1:12:45 AM com.jme3.app.Application handleError
SEVERE: Uncaught exception thrown in Thread[LWJGL Renderer Thread,5,main]
java.lang.IllegalArgumentException: Number of remaining buffer elements is 0, must be at least 1. Because at most 1 elements can be returned, a buffer with at least 1 elements is required, regardless of actual returned element count
at org.lwjgl.BufferChecks.throwBufferSizeException(BufferChecks.java:162)
at org.lwjgl.BufferChecks.checkBufferSize(BufferChecks.java:189)
at org.lwjgl.BufferChecks.checkBuffer(BufferChecks.java:244)
at org.lwjgl.opengl.GL15.glBufferData(GL15.java:162)
at com.jme3.renderer.lwjgl.LwjglRenderer.updateBufferData(LwjglRenderer.java:2056)
at com.jme3.renderer.lwjgl.LwjglRenderer.drawTriangleList(LwjglRenderer.java:2200)
at com.jme3.renderer.lwjgl.LwjglRenderer.renderMeshDefault(LwjglRenderer.java:2395)
at com.jme3.renderer.lwjgl.LwjglRenderer.renderMesh(LwjglRenderer.java:2434)
at com.jme3.material.Material.renderMultipassLighting(Material.java:830)
at com.jme3.material.Material.render(Material.java:1049)
at com.jme3.renderer.RenderManager.renderGeometry(RenderManager.java:518)
at com.jme3.renderer.queue.RenderQueue.renderGeometryList(RenderQueue.java:301)
at com.jme3.renderer.queue.RenderQueue.renderQueue(RenderQueue.java:353)
at com.jme3.renderer.RenderManager.renderViewPortQueues(RenderManager.java:754)
at com.jme3.renderer.RenderManager.flushQueue(RenderManager.java:710)
at com.jme3.renderer.RenderManager.renderViewPort(RenderManager.java:974)
at com.jme3.renderer.RenderManager.render(RenderManager.java:1016)
at com.jme3.app.SimpleApplication.update(SimpleApplication.java:251)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.runLoop(LwjglAbstractDisplay.java:151)
at com.jme3.system.lwjgl.LwjglDisplay.runLoop(LwjglDisplay.java:181)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:228)
at java.lang.Thread.run(Thread.java:722)
авг 31, 2012 1:12:45 AM com.jme3.renderer.lwjgl.LwjglRenderer cleanup
INFO: Deleting objects and invalidating state
авг 31, 2012 1:12:45 AM com.jme3.scene.Node detachChildAt
INFO: Gui Node (Node): Child removed.
авг 31, 2012 1:12:45 AM com.jme3.scene.Node detachChildAt
INFO: Gui Node (Node): Child removed.
авг 31, 2012 1:12:45 AM com.jme3.input.lwjgl.LwjglMouseInput destroy
INFO: Mouse destroyed.
авг 31, 2012 1:12:45 AM com.jme3.input.lwjgl.LwjglKeyInput destroy
INFO: Keyboard destroyed.
авг 31, 2012 1:12:45 AM com.jme3.system.lwjgl.LwjglAbstractDisplay deinitInThread
INFO: Display destroyed.
BUILD STOPPED (total time: 12 seconds)
[/patch]
I run nightly builds of jme3. Can you help in running your project?
PS: m_DiffuseMap is now should be DiffuseMap… and all other shader stuff should be without “m_” too.