Optimization issues


Check out Bounding Volumes:

http://www.jmonkeyengine.com/wiki/doku.php/tutorial_2_-_learning_jme_2#bounding_volumes

The idea is that it is faster for the engine to detect if a simple volume, like a box, is on or off the screen, and decide whether to draw it or not on that basis. Since you are already drawing something simple (cubes) maybe you could group your cubes into 10x10 units or something like that?

You can also check your CullState:

 CullState cs = DisplaySystem.getDisplaySystem().getRenderer().createCullState();
       cs.setCullFace(CullState.Face.Front);
       model.setRenderState(cs);



This will automatically turn off all polygons that are facing away from the camera, which should speed things up a little.

Wow, I thought I'd already implemented that, but apparently it isn't working. Each one of the Blocks is a SharedMesh, and I set it up like this:



...
Block shareBlock = new Block(("WorldBlock"), new Vector3f(0,0,0), 1.0f, 1.0f, 1.0f);
shareBlock.setModelBound(new BoundingBox());
shareBlock.updateModelBound();
...
worldBlocks[i][j][k] = new SharedMesh(shareBlock);
worldBlocks[i][j][k].setLocalTranslation(new Vector3f(i*2,k*2,j*2));
worldBlocksTextureStates[i][j][k] = DisplaySystem.getDisplaySystem().getRenderer().createTextureState();
terrainNode.attachChild(worldBlocks[i][j][k]);  //attached to worldRoot
worldBlocksTextureStates[i][j][k].setTexture(orangeTexture);
worldBlocks[i][j][k].setRenderState(worldBlocksTextureStates[i][j][k]);
worldBlocks[i][j][k].updateRenderState();
worldBlocks[i][j][k].setModelBound(new BoundingBox());
worldBlocks[i][j][k].updateModelBound();
...
worldRoot.updateModelBound();  //worldRoot is the root node of the gamestate
CullState cs = DisplaySystem.getDisplaySystem().getRenderer().createCullState();
cs.setCullFace(Face.Back);
worldRoot.setRenderState(cs);
...
worldRoot.updateRenderState();
worldRoot.updateWorldBound();



I'm pretty sure I updateModelBound more often than I need to, but I'm not sure what else to try. After the terrain finishes loading, I have the game output the number of triangles and vertices, and both remain the same whether or not I set the cullstate (which is why I think I've done it incorrectly).

Any ideas?

Cullstate won’t affect the number of verts and faces in the scene, just whether or not they are rendered. (ie, if you went inside your model, you wouldn’t be able to see it from the inside out, because you would be looking at the back of the faces.)



I’m not all that familiar with jME, so I can’t suggest much beyond what I said before about grouping your boxes into larger bounding boxes. Maybe having a bounding box for every single cube is too heavy. If you created one for every group of 10x10 cubes it might help. It’s just an idea though.



Another idea might be not to use separate objects for every cube. If the topology of the land is not changing in realtime, perhaps you could merge them into a single mesh? Not sure, but I would expect the overhead of all those meshes to add some slowdown. (??? jme experts, can you confirm ??? )



This page on the wiki shows how to create geometry from scratch:



http://www.jmonkeyengine.com/wiki/doku.php/tutorial_3_-_learning_jme_2



Shouldn’t be too hard to build your cubes all in one mesh…



Good luck!


greymoth said:

Not sure, but I would expect the overhead of all those meshes to add some slowdown. (??? jme experts, can you confirm ??? )


I'm not an expert, but I can confirm. The number of objects you have in your scenegraph is killer for every scenegraph API. The deeper or broader the scenegraph gets the more the framerates will drop.

If your terrain doesn't constantly change, your very best bet for FPS boost is


worldRoot.lock(); // worldRoot.lockBranch(); and worldRoot.lockMeshes(); are called in there and are most important to you here



If you at any point want to change your terrain, you can call worldRoot.unlock(); make changes and updateGeometricState(), etc. and lock() again.

@dhdd: Cool, nice trick. Thanks.

you could also remove all boxes hidden by other ones, or set their cullstate to always

Thanks for the ideas, but my terrain will need to change in real time. I've already written the terrain generator to only create the cubes it needs to not have holes in the world, but if there is a way to not render what you can't see (like what's behind you, what's behind mountains, etc…), that would be ideal!

Well, If your terrain does not change for one second or so, you can achieve large FPS gains with locking and unlocking before changing and then locking again.



If thats not the case, that is if you change your terrain EVERY FRAME, you can still add BoundingBoxes to all your boxes and have them being culled dynamically, also backface culling is a good way to go:


terrain.setCullHint(CullHint.Dynamic);
CullState cs = ....
cs.setEnabled(true);
cs.setCullFace(Face.Back);
terrain.setRenderState(cs);
terrain.updateRenderState();
for(Spatial box : terrain.getChildren()) {
  box.setModelBound(new BoundingBox());
  box.updateModelBound();
} // for



If you group 10 or so of your boxes together under one Node and give that node a boundingbox as well, the whole node can be culled quite easily, giving you more performance. But just test around with the options above.

You can change it even with ym emthod, just create some helper methods, removing the one, and then regenrating the terrain around it. (Let me guess works style game?)

merge merge ! :)  replace 4x4 small with one single bigger block.

