Geometry Batching (previously known as Geometry Instancing)

Hi. I’ve converted the Geometry Instancing classes from JME2 to JME3. They’re suitable for creating batches of low poly geometry like trees or grass to keep the object count down.



package world.geometryinstancing.instance;

[java]/**

  • <code>GeometryBatchCreator</code> is a container class for
  • <code>GeometryInstances</code>.

    *
  • @author Patrik Lindegrén

    */

    public class GeometryBatchCreator {

    protected ArrayList<GeometryInstance> instances;

    private int nVerts;

    private int nIndices;



    public GeometryBatchCreator() {

    instances = new ArrayList<GeometryInstance>(1);

    nVerts = 0;

    nIndices = 0;

    }



    public void clearInstances() {

    instances.clear();

    nVerts = 0;

    nIndices = 0;

    }



    public void addInstance(GeometryInstance geometryInstance) {

    if (geometryInstance == null) {

    return;

    }

    instances.add(geometryInstance);

    nIndices += geometryInstance.getNumIndices();

    nVerts += geometryInstance.getNumVerts();

    }



    public void removeInstance(GeometryInstance geometryInstance) {

    if (instances.remove(geometryInstance)) {

    nIndices -= geometryInstance.getNumIndices();

    nVerts -= geometryInstance.getNumVerts();

    }

    }



    public int getNumVertices() {

    return nVerts;

    }



    public int getNumIndices() {

    return nIndices;

    }



    public ArrayList<GeometryInstance> getInstances() {

    return instances;

    }



    public void commit(Mesh batch) {

    for (GeometryInstance instance : instances) {

    instance.commit(batch);

    }

    }

    }[/java]



    package world.geometryinstancing.instance;

    [java]/**
  • <code>GeometryInstance</code> uses a <code>GeometryInstanceAttributes</code>
  • to define an instance of object in world space.

    *
  • @author Patrik Lindegrén

    */

    public abstract class GeometryInstance<T extends GeometryInstanceAttributes> {

    protected T attributes;



    public abstract void commit(Mesh batch);



    public abstract int getNumIndices();



    public abstract int getNumVerts();



    public GeometryInstance(T attributes) {

    this.attributes = attributes;

    }



    public T getAttributes() {

    return attributes;

    }

    }[/java]



    package world.geometryinstancing.instance;



    [java]/**
  • <code>GeometryInstanceAttributes</code> specifies the attributes for a
  • <code>GeometryInstance</code>.

    *
  • @author Patrik Lindegrén

    */

    public class GeometryInstanceAttributes {

    protected Vector3f translation; // Translation

    protected Vector3f scale; // Scale

    protected Quaternion rotation; // Rotation

    protected Matrix4f mtNormal; // Normal matrix (scale, rotation)

    protected Matrix4f mtWorld; // Local to world matrix (scale, rotation, translation)



    public GeometryInstanceAttributes(Vector3f translation, Vector3f scale,

    Quaternion rotation) {

    this.scale = scale;

    this.rotation = rotation;

    this.translation = translation;

    mtWorld = new Matrix4f();

    mtNormal = new Matrix4f();

    buildMatrices();

    }



    /**
  • Vector used to store and calculate rotation in degrees Not needed when
  • radian rotation is implemented in Matrix4f

    /

    private Vector3f rotationDegrees = new Vector3f();



    /
    * <code>buildMatrices</code> updates the world and rotation matrix */

    public void buildMatrices() {

    // Scale (temporarily use mtWorld as storage)

    mtWorld.loadIdentity();

    mtWorld.m00 = scale.x;

    mtWorld.m11 = scale.y;

    mtWorld.m22 = scale.z;



    // Build rotation matrix (temporarily use mtNormal as storage)

    // rotationDegrees.set(rotation).multLocal(FastMath.RAD_TO_DEG);

    mtNormal.loadIdentity();

    mtNormal.setRotationQuaternion(rotation);

    // mtNormal.angleRotation(rotationDegrees);

    //mtNormal.radianRotation(rotation); // Add a radian rotation function to Matrix4f (requested feature)



    // Build normal matrix (scale * rotation)

    mtNormal.multLocal(mtWorld);



    // Build world matrix (scale * rotation + translation)

    mtWorld.set(mtNormal);

    mtWorld.setTranslation(translation);

    }



    public Vector3f getScale() {

    return scale;

    }



    /**
  • After using the <code>setScale</code> function, user needs to call the
  • <code>buildMatrices</code> function

    *
  • @param scale

    */

    public void setScale(Vector3f scale) {

    this.scale = scale;

    }



    public Vector3f getTranslation() {

    return translation;

    }



    /**
  • After using the <code>setTranslation</code> function, user needs to call
  • the <code>buildMatrices</code> function

    *
  • @param translation

    */

    public void setTranslation(Vector3f translation) {

    this.translation = translation;

    }



    public Quaternion getRotation() {

    return rotation;

    }



    /**
  • After using the <code>setRotation</code> function, user needs to call the
  • <code>buildMatrices</code> function

    *
  • @param rotation

    */

    public void setRotation(Quaternion rotation) {

    this.rotation = rotation;

    }



    public Matrix4f getWorldMatrix() {

    return mtWorld;

    }



    public Matrix4f getNormalMatrix() {

    return mtNormal;

    }

    }[/java]



    package world.geometryinstancing;

    [java]/**
  • <code>GeometryBatchInstance</code> uses a <code>GeometryBatchInstanceAttributes</code>
  • to define an instance of object in world space. Uses TriMesh as source
  • data for the instance, instead of GeomBatch which does not have an index
  • buffer.

    *
  • @author Patrik Lindegrén

    /

    public class GeometryBatchInstance

    extends GeometryInstance<GeometryBatchInstanceAttributes> {

    public Mesh instanceMesh;



    public GeometryBatchInstance(Mesh sourceBatch,

    GeometryBatchInstanceAttributes attributes) {

    super(attributes);

    this.instanceMesh = sourceBatch;

    }



    /
    * Vector used to store and calculate world transformations */

    Vector3f worldVector = new Vector3f();



    /**
  • Uses the instanceAttributes to transform the instanceBatch into world
  • coordinates. The transformed instance mesh is added to the mesh.

    *
  • @param mesh

    */

    public void commit(Mesh mesh) {

    if (mesh == null || instanceMesh == null || getNumVerts() <= 0) {

    return;

    }



    int nVerts = 0;



    // Texture buffers



    //for (int i = 0; i < instanceMesh.getNumLodLevels(); i++) {

    FloatBuffer texBufSrc = instanceMesh.getFloatBuffer(Type.TexCoord);

    FloatBuffer texBufDst = mesh.getFloatBuffer(Type.TexCoord);

    if (texBufSrc != null && texBufDst != null) {

    texBufSrc.rewind();

    texBufDst.put(texBufSrc);

    }

    //}



    // Vertex buffer

    FloatBuffer vertBufSrc = instanceMesh.getFloatBuffer(Type.Position);

    FloatBuffer vertBufDst = mesh.getFloatBuffer(Type.Position);

    if (vertBufSrc != null && vertBufDst != null) {

    vertBufSrc.rewind();



    nVerts = vertBufDst.position() / 3;

    for (int i = 0; i < instanceMesh.getVertexCount(); i++) {

    worldVector.set(vertBufSrc.get(), vertBufSrc.get(),

    vertBufSrc.get());

    attributes.getWorldMatrix().mult(worldVector, worldVector);

    vertBufDst.put(worldVector.x);

    vertBufDst.put(worldVector.y);

    vertBufDst.put(worldVector.z);

    }

    }



    // Color buffer

    FloatBuffer colorBufSrc = instanceMesh.getFloatBuffer(Type.Color);

    FloatBuffer colorBufDst = mesh.getFloatBuffer(Type.Color);

    if (colorBufSrc != null && colorBufDst != null) {

    colorBufSrc.rewind();

    for (int i = 0; i < instanceMesh.getVertexCount(); i++) {

    colorBufDst.put(colorBufSrc.get() * attributes.getColor().r);

    colorBufDst.put(colorBufSrc.get() * attributes.getColor().g);

    colorBufDst.put(colorBufSrc.get() * attributes.getColor().b);

    colorBufDst.put(colorBufSrc.get() * attributes.getColor().a);

    }

    } else if (colorBufDst != null) {

    for (int i = 0; i < instanceMesh.getVertexCount(); i++) {

    colorBufDst.put(attributes.getColor().r);

    colorBufDst.put(attributes.getColor().g);

    colorBufDst.put(attributes.getColor().b);

    colorBufDst.put(attributes.getColor().a);

    }

    }



    // Normal buffer

    FloatBuffer normalBufSrc = instanceMesh.getFloatBuffer(Type.Normal);

    FloatBuffer normalBufDst = mesh.getFloatBuffer(Type.Normal);

    if (normalBufSrc != null && normalBufDst != null) {

    normalBufSrc.rewind();

    for (int i = 0; i < instanceMesh.getVertexCount(); i++) {

    worldVector.set(normalBufSrc.get(), normalBufSrc.get(),

    normalBufSrc.get());

    attributes.getNormalMatrix().mult(worldVector, worldVector);

    worldVector.normalizeLocal();

    normalBufDst.put(worldVector.x);

    normalBufDst.put(worldVector.y);

    normalBufDst.put(worldVector.z);

    }

    }



    // Index buffer

    IndexBuffer indexBufSrc = instanceMesh.getIndexBuffer();

    IndexBuffer indexBufDst = mesh.getIndexBuffer();

    if (indexBufSrc != null && indexBufDst != null) {

    indexBufSrc.getBuffer().rewind();

    for (int i = 0; i < instanceMesh.getIndexBuffer().getBuffer().limit(); i++) {

    indexBufDst.put(indexBufDst.getBuffer().position(), nVerts + indexBufSrc.get(i));

    indexBufDst.getBuffer().position(indexBufDst.getBuffer().position()+1);



    }

    }

    instanceMesh.setBound(new BoundingBox() );

    }



    public int getNumIndices() {

    if (instanceMesh == null) {

    return 0;

    }

    return instanceMesh.getIndexBuffer().size();

    }



    public int getNumVerts() {

    if (instanceMesh == null) {

    return 0;

    }

    return instanceMesh.getVertexCount();

    }

    }[/java]



    package world.geometryinstancing;

    [java]/**
  • <code>GeometryBatchInstanceAttributes</code> specifies the attributes for a
  • <code>GeometryBatchInstance</code>

    *
  • @author Patrik Lindegrén

    /

    public class GeometryBatchInstanceAttributes

    extends GeometryInstanceAttributes {

    protected ColorRGBA color;



    public GeometryBatchInstanceAttributes(Vector3f translation, Vector3f scale,

    Quaternion rotation, ColorRGBA color) {

    super(translation, scale, rotation);

    this.color = color;

    }



    /
    * <code>buildMatrices</code> updates the world and rotation matrix */

    public ColorRGBA getColor() {

    return color;

    }



    public void setColor(ColorRGBA color) {

    this.color = color;

    }

    }[/java]



    Lastly, you’ll need to put this static method somewhere:



    [java]public static void createBatchBuffers(Mesh output, GeometryBatchCreator batch)

    {

    output.setBuffer(VertexBuffer.Type.Index, 1, BufferUtils.createIntBuffer( batch.getNumIndices() ) );

    output.setBuffer(VertexBuffer.Type.Position, 3, BufferUtils.createFloatBuffer( batch.getNumVertices() * 3 ) );

    output.setBuffer(VertexBuffer.Type.Normal, 3, BufferUtils.createFloatBuffer( batch.getNumVertices() * 3 ) );



    output.setBuffer(VertexBuffer.Type.TexCoord, 2, BufferUtils.createFloatBuffer( batch.getNumVertices() * 2 ) );



    output.setBuffer(VertexBuffer.Type.Color, 4, BufferUtils.createFloatBuffer( batch.getNumVertices() * 4 ) );

    output.updateCounts();

    // Commit the instances to the mesh batch

    batch.commit(output);



    }[/java]



    Enjoy.



    Edit: cleaned up imports, changed topic name to ‘Geometry Batching’

