Does JME have the ability to group small objects into on renderable object?

Did you rewind() them before use like I said? Check how many bytes are actually copied into the buffer that you create. somefloatbuffer.put(FloatBuffer mybuffer) is not just relative for somefloatbuffer, but also for mybuffer, so keep that in mind!

I did but I'm doing something very wrong. Time to start again from scratch and re-think what I'm doing.

I haven't had much time to work on this, but I'm still getting a hard crash at runtime because I'm doing something stupid. I've been staring at the same bit of code that I'm going code-blind.


/*
 * GeometryBatch.java
 *
 * Created on October 30, 2005, 7:20 PM
 *
 * To change this template, choose Tools | Options and locate the template under
 * the Source Creation and Management node. Right-click the template and choose
 * Open. You can then make changes to the template in the Source Editor.
 */
import com.jme.scene.TriMesh;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.ByteBuffer;

/**
 *
 * @author mike
 */
public class GeometryBatch extends TriMesh {

You might consider dumping all of those dupe methods that are already in and proved correct in our BufferUtils class.  Specifically your creation methods for IntBuffer and FloatBuffer.  I notice you do not specify endian for either, which will give unexpected results on some platforms in certain situations.



Finally, if you give us a test class to run against this code, that would help.

Oops - wasn't aware BufferUtils was available for me. I'll try that out and see if I can prepare a test program if it still doesn't work. Thanks

I forgot the crash:


Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
J  org.lwjgl.opengl.GL11.nglDrawElements(IIILjava/nio/Buffer;IJ)V
J  org.lwjgl.opengl.GL11.glDrawElements(ILjava/nio/IntBuffer;)V
J  com.jme.renderer.lwjgl.LWJGLRenderer.draw(Lcom/jme/scene/TriMesh;)V
J  com.jme.scene.TriMesh.draw(Lcom/jme/renderer/Renderer;)V
J  com.jme.renderer.RenderQueue.renderOpaqueBucket()V
v  ~RuntimeStub::alignment_frame_return Runtime1 stub
j  com.jme.renderer.RenderQueue.renderBuckets()V+1
j  com.jme.renderer.Renderer.renderQueue()V+9
j  com.jme.renderer.lwjgl.LWJGLRenderer.displayBackBuffer()V+1
j  com.jme.app.BaseGame.start()V+63
j  OldLevelLoader.main([Ljava/lang/String;)V+14
v  ~StubRoutines::call_stub

Uhm just a quick glance I see:



indices = GeometryBatch.createIntBuffer(total);



and total being defined:



total = this.getTextureBuffer().capacity() + mesh.getTextureBuffer().capacity(); 



Doesn't seem right.

Oops - I fixed that just after I posted here but I forgot to update the post. Thanks

Ooh one step closer. I can add multiple meshes though the indices aren't quite right. Still it runs and renders some things. Not sure if anyone's really interested but I intend to release the code to the project when I get it working. The code so far:


/*
 * GeometryBatch.java
 *
 * Created on October 30, 2005, 7:20 PM
 *
 * To change this template, choose Tools | Options and locate the template under
 * the Source Creation and Management node. Right-click the template and choose
 * Open. You can then make changes to the template in the Source Editor.
 */
import com.jme.scene.TriMesh;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.ByteBuffer;

import com.jme.util.geom.BufferUtils;

/**
 *
 * @author mike
 */
public class GeometryBatch extends TriMesh {
   
    /** Creates a new instance of GeometryBatch */
    public GeometryBatch() {
        FloatBuffer vertices;
        FloatBuffer normal;
        FloatBuffer color;
        FloatBuffer texture;
        IntBuffer indices;

        vertices = BufferUtils.createVector3Buffer(0);
        normal = BufferUtils.createFloatBuffer(0);
        color = BufferUtils.createColorBuffer(0);
        texture = BufferUtils.createFloatBuffer(0);
        indices = BufferUtils.createIntBuffer(0);
        reconstruct(vertices, normal, color, texture, indices);
    }
   
    public static GeometryBatch create(String name, TriMesh mesh)
    {
        return new GeometryBatch(name, mesh);
    }
   
    /** Creates a new instance of GeometryBatch */
    public GeometryBatch(String name, TriMesh mesh) {
   super(name);
       
        FloatBuffer vertices = mesh.getVertexBuffer();
        FloatBuffer normal = mesh.getNormalBuffer();
        FloatBuffer color = mesh.getColorBuffer();
        FloatBuffer texture = mesh.getTextureBuffer();
        IntBuffer indices = mesh.getIndexBuffer();
       
        dumpIntBuffer(mesh.getIndexBuffer(), "New index buffer");
      
        reconstruct(vertices, normal, color, texture, indices);
    }
   
    public static void dumpFloatBuffer(FloatBuffer buff, String title)
    {
        System.out.println("********** Start FloatBuffer " + title);
        int cap = buff.capacity();
        buff.rewind();
       
        System.out.println();
       
        for (int ii=0; ii<cap; ii++)
        {
            System.out.println(buff.get());
        }
        System.out.println();
        System.out.println("********** End FloatBuffer **********");

        buff.rewind();
    }
   
    public static void dumpIntBuffer(IntBuffer buff, String title)
    {
        System.out.println("********** Start IntBuffer " + title);
        int cap = buff.capacity();
        buff.rewind();
       
        System.out.println();
       
        for (int ii=0; ii<cap; ii++)
        {
            System.out.println(Integer.toString(ii) + " " + Integer.toString(buff.get()));
        }
        System.out.println();
        System.out.println("********** End IntBuffer **********");
       
        buff.rewind();
    }
   
    public void newAddMesh(TriMesh mesh)
    {
        FloatBuffer color = null;
   
    }
   
    public void addMesh(TriMesh mesh)
    {
        System.out.println("Adding mesh " +
                Integer.toString(mesh.getVertexBuffer().capacity()));

        // Append the indices.
        int total = this.getIndexBuffer().capacity() + mesh.getIndexBuffer().capacity();
        IntBuffer indices = BufferUtils.createIntBuffer(total);
        IntBuffer buff = this.getIndexBuffer();
        buff.rewind();
        for (int ii=0; ii<buff.capacity(); ii++)
        {
            indices.put(buff.get());
        }

        int extra = this.getIndexBuffer().capacity();
        buff = mesh.getIndexBuffer();
        buff.rewind();
        for (int ii=0; ii<buff.capacity(); ii++)
        {
            indices.put(buff.get() + extra);
        }
       
        // Append the normals.
        total = this.getNormalBuffer().capacity() + mesh.getNormalBuffer().capacity();
        FloatBuffer normal = BufferUtils.createFloatBuffer(total);
        FloatBuffer buff2 = this.getNormalBuffer();
        buff2.rewind();
        for (int ii=0; ii<buff2.capacity(); ii++)
        {
            normal.put(buff2.get());
        }
       
        buff2 = mesh.getNormalBuffer();
        buff2.rewind();
        for (int ii=0; ii<buff2.capacity(); ii++)
        {
            normal.put(buff2.get());
        }
       
        // Append the normals.
        total = this.getTextureBuffer().capacity() + mesh.getTextureBuffer().capacity();
        FloatBuffer texture = BufferUtils.createFloatBuffer(total);
        buff2 = this.getTextureBuffer();
        buff2.rewind();
        for (int ii=0; ii<buff2.capacity(); ii++)
        {
            texture.put(buff2.get());
        }
       
        buff2 = mesh.getTextureBuffer();
        buff2.rewind();
        for (int ii=0; ii<buff2.capacity(); ii++)
        {
            texture.put(buff2.get());
        }
        
        // Append the vertices.
        total = this.getVertexBuffer().capacity() + mesh.getVertexBuffer().capacity();
        FloatBuffer vertices = BufferUtils.createFloatBuffer(total);
        buff2 = this.getVertexBuffer();
        buff2.rewind();
        for (int ii=0; ii<buff2.capacity(); ii++)
        {
            vertices.put(buff2.get());
        }
       
        buff2 = mesh.getVertexBuffer();
        buff2.rewind();
        for (int ii=0; ii<buff2.capacity(); ii++)
        {
            vertices.put(buff2.get());
        }
      
        FloatBuffer color = BufferUtils.createFloatBuffer(0);

        reconstruct(vertices, normal, color, texture, indices);
    }
}



The code to use it:

        Sphere s3 = new Sphere("Sphere2", 30, 30, 25);
        s3.setLocalTranslation(new Vector3f(50, 0, 50));
        s3.setModelBound(new BoundingBox());
        s3.updateModelBound();

        Box b = new Box("Sphere1", new Vector3f(0, 0, 0), 30, 30, 25);
        b.setLocalTranslation(new Vector3f(0, 250, 0));
        b.setModelBound(new BoundingBox());
        b.updateModelBound();

        GeometryBatch geom = GeometryBatch.create("Fred", b);
        geom.setLocalTranslation(new Vector3f(0, 250, 0));
        geom.setModelBound(new BoundingBox());
        geom.updateModelBound();
        rootNode.attachChild(geom);
       
        Box b2 = new Box("Box23", new Vector3f(0, 0, 0), 65, 15, 15);
        b2.setLocalTranslation(new Vector3f(50, 250, 50));
        b2.setModelBound(new BoundingBox());
        b2.updateModelBound();
        geom.addMesh(b2);
        geom.addMesh(s3);

As I said before local translation has no influence on the vertices, so it has no influence on merging them either. So the objects you merge still have the same center, so don't be surprised if they appear to be mangled together or something like that. Also you can just pass "null" for the colorbuffer if you don't want to use it. (you can setDefaultColor(); to give it some color without using a buffer if you want). If you change this and still have problems, I'll take a look at here.



Btw. I told you about bufferutils in the same post I explained native buffer. I neglected to mention ByteOrder though, so sorry about that.



Also, I think if you get this working and improve the workings some (ability to merge multiple meshes at once, take into account any Buffer can be null, etc.) I'd be very useful for some people. You can put it in the user code wiki, or it might even make it into jME itself.

Yes I realise that the translation has no effect - I haven't removed those lines. I intend to apply the translation when you add the mesh, e.g.:



addMesh(TriMesh mesh, Vector3f translation);



Well hopefully I can get this working and I'll worry about where it goes later. If it goes into JME, then awesome. If it goes on the wiki, awesome. It's a win-win!  :smiley:

So this is where I’m at:







One of the buffers must be wrong. I’m combing the code to spot my mistake…

Another quick look, you're increasing the values of the indices of the second buffer by the amount of indices present in the original mesh. However, indices point to vertices not… indices. So change 

int extra = this.getIndexBuffer().capacity();


to

 int extra = this.getVertexBuffer().capacity();

This is driving me nuts now. Here's the current code to use the GeometryBatch class:


        Box b = new Box("Sphere1", new Vector3f(0, 0, 0), 4, 4, 4);
        Box b2 = new Box("Box23", new Vector3f(0, 0, 0), 15, 15, 15);
        Box b3 = new Box("Box23", new Vector3f(0, 0, 0), 26, 26, 26);

        GeometryBatch geom = GeometryBatch.create("Fred", b);
        geom.setLocalTranslation(new Vector3f(0, 250, 0));
        geom.setModelBound(new BoundingBox());
        geom.updateModelBound();
        geom.addMesh(b2);
        geom.addMesh(b3);
      
        rootNode.attachChild(geom);



And the current code is:

/*
 * GeometryBatch.java
 *
 * Created on October 30, 2005, 7:20 PM
 *
 * To change this template, choose Tools | Options and locate the template under
 * the Source Creation and Management node. Right-click the template and choose
 * Open. You can then make changes to the template in the Source Editor.
 */
import com.jme.scene.TriMesh;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.ByteBuffer;

import com.jme.util.geom.BufferUtils;

/**
 *
 * @author mike
 */
public class GeometryBatch extends TriMesh {
   
    /** Creates a new instance of GeometryBatch */
    public GeometryBatch() {
        FloatBuffer vertices;
        FloatBuffer normal;
        FloatBuffer color;
        FloatBuffer texture;
        IntBuffer indices;

        vertices = BufferUtils.createFloatBuffer(0);
        normal = BufferUtils.createFloatBuffer(0);
        color = BufferUtils.createColorBuffer(0);
        texture = BufferUtils.createFloatBuffer(0);
        indices = BufferUtils.createIntBuffer(0);
        reconstruct(vertices, normal, color, texture, indices);
    }
   
    public static GeometryBatch create(String name, TriMesh mesh)
    {
        return new GeometryBatch(name, mesh);
    }
   
    /** Creates a new instance of GeometryBatch */
    public GeometryBatch(String name, TriMesh mesh) {
   super(name);
       
        FloatBuffer vertices = BufferUtils.clone(mesh.getVertexBuffer());
        FloatBuffer normal = BufferUtils.clone(mesh.getNormalBuffer());
        FloatBuffer color = BufferUtils.clone(mesh.getColorBuffer());
        FloatBuffer texture = BufferUtils.clone(mesh.getTextureBuffer());
        IntBuffer indices = BufferUtils.clone(mesh.getIndexBuffer());
       
        System.out.println("GeometryBatch::GeometryBatch - Index count " +
                   Integer.toString(indices.capacity()));
        System.out.println("GeometryBatch::GeometryBatch - Vertex count " +
                   Integer.toString(vertices.capacity()));
        System.out.println("GeometryBatch::GeometryBatch - Normal count " +
                   Integer.toString(normal.capacity()));
        System.out.println("GeometryBatch::GeometryBatch - Texture count " +
                   Integer.toString(texture.capacity()));

        GeometryBatch.dumpIntBuffer(indices, "Original");
        System.out.println("");

        reconstruct(vertices, normal, color, texture, indices);
    }
   
    public static void dumpFloatBuffer(FloatBuffer buff, String title)
    {
        System.out.println("********** Start FloatBuffer " + title);
        int cap = buff.capacity();
        buff.rewind();
       
        System.out.println();
       
        for (int ii=0; ii<cap; ii++)
        {
            System.out.println(buff.get());
        }
        System.out.println();
        System.out.println("********** End FloatBuffer **********");

        buff.rewind();
    }
   
    public static void dumpIntBuffer(IntBuffer buff, String title)
    {
        System.out.println("********** Start IntBuffer " + title);
        int cap = buff.capacity();
        buff.rewind();
       
        System.out.println();
       
        for (int ii=0; ii<cap; ii++)
        {
            System.out.println(Integer.toString(ii) + " " + Integer.toString(buff.get()));
        }
        System.out.println();
        System.out.println("********** End IntBuffer **********");
       
        buff.rewind();
    }
   
    public void newAddMesh(TriMesh mesh)
    {
        FloatBuffer color = null;
   
    }
   
    public void addMesh(TriMesh mesh)
    {
        System.out.println("GeometryBatch::addMesh - adding index count " +
                   Integer.toString(mesh.getIndexBuffer().capacity()));
        System.out.println("GeometryBatch::addMesh - adding vertex count " +
                   Integer.toString(mesh.getVertexBuffer().capacity()));
        System.out.println("GeometryBatch::addMesh - adding normal count " +
                   Integer.toString(mesh.getNormalBuffer().capacity()));
        System.out.println("GeometryBatch::addMesh - adding texture count " +
                   Integer.toString(mesh.getTextureBuffer().capacity()));
        System.out.println("");

        // Append the indices.
        int total = this.getIndexBuffer().capacity() + mesh.getIndexBuffer().capacity();
        IntBuffer indices = BufferUtils.createIntBuffer(total);
        IntBuffer buff = this.getIndexBuffer();
        buff.rewind();
        for (int ii=0; ii<buff.capacity(); ii++)
        {
            indices.put(buff.get());
        }

        int extra = this.getVertexBuffer().capacity();
        buff = mesh.getIndexBuffer();
        buff.rewind();
        for (int ii=0; ii<buff.capacity(); ii++)
        {
            indices.put(buff.get() + extra);
        }
       
        // Append the normals.
        total = this.getNormalBuffer().capacity() + mesh.getNormalBuffer().capacity();
        FloatBuffer normal = BufferUtils.createFloatBuffer(total);
        FloatBuffer buff2 = this.getNormalBuffer();
        buff2.rewind();
        for (int ii=0; ii<buff2.capacity(); ii++)
        {
            normal.put(buff2.get());
        }
       
        buff2 = mesh.getNormalBuffer();
        buff2.rewind();
        for (int ii=0; ii<buff2.capacity(); ii++)
        {
            normal.put(buff2.get());
        }
       
        // Append the texture coords.
        total = this.getTextureBuffer().capacity() + mesh.getTextureBuffer().capacity();
        FloatBuffer texture = BufferUtils.createFloatBuffer(total);
        buff2 = this.getTextureBuffer();
        buff2.rewind();
        for (int ii=0; ii<buff2.capacity(); ii++)
        {
            texture.put(buff2.get());
        }
       
        buff2 = mesh.getTextureBuffer();
        buff2.rewind();
        for (int ii=0; ii<buff2.capacity(); ii++)
        {
            texture.put(buff2.get());
        }
        
        // Append the vertices.
        total = this.getVertexBuffer().capacity() + mesh.getVertexBuffer().capacity();
        FloatBuffer vertices = BufferUtils.createFloatBuffer(total);
        buff2 = this.getVertexBuffer();
        buff2.rewind();
        for (int ii=0; ii<buff2.capacity(); ii++)
        {
            vertices.put(buff2.get());
        }
       
        buff2 = mesh.getVertexBuffer();
        buff2.rewind();
        for (int ii=0; ii<buff2.capacity(); ii++)
        {
            vertices.put(buff2.get());
        }
      
        FloatBuffer color = BufferUtils.createColorBuffer(0);

        reconstruct(vertices, normal, color, texture, indices);
        System.out.println("GeometryBatch::addMesh - Index count " +
                   Integer.toString(indices.capacity()));
        System.out.println("GeometryBatch::addMesh - Vertex count " +
                   Integer.toString(vertices.capacity()));
        System.out.println("GeometryBatch::addMesh - Normal count " +
                   Integer.toString(normal.capacity()));
        System.out.println("GeometryBatch::addMesh - Texture count " +
                   Integer.toString(texture.capacity()));  
   

        GeometryBatch.dumpIntBuffer(indices, "Adding");
    }
   
    public String toString()
    {
        return super.toString();
    }
}



If you want to try it out, just create a class GeometryBatch with this class and add the top chunk of code to any working JME app.

I think it has vertices for 3 cubes but they're possibly all being drawn over each other. But I can't see why that should be.

Alright another small mistake by me. You should divide the capicity of the VertexBuffer you use to increase the indices by 3. Because there's 3 floats for each vertice. It should work then.

Oho! Now you're talking! I think that may just have fixed it. Thanks!

Ok first working version:



http://www.jmonkeyengine.com/wiki/doku.php?id=geometrybatch

OK. Some tips if you want to do further implementation. Other buffers (like the normal buffer, texture coordinates) can be null too. And I think you'd get the most performance if you can merge more than two object at once (I assume you want to use this for more than just putting two boxes together)

Yeah I want to do more than just boxes. I could pass a Vector of objects to add and I'm sure there are other things to add. It's a starting point and a learning excercise for me.

I wonder, if you could detect the areas inside the model at runtime (the internal overlap caused by two models intersecting) and remove them, then attach adjoining points together to create a single model, that would be pretty neat. And if you can do additive grouping, why not subtractive grouping where overlapping areas are destroyed. That might be handy to, for example, modify a cliff-face to contain a cave by putting an invisible model of the cave where you want it and then substracting it from the cliff-face. You could possibly use this to modify generated terrain rather than tacking a ready-made cliff-face-with-cave model onto it. That would save a lot of effort. Just a thought.