Merging imported meshes with multiple textures

Hi,



When I load a 3ds model into .jme a new mesh is created for every material on the original model. I’m trying tor reduce the mesh count in my scene and therefore want to tackle this mesh creation. I’ve managed to merge all the vertices etc into a single mesh, but am now walking into the texturing.



Is there a way to multitexture via a single texturestate in such a way that no creation of different meshes is needed? I know multitexturing is possible, but I don’t know how to contain the placing of the different textures.



Below is an image of where I’m at now. The left house is the original model. The right (funky) house is the current state of the merged mesh.







This is the code I’m using to merge the meshes:



import java.nio.FloatBuffer;
import java.nio.IntBuffer;

import com.jme.scene.TriMesh;
import com.jme.scene.batch.TriangleBatch;
import com.jme.util.geom.BufferUtils;

/**
 * @author jeroenwarmerdam
 *
 */
public class GroupedTriMesh extends TriMesh {

    /**
     *
     */
    private static final long serialVersionUID = 6145780807264913273L;

    /**
     *
     */
    public GroupedTriMesh() {
    }

    public GroupedTriMesh(TriMesh baseMesh) {
        this(baseMesh.getName() + "_group");

        add(baseMesh);
    }

    /**
     * @param name
     */
    public GroupedTriMesh(String name) {
        super(name);
    }

    /**
     * @param name
     * @param vertices
     * @param normal
     * @param color
     * @param texture
     * @param indices
     */
    public GroupedTriMesh(String name, FloatBuffer vertices, FloatBuffer normal, FloatBuffer color,
            FloatBuffer texture, IntBuffer indices) {
        super(name, vertices, normal, color, texture, indices);
    }

    public void add(TriMesh mesh) {
        // Try and append the content of the new mesh to this mesh's buffers
        // For now this only works on single-batch meshes
        TriangleBatch thisBatch = this.getBatch(0);
        TriangleBatch thatBatch = mesh.getBatch(0);

        // Make sure both batches exist
        if (thisBatch != null && thatBatch != null) {

            // Start with the vertex buffers
            int vertexCount = this.getVertexCount();
            FloatBuffer newVertexBuffer = mergeBuffers(thisBatch.getVertexBuffer(), thatBatch.getVertexBuffer(), 0);
            thisBatch.setVertexBuffer(newVertexBuffer);

            // Then do the index buffers
            IntBuffer newIndexBuffer = mergeBuffers(thisBatch.getIndexBuffer(), thatBatch.getIndexBuffer(), vertexCount);
            thisBatch.setIndexBuffer(newIndexBuffer);

            // Then do the normal buffers
            FloatBuffer newNormalBuffer = mergeBuffers(thisBatch.getNormalBuffer(), thatBatch.getNormalBuffer(),
                    vertexCount);
            thisBatch.setNormalBuffer(newNormalBuffer);

            // Also do the binormal buffers
            FloatBuffer newBinormalBuffer = mergeBuffers(thisBatch.getBinormalBuffer(), thatBatch.getBinormalBuffer(),
                    vertexCount);
            thisBatch.setBinormalBuffer(newBinormalBuffer);

            // Also do the tangent buffers
            FloatBuffer newTangentBuffer = mergeBuffers(thisBatch.getTangentBuffer(), thatBatch.getTangentBuffer(),
                    vertexCount);
            thisBatch.setTangentBuffer(newTangentBuffer);

            int texOffset = 0;
            // See if we can handle textures too
            System.out.println("Size: " + thatBatch.getTextureBuffers().size() + " Size 2: "
                    + thisBatch.getTextureBuffers().size());
            for (int i = 0; i < thatBatch.getTextureBuffers().size(); i++) {
                FloatBuffer thisTextureBuffer = thisBatch.getTextureBuffer(i);
                FloatBuffer thatTextureBuffer = thatBatch.getTextureBuffer(i);

                // Only progress if that buffer is not null
                if (thatTextureBuffer != null) {
                    if (thisTextureBuffer == null)
                        thisTextureBuffer = BufferUtils.createFloatBuffer(0);

                    int thisTexBufferSize = thisTextureBuffer.capacity();
                    FloatBuffer newTextureBuffer = mergeBuffers(thisTextureBuffer, thatTextureBuffer, texOffset);
                    texOffset += thisTexBufferSize;

                    thisBatch.setTextureBuffer(newTextureBuffer, i);
                }

            }

            // Try and do the color buffers
            FloatBuffer newColorBuffers = mergeBuffers(thisBatch.getColorBuffer(), thatBatch.getColorBuffer(),
                    vertexCount);
            // Only do this if there are colors in either
            if (newColorBuffers.capacity() == 0)
                newColorBuffers = null;
            thisBatch.setColorBuffer(newColorBuffers);
        }
    }