Rickard, that looks good. So how do you use it? Do you have an example (perhaps your test code)?



I’d like to use Geometry Batching in JME3. Your code is the only I have found sofar, but since i’m still pretty green on it all, I could use a starter.



Thanks

Certainly. I guess i was going to, but never got around to actually doing it.

Here’s how i currently use it (i use a Random() object for “r”, feel free to replace with Math.random() ):



[java]

Mesh instanceMesh = new Mesh(); // this is the mesh where the batches will be stored

Mesh myMesh = new Mesh(); // ← replace with the mesh you would like to batch

GeometryBatchCreator batch = new GeometryBatchCreator();

// make a for loop that defines how many meshes you want in your batch

for(int i = 0; i < 3; i++){

float scale = 1f;



GeometryBatchInstance instance = new GeometryBatchInstance( myMesh, new GeometryBatchInstanceAttributes(

// Translation

new Vector3f( (r.nextFloat()*tileSize), 0, (r.nextFloat()*tileSize) ), // randomize location.

// Scale

new Vector3f(scale, scale, scale),

// Rotation

new Quaternion().fromAngles( 0, r.nextFloat()FastMath.PI2, 0 ), // rotate according to normal

// Color (not used currently)

new ColorRGBA(0,0,0,1)

)

);



batch.addInstance(instance); // add this mesh instance to the batch

}