Bringing down the sheer amount of blocks will save you quite a bit on computing time.


Group your Boxes!



I recently had implemented a Octree for a similar Terrain "System".



It was Box based, so each box had to be 1x1, wich made the implementation much easier.



If you group your Boxes in an Octree, the engine can cull away big parts of your terrain with few BoundingBox checks!

cam.setfarFrustum would also help i guess.

I've added a bit of grouping to the Blocks, which has helped (thanks!), but I'm still having the same problem with the .lock()



Is there an easy way to automatically cull Spatials that are outside a certain distance from the camera?  That would help the framerate quite a bit.



EDIT:


Empire Phoenix said:

cam.setfarFrustum would also help i guess.


I overlooked your advice and I don't know why  XD  that did exactly what I wanted for my distance-rendering problem.  Thank you very much!

the locking WARNING is normal. When you lock a mesh jme creates display lists which let the geometry render faster. When you lock again without unlocking before, the warning just tells you that the display list is already there.



Btw. I really think we should get rid of that warning or at least reduce it to INFO.

Ok, if I understand right, if I would like to implement octree culling algorithm for example I would just have to split all the elements in scene properly to octreenodes, and make bounding volumes for them and they would get culled automatically?

simple, do not use multi thread in jme2 it simply does not work

I think my problem was with how my code was structured… a little cleaning and I haven't seen that particular problem since.



My problem now is with the skybox.  I set the camera's farFrustum to 150, which gives reasonable performance and viewing distance, and I set the skybox to a size of 200200200. The idea is that everything but the skybox is culled by the camera after a certain distance, but the problem is that the skybox still gets culled.  I've tried setting a cullState for the box set to not cull, I've tried setting the cullHint to Never, I've tried setLastFrustumIntersection(FrustumIntersect.Inside). None of these seem to work. What is the correct way to do this?

Your skybox doesn't need to be large like that, it could be 10 x 10 x 10. You only need to render it first, before the rest of your scene.

How can I do that and prevent it from being culled by the camera?



EDIT:


nymon said:

Your skybox doesn't need to be large like that, it could be 10 x 10 x 10. You only need to render it first, before the rest of your scene.


Thanks for the help! Writing my own render() fixed the problem.

Working code:

renderSkybox()


private void renderSkybox(){
   System.out.println("Building skybox...");
   sky = new Skybox("Skybox", 20, 20, 20);
   Texture northTexture = TextureManager.loadTexture("Resources/Skybox/northSky.png", MinificationFilter.Trilinear, MagnificationFilter.Bilinear);
   Texture southTexture = TextureManager.loadTexture("Resources/Skybox/southSky.png", MinificationFilter.Trilinear, MagnificationFilter.Bilinear);
   Texture eastTexture = TextureManager.loadTexture("Resources/Skybox/eastSky.png", MinificationFilter.Trilinear, MagnificationFilter.Bilinear);
   Texture westTexture = TextureManager.loadTexture("Resources/Skybox/westSky.png", MinificationFilter.Trilinear, MagnificationFilter.Bilinear);
   Texture upTexture = TextureManager.loadTexture("Resources/Skybox/upSky.png", MinificationFilter.Trilinear, MagnificationFilter.Bilinear);
   Texture downTexture = TextureManager.loadTexture("Resources/Skybox/downSky.png", MinificationFilter.Trilinear, MagnificationFilter.Bilinear);
   
   sky.setTexture(Skybox.Face.North, northTexture);
   sky.setTexture(Skybox.Face.South, southTexture);
   sky.setTexture(Skybox.Face.East, eastTexture);
   sky.setTexture(Skybox.Face.West, westTexture);
   sky.setTexture(Skybox.Face.Up, upTexture);
   sky.setTexture(Skybox.Face.Down, downTexture);
   
   FogState fog = DisplaySystem.getDisplaySystem().getRenderer().createFogState();
   fog.setEnabled(false);
   sky.setRenderState(fog);
   
   worldRoot.attachChild(sky);
   sky.setCullHint(Spatial.CullHint.Never);
   sky.setLightCombineMode(LightCombineMode.Off);
   sky.setTextureCombineMode(TextureCombineMode.Replace);
   
   sky.updateRenderState();
   
   System.out.println("...built");
}



render()


public void render(float tpf) {
   display.getRenderer().clearBuffers();
   display.getRenderer().draw(world.sky);
   display.getRenderer().draw(world.terrainNode);
   display.getRenderer().displayBackBuffer();
   
   SceneMonitor.getMonitor().renderViewer(display.getRenderer()); //MONITOR
}



For some reason, setting lockMeshes() still removes all textures from my Blocks, even when done after everything is rendered with SceneMonitor.
Fiarr said:

For some reason, setting lockMeshes() still removes all textures from my Blocks, even when done after everything is rendered with SceneMonitor.

Thanks for using Scene Monitor! Be sure you test your locking problem without Scene Monitor running, because I make no guarantee that SM doesn't interfere in some way. Just a thought. ;)