    protected FloatBuffer mergeBuffers(FloatBuffer thisBuffer, FloatBuffer thatBuffer, int offset) {
        // Try and append the buffers
        if (thisBuffer == null) {
            // Create a new buffer
            thisBuffer = BufferUtils.createFloatBuffer(0);
        }
        if (thatBuffer == null) {

            // Create a new buffer
            thatBuffer = BufferUtils.createFloatBuffer(0);
        }
        FloatBuffer newBuffer = BufferUtils.createFloatBuffer(thisBuffer.capacity() + thatBuffer.capacity());
        for (int i = 0; i < thisBuffer.capacity(); i++) {
            newBuffer.put(thisBuffer.get(i));
        }
        for (int i = 0; i < thatBuffer.capacity(); i++) {
            newBuffer.put(thatBuffer.get(i) + offset);
        }
        return newBuffer;
    }

    protected IntBuffer mergeBuffers(IntBuffer thisBuffer, IntBuffer thatBuffer, int offset) {
        // Try and append the buffers
        if (thisBuffer == null) {
            // Create a new buffer
            thisBuffer = BufferUtils.createIntBuffer(0);
        }
        if (thatBuffer == null) {

            // Create a new buffer
            thatBuffer = BufferUtils.createIntBuffer(0);
        }
        IntBuffer newBuffer = BufferUtils.createIntBuffer(thisBuffer.capacity() + thatBuffer.capacity());
        for (int i = 0; i < thisBuffer.capacity(); i++) {
            newBuffer.put(thisBuffer.get(i));
        }
        for (int i = 0; i < thatBuffer.capacity(); i++) {
            newBuffer.put(thatBuffer.get(i) + offset);
        }
        return newBuffer;
    }

}



And here is how I apply it via a Node


/**
 *
 */

import com.jme.image.Texture;
import com.jme.math.FastMath;
import com.jme.math.Vector3f;
import com.jme.scene.Node;
import com.jme.scene.Spatial;
import com.jme.scene.TriMesh;
import com.jme.scene.shape.Quad;
import com.jme.scene.state.TextureState;
import com.jme.system.DisplaySystem;

/**
 * @author jeroenwarmerdam
 *
 */
public class GroupedNode extends Node {

    protected GroupedTriMesh groupedMesh;
    protected TextureState tState;

    /**
     *
     */
    public GroupedNode() {
    }

    /**
     * @param name
     */
    public GroupedNode(String name) {
        super(name);
    }

    /**
     * @param name
     */
    public GroupedNode(Node node) {
        super(node.getName());

        groupedMesh = new GroupedTriMesh(node.getName() + "_groupedmesh");
        mergeContents(node);
        this.attachChild(groupedMesh);

        tState.apply();
        groupedMesh.setRenderState(tState);
        groupedMesh.updateRenderState();
    }

    /**
     *
     */
    private void mergeContents(Node node) {

        TextureState nodeTState = (TextureState) node.getRenderState(TextureState.RS_TEXTURE);
        if (nodeTState != null) {
            if (tState == null) {
                tState = DisplaySystem.getDisplaySystem().getRenderer().createTextureState();
            }
            tState.setTexture(nodeTState.getTexture(), tState.getNumberOfSetTextures());

        }
        if (node.getChildren() != null) {

            for (Spatial child : node.getChildren()) {
                if ((child.getType() & Spatial.NODE) != 0) {
                    mergeContents((Node) child);
                } else if ((child.getType() & Spatial.TRIMESH) != 0) {
                    TextureState childTState = (TextureState) child.getRenderState(TextureState.RS_TEXTURE);
                    if (childTState != null) {
                        if (tState == null) {
                            tState = DisplaySystem.getDisplaySystem().getRenderer().createTextureState();
                        }
                        tState.setTexture(childTState.getTexture(), tState.getNumberOfSetTextures());
                       
//                        Quad quad = new Quad("q", 10, 10);
//                        quad.setLocalTranslation( new Vector3f(10 * tState.getNumberOfSetTextures(), 0, -10));
//                        quad.getLocalRotation().fromAngleAxis(FastMath.DEG_TO_RAD * -90f, Vector3f.UNIT_X);
//                        TextureState qts = DisplaySystem.getDisplaySystem().getRenderer().createTextureState();
//                        qts.setEnabled(true);
//                        qts.setTexture(childTState.getTexture());
//                        quad.setRenderState(qts);
//                        this.attachChild(quad);
//                        quad.updateRenderState();
                    }
                    groupedMesh.add((TriMesh) child);
                } else {
                    System.out.println("WRONG TYPE: " + child.getType());
                }
            }
        }
    }
}