MeshCombiner.createBatchBuffers(instanceMesh, batch); // create the actual batch



Geometry g = new Geometry(“treebatch”, instanceMesh);

// add the material of your choice to ‘g’

[/java]

Remember you can batch different meshes if you’d like, as long as they are supposed to have the same material.

In jme2 there was a helper class as well that made it easier to create vegetation for large terrains. I’ll see if i get around to porting it as well.

Thanks, great :slight_smile:

I am going to try this tonight.



But what if I need different materials? (say n number of wooden plants and m number of stone rocks)

Would I need different batches, or is there a way to put them in one batch?

You can put them in the same batch, but they’d still have to use the same material.

One way would be to create a texture where all “vegetation” fits, and uv map the objects so that they can share the same texture. The drawback is the material parameters still have to be the same, but for “plain” texturing" it would work.

Well, correct me if i’m wrong then, but if I have a few (<100) different materials then i can also create a different batch for each material and superimpose the batches on top of eachother. That might work for me.

I was under the impression though that Geometry Batching would allow for different materials (or at least textures) within one batch (without UV mapping from one composed supertexture). Perhaps it’s a limitation of how this implementation works and there is room to tweak. I’m still a bit green on this though I believe I understand most of what the code says.



Have you explored this technique beyond your conversion to JME3? Perhaps in JME2?



