Geometry Batching in JME3

Geometry Batching



Figuring that I probably won’t be the only one with an interest in this I think it’s a good idea to make a single topic for reference and perhaps put it into the documentation.

In any case, I hope I can count on a few elaborate answers, and perhaps more questions, to get this topic clear.



So, to the point.

I (think I) like to use this technique in JME3 to do something similar as bgilb did in JME2. Basically I like to draw as many simple objects as possible per second, with several different configurations. Cubes, as in bgilb’s example will do for this topic.



In a nutshell, in layman’s terms (as I understand):

Videocards have a tough time rendering your playworld if you just put your gazillion simple objects in them. Stuff has to be written to the video memory for each object and that is slow and cpu intensive. They are much faster with a few more complex shapes.

How does Geometry Batching help here? It helps by bunching the meshes of your hordes of simple objects into one batch, and sending that batch in one go to the video card. As it happens video cards don’t break a sweat over drawing triangles.



To get a bit of deeper knowledge I have done some reading on the subject

nVidia GPU Gems 2 - Chapter 3. Inside Geometry Instancing

nVidia performance slides on batching

Geometry Batching topic on gamedev.net

bgilb’s example code for JME2

but nothing specific to JME3 except rickard’s JME2->3 class conversion.





So here are my questions sofar. I hope they can be answered or discussed. I hope also that this will end up with some practical example code in some way. In any case, my thanks for helping out.



  • [1] Is there Geometry Batching support in JME 3? If so, is there an example and perhaps documentation? If not, is support planned?

  • [2] Can I mix cubes with different textures in a Geometry Batch?

  • [3] Can I mix different (simple) shapes in a Geometry Batch?

  • [4] If I like to alter the texture or shape, would I need to entirely rebuild the batch?

  • [5] What about lighting? Is the default lighting shader usable? How would that work?

  • [6] Shadows, are those possible?

  • [7] Physics? How would you go about using that, if at all possible. Say, in a cube world you cannot add a physics node to every cube. Would physics work at all with Geometry Batching, even with a low number of cubes?

  • [8] How would one go about selecting an object that's in the batch? Can you still ray cast and get some object handle back?

[1] No

[2] You will need to have different geometries/meshes for every unique material

[3] Yes

[4] If you need to change the shape of one instance, you only modify that instance. If you need to change the shape of a mesh that you used in many instances, you need to change all the instances.

[5] Any material-related things should work fine. Geometry batching only concerns itself with the mesh, not the material

[6] Yes

[7] You will need to regenerate the collision mesh/shape for physics each time the batch changes. You would want to organize your batches in such way that you wouldn’t change them often.

[8] The selection gives you a geometry object and a triangle index. If you have a table of all instances you added and their triangle index ranges, you can use that to look up the specific instance that you selected.

[7] You will need to regenerate the collision mesh/shape for physics each time the batch changes. You would want to organize your batches in such way that you wouldn’t change them often.

But you cannot use a few thousand physicsNode cubes because physics is expensive. Or am I to understand that the entire batch gets 1 physicsnode as a whole? If so, is there still a method to determine which specific shape was impacted on a collision?

[8] The selection gives you a geometry object and a triangle index. If you have a table of all instances you added and their triangle index ranges, you can use that to look up the specific instance that you selected.

That is still by using Ray casting?
durandal said:
But you cannot use a few thousand physicsNode cubes because physics is expensive. Or am I to understand that the entire batch gets 1 physicsnode as a whole? If so, is there still a method to determine which specific shape was impacted on a collision?

That is still by using Ray casting?

The entire batch is a PhysicsNode. I think you'll have to ask normen as to how handle that part best, and if it is possible to determine which shape was hit.

I have a situation where I require batching to be done as well, on a lot of boxes.

Does anyone know how I can achieve this? Currently I instantiate a lot of Box instances and just place them in the positions that I would like and it is incredibly slow.



Also, no chance support is planned for batching now is there?



I found some code with an example of batching, but I cannot find the make the imports required to run the code.

import com.jme3.scene.BatchedGeometry;

import com.jme3.scene.GeometryBatch;



Code found here:

http://code.google.com/p/jmonkeyengine/source/browse/trunk/engine/src/test/jme3test/batching/?r=8128

Yes there is batching, and it works, but they are in the BatchNode and SimpleBatchNode classes (in com.jme3.scene). I use jme3 stable and they’re there.



http://code.google.com/p/jmonkeyengine/source/browse/trunk/engine/src/test/jme3test/batching/TestBatchNode.java?spec=svn8500&r=8500

Thanks androlo, that’s really helpful. Cheers!

@dengkesha

you can create all those examples from jMP also from “new project” → JME3 → JME tests

1 Like

For static scenes its best to use the GeometryBatchFactory.

1 Like
@androlo said:
Yes there is batching, and it works, but they are in the BatchNode and SimpleBatchNode classes (in com.jme3.scene). I use jme3 stable and they're there.

http://code.google.com/p/jmonkeyengine/source/browse/trunk/engine/src/test/jme3test/batching/TestBatchNode.java?spec=svn8500&r=8500

BatchNode and SimpleBatchNode are still WIP.
For now you can't change the material to either the batchNode or the sub geometries
Also, batching and unbatching is slow, and will be optimized
1 Like

anyone having issues with BatchNode after detaching and then reattaching a child to the batch? I Detach a child change the tex coords of it (on a atlas file) and reattach it , it works, but the colors of the child flicker…





I used the code below:

[java] public void changeColorRGBA(ColorRGBA convertedColor, String id) {



Spatial spatialToColor = myApp.getRootNode().getChild(id);



Geometry geom = (Geometry) spatialToColor;



geom.removeFromParent();

batch.detachChild(geom);

//batch.detachChildAt(batch.getChildIndex(spatialToColor));



Vector2f[] texCoord = new Vector2f[geom.getMesh().getVertexCount()];



// this should be brown

float xTexel = 0.1f; // the x coord in your atlas texture

float yTexel = 0.4f; // the y coord in your atlas texture



for(int i=0; i<texCoord.length; i++) {

texCoord = new Vector2f(xTexel, yTexel);

}



System.out.println("changing color of one box");



geom.getMesh().clearBuffer(Type.TexCoord);

geom.getMesh().setBuffer(Type.TexCoord, 2, BufferUtils.createFloatBuffer(texCoord));

geom.getMesh().updateBound();



batch.attachChild(geom);



}

[/java]

you have to call batch() after each attach or remove operation.



Also you don’t have to detach and reattach the geom to do this, you can edit directly the batch mesh.

you have a batchNode.getOffsetIndex(geometry) that return the index of the first vertex of this geometry in the batched mesh.

3 Likes

@homsi you better post a damn video of that thing when it’s done and working. :stuck_out_tongue:

@nehon but if I update my geom vertex without detaching nothing happens :frowning:



@madjack I will once I get it running :slight_smile: its gonna be LEGEN…wait for it



am I doing it wrong?



[java] public void changeColorRGBA(String id) {



Spatial spatialToColor = myApp.getRootNode().getChild(id);



Geometry geom = (Geometry) spatialToColor;

Vector2f[] texCoord = new Vector2f[geom.getMesh().getVertexCount()];



// this should be brown

float xTexel = 0.1f; // the x coord in your atlas texture

float yTexel = 0.4f; // the y coord in your atlas texture



for(int i=batch.getOffsetIndex(geom); i<(batch.getOffsetIndex(geom) + texCoord.length); i++) {

texCoord[ batch.getOffsetIndex(geom)] = new Vector2f(xTexel, yTexel);

}

System.out.println(“changing color of one box”);



geom.getMesh().clearBuffer(Type.TexCoord);

geom.getMesh().setBuffer(Type.TexCoord, 2, BufferUtils.createFloatBuffer(texCoord));

geom.getMesh().updateBound();



batch.batch();

[/java]





edit: and if I detachChild() - batch()- changecolor - attach() - batch() all the batch turns to the new color

is that a new feature? there’s no documentation on it. could someone please explain more?



[java] batchNode.getOffsetIndex(geometry) that return the index of the first vertex of this geometry in the batched mesh.[/java]



I am assuming if it returns the 1st vertex I change the color on that vertex + the rest of them ( + geom vertex count() ) but nothing happens. whats missing here?

As there is the getOffsetIndex method, it seems it might be the better approach to use that to affect the batched geometry directly.

However, in your code above it seems you’re getting a geometry from the root node, are you sure that is the geometry created by BatchNode and not your own created geometry?

When using the getOffsetIndex way, you need to make the changes on the batched geometry, not the original ones (they’re not visible).

The debugger and System.out.println would be a good friend here.

BatchNode seems to name the batched geometries (one per material) after the following pattern:

[java]batch.geometry = new Geometry(name + “-batch” + batches.size());[/java] (where name is the name of the BatchNode)

Make sure you make the changes on that geometry.

BatchNode creates Batch geometries resulting of the merge of it’s sub geometries.

There is one batch geometry per material.

the batch geometries are named <batchNode name>-batch<batchnumber>.

If you have only one material (which i recommend) and that you call you batchNode “batchNode”, the batch geometry will be called “batchNode-batch0”.

That’s this geometry you have to fetch and change its texture coordinates buffer.



this is pseudo code

[java]

void changeColor(Geometry geom){

Geometry g = (Geometry) batchNode.getChild(“batchNode-batch0”);





do your tex coordinate thing like before but instead of using a Vector2f array use a float array of vartCount * 2 size. (let’s call it “texCoordsFloat”)



//getting the texture coordinate buffer of the batch geom

VertexBuffer buff = g.getMesh().getBuffer(Type.TexCoord);

//getting the FloatBuffer of this vertex buffer

FloatBuffer fb = (FloatBuffer) buff.getData();

fb.rewind();

fb.position(.getOffsetIndex(geometry);

fb.put(texCoordsFloat);

buff.updateData(fb);

g.getMesh().updateBound();



}

[/java]

This might need some tweaking but it’s roughly what you should do.

2 Likes

good discussion!



I’d be interested to know how would you be able to tell which batch geom belongs to which spatial. So let’s say we want to change the color of spatial_3 already batched , what would be the batch # for this spatial?



[java]Geometry g = (Geometry) batchNode.getChild(“batchNode-batch0”);[/java]



0 is always the same batch meaning the same object.

I think like this:



[java] int index = spatialToColor.getParent().getChildIndex(spatialToColor);[/java] double check it though



I am writing the function that does what @nehon said. Great pseudocode btw I understand the concept perfectly now.

I guess I was wrong :S I am trying the one above or this one:

[java]

Spatial spatialToColor = myApp.getRootNode().getChild(id);

int theIndex = Integer.parseInt(id.replaceAll("[\D]", ""));[/java]



which takes the id of the spatial and sets it as the index of the batch geom, I thought they were the same but no :S anyone knows how to get the index of the batch geom of a given spatial?



I got it working except the wrong shelves are getting colored :frowning:



update:

I don’t get it. Why do I have many batch geoms? I have only one material (w/ texture atlas file)

@nehon said:
If you have only one material (which i recommend) and that you call you batchNode “batchNode”, the batch geometry will be called “batchNode-batch0″.