Any way, off to try. I’m sure I’ll be back on topic soon :wink:

It cannot work, the material is on the level of the Geometry and not of the Mesh.

There’s one geometry per mesh, so no, using geometry batching won’t help with that. You have to organize your batches in such way that every material has its own batch.

Yes i figured out that the whole deal is nothing more than one adding all your meshes of your simple object into one big mesh. It ends up being one Geometry. The profit is in the ellimination of overhead (CPU doesn’t need to send all the simple objects to the video card) and making use of the GPU’s ability to just draw triangles fast.



I’m trying to get this working, but hit a snag.

[java]public abstract class GeometryInstance {

protected T attributes;



public abstract void commit(Mesh batch);



public abstract int getNumIndices();



public abstract int getNumVerts();



public GeometryInstance(T attributes) {

this.attributes = attributes;

}



public T getAttributes() {

return attributes;

}

}

[/java]

The IDE wants me to create the type T. I’m assuming the idea is to use generics in this class,

[java]public abstract class GeometryInstance {

protected T attributes;[/java]

but when I do that GeometryBatchCreator and GeometryBatchInstance start complaining about incompatible type (creator) and not being able to find the referenced methods (BatchInstance). It looks like GeometryBatchInstance is looking at the base class (GeometryInstanceAttributes) in stead of the extended class (GeometryBatchInstanceAttributes).



ricard, any idea? Could you check against your own code? Sofar i’ve just copy and pasted, and fixed the imports, so if yours doesn’t show errors so shouldn’t mine.





EDIT: I just found out that this forum filters out < and > and anything in between them if you put it in a code block - if you want those you have to use the html codes (& lt ; ) - so code blocks here are pretty useless, even dangerous. (doh!). The code you posted above must have lost all generic tag information.

ricard, any chance i can download the sources somewhere?

You can use the & lt ; method or you can “Insert… Snippet… Add Your Own Snippet”, that method works with special characters etc.

You can use the & lt ; method or you can “Insert.. Snippet.. Add Your Own Snippet”, that method works with special characters etc.

Thanks Normen, I'll try the Snippet method :)


I used the 'quote' links of the posts containing the source code to get to the unmangled source code and build myself an error free application that should display some boxes.

When I start the test I get an exception, and this as logging:
21-nov-2010 0:25:34 com.jme3.renderer.lwjgl.LwjglRenderer updateShaderSourceData
WARNING: Common/MatDefs/Gui/Gui.vert compile error: Vertex shader failed to compile with the following errors:
ERROR: error(#272) Implicit version number 110 not supported by GL3 forward compatible context
ERROR: error(#273) 2 compilation errors. No code generated

21-nov-2010 0:25:34 com.jme3.renderer.lwjgl.LwjglRenderer updateShaderSourceData
WARNING: #define TEXTURE 1
#define VERTEX_COLOR 1
uniform mat4 g_WorldViewProjectionMatrix;
uniform vec4 m_Color;

attribute vec3 inPosition;

#ifdef VERTEX_COLOR
attribute vec4 inColor;
#endif

#ifdef TEXTURE
attribute vec2 inTexCoord;
varying vec2 texCoord;
#endif

varying vec4 color;

void main() {
//vec2 pos = (g_WorldViewProjectionMatrix * inPosition).xy;
//gl_Position = vec4(pos, 0.0, 1.0);
gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0);
#ifdef TEXTURE
texCoord = inTexCoord;
#endif
#ifdef VERTEX_COLOR
color = m_Color * inColor;
#else
color = m_Color;
#endif
}


21-nov-2010 0:25:34 com.jme3.app.Application handleError
SEVERE: Uncaught exception thrown in Thread[LWJGL Renderer Thread,5,main]
java.lang.IllegalStateException: Cannot render mesh without shader bound
at com.jme3.renderer.lwjgl.LwjglRenderer.setVertexAttrib(LwjglRenderer.java:1808)
at com.jme3.renderer.lwjgl.LwjglRenderer.setVertexAttrib(LwjglRenderer.java:1813)
at com.jme3.renderer.lwjgl.LwjglRenderer.renderMeshDefault(LwjglRenderer.java:2012)
at com.jme3.renderer.lwjgl.LwjglRenderer.renderMesh(LwjglRenderer.java:2042)
at com.jme3.material.Material.render(Material.java:699)
at com.jme3.renderer.RenderManager.renderGeometry(RenderManager.java:386)
at com.jme3.renderer.queue.RenderQueue.renderGeometryList(RenderQueue.java:132)
at com.jme3.renderer.queue.RenderQueue.renderQueue(RenderQueue.java:176)
at com.jme3.renderer.RenderManager.renderViewPortQueues(RenderManager.java:546)
at com.jme3.renderer.RenderManager.flushQueue(RenderManager.java:502)
at com.jme3.renderer.RenderManager.renderViewPort(RenderManager.java:664)
at com.jme3.renderer.RenderManager.render(RenderManager.java:687)
at com.jme3.app.SimpleApplication.update(SimpleApplication.java:216)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.runLoop(LwjglAbstractDisplay.java:144)
at com.jme3.system.lwjgl.LwjglDisplay.runLoop(LwjglDisplay.java:141)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:198)
at java.lang.Thread.run(Thread.java:619)
Java Result: -1073741819
BUILD SUCCESSFUL (total time: 3 seconds)

Using openGL2 in stead of 3 seems to work. Any idea why?



So when I said it worked under OpenGL2 that was not entirely true. It works, partly.
[java]public class TestGeoBatch extends SimpleApplication {

// for Geometry Batching, you’ll need to put this static method somewhere
public static void createBatchBuffers(Mesh output, GeometryBatchCreator batch)
{
output.setBuffer(VertexBuffer.Type.Index, 1, BufferUtils.createIntBuffer( batch.getNumIndices() ) );
output.setBuffer(VertexBuffer.Type.Position, 3, BufferUtils.createFloatBuffer( batch.getNumVertices() * 3 ) );
output.setBuffer(VertexBuffer.Type.Normal, 3, BufferUtils.createFloatBuffer( batch.getNumVertices() * 3 ) );

output.setBuffer(VertexBuffer.Type.TexCoord, 2, BufferUtils.createFloatBuffer( batch.getNumVertices() * 2 ) );

output.setBuffer(VertexBuffer.Type.Color, 4, BufferUtils.createFloatBuffer( batch.getNumVertices() * 4 ) );
output.updateCounts();
// Commit the instances to the mesh batch
batch.commit(output);
}

public static void main(String[] args) {
TestGeoBatch app = new TestGeoBatch();
AppSettings settings = new AppSettings(true);
settings.setRenderer(AppSettings.LWJGL_OPENGL2);
settings.setWidth(1024);
settings.setHeight(768);
app.setShowSettings(false);
app.setSettings(settings);
app.start();
}


Material mat;
Mesh boxMesh;

private void init(){
// for now, build a cube and cache it
if(boxMesh == null){
boxMesh = new Box(0.5f,0.5f,0.5f);
TangentBinormalGenerator.generate(boxMesh); // magic line or shadows fail
boxMesh.setStatic();
}
if(mat==null){
mat = new Material(assetManager, "Common/MatDefs/Misc/SimpleTextured.j3md");
mat.setTexture("m_ColorMap", assetManager.loadTexture("Textures/Terrain/Pond/Pond.png"));
}
}


@Override
public void simpleInitApp() {

// set up the mats, meshes and lighting
init();

// steal the mesh from a box
Mesh myMesh = boxMesh.clone(); // the Mesh that will be multiplied. Currently a cube

// ======================================================================================
Mesh instanceMesh = new Mesh(); // this is the mesh where the batches will be stored
GeometryBatchCreator batch = new GeometryBatchCreator();
// make a for loop that defines how many meshes you want in your batch
int size = 10;
for(int z = 0; z < size; z++){
for(int x = 0; x < size; x++){
float scale = 1f;

GeometryBatchInstance instance = new GeometryBatchInstance( myMesh, new GeometryBatchInstanceAttributes(
// Translation
new Vector3f( x, -1, z ),
// Scale
new Vector3f(scale, scale, scale),
// Rotation
new Quaternion().fromAngles( 0, 0, 0 ), // rotate according to normal
// Color (not used currently)
new ColorRGBA(0,0,0,1)
)
);

batch.addInstance(instance); // add this mesh instance to the batch
}
}

createBatchBuffers(instanceMesh, batch); // create the actual batch

Geometry g = new Geometry("treebatch", instanceMesh);
// add the material of your choice to 'g'


// ======================================================================================
g.setMaterial(mat);
TangentBinormalGenerator.generate(g); // magic line or shadows fail

rootNode.attachChild(g);

}
[/java]

It looks good when I start it up (yay!), showing a layer 10x10 cubes. However, once I rotate the camera a bit (or strafe) it all goes black until i look back to the original point.
I thought it had something to do with the TangentBinormalGenerator so I added TangentBinormalGenerator.generate(g) but that didn't help.
When I turn wireframes on, they disappear when rotating the camera too.

Help?

edit:
I just noticed these warnings too, for every cube
21-nov-2010 1:21:14 com.jme3.util.TangentBinormalGenerator processTriangleData
WARNING: Normal and tangent are parallel for vertex 11.
21-nov-2010 1:21:14 com.jme3.util.TangentBinormalGenerator processTriangleData
WARNING: Binormal is flipped for vertex 16.

Could it be camera clipping, or some culling error or perhaps the default box mesh isn't right?

Could this have something to do with the model bound maybe not being updated? So the camera thinks the geometry is outside the frustrum and culls it. Or was there a bug related to this? My batches have been pretty small, so i might not have noticed this.

Could be. More than willing to give it a go. It does look like camera related. Something I should fix locally? (if so, how?)



As for the openGL3 crash; an old test just crashed with a similar message. I didn’t change anything except get the latest nightly build. Should I open a ticket or bug report somewhere? (where?)

I also think the logger on the BinormalGenerator is generic in stead of specific; It seems to only shut off with Logger.getLogger("").setLevel(Level.OFF) which is a bit undesired.

Great, that worked!



adding g.setModelBound(new BoundingBox()) did the trick. Any idea if i need to call updateBound() too?

I am happy for tonight. 32x32x32 (32k) cubes on screen @ 590 fps. Funny fact; if i enable wireframe, that drops to 230ish.



So, off to bed for now. Thanks all for helping out. Probably more questions soon.



If any would have an answer on the openGL3 crash, logger, or how/where to submit bugs that’d be nice.

Don’t use OpenGL3. All shaders have to be ported to it in order for it to be usable.

@rickard



Any tips about modifying the batch after creation?

Currently I’m able to remove or add a GeometryInstance and recreate the mesh, and I’m pretty sure I can calculate the transform (translation + rotation) for each GeometryInstance based on the transform of the Mesh, so to recreate it in the same position (minus or plus some piece), but I wonder if I could “simply” remove or add the right vertices from the buffer and recreate the mesh, leaving the rest of the vertex data intact. That mainly because I’m a little worried of a cumulative error in the floating point calculation for the position of each GeometryInstance when recreating the mesh.

No specific tips, no. Once it’s a batch, it’s a mesh like any other. You’d need to use the float/vertex/index buffers to achieve what you want, unless you can come up with a way to adjust for the calculations before hand, or during batching.

I used these classes to load a MineCraft level into JME3, and it’s working very well.



If I add the created mesh to a PhysicNode everything slows down alot, even with very small maps, and the physics seem to be a little buggy with the Batch. (I cannot get the charackter to move smooth on the surface of the big block i created.)



Any advice?

I guess its not a good idea to do that, physics collision meshes should be closed, one-piece meshes.

So there’s no way i could use this with Physics? This is the first way I could actually render a whole MineCraft level with a FPS Count above 60.