Skip to content
Advertisement

Trying to implement model batching for opengl not sure why it’s not showing anything

I am trying to implement model and sprite batching for a game engine I am working on, trying to test it on GUI Renderer but can’t get any results, errors or anything drawing to the screen am I doing something wrong I will link the Github page for the full code but here is the code for MeshBatch class and the GUIRenderer class I test it on and the Shader

Github page for the full Engine : https://github.com/reddyalice/Melek

public class MeshBatch  implements Comparable<MeshBatch>{

    private final float[] vertices;
    private final float[] textureCoords;
    private final float[] colors;
    private final float[] texID;
    private final float[] normals;
    private final int[] indices;

    private final HashMap<Scene, HashMap<Window, Integer>> ids = new HashMap<>();
    private final HashMap<Scene, Array<Integer>> VBOS = new HashMap<>();
    private final HashMap<Scene, Integer> EBOS = new HashMap<>();

    private final Mesh mesh;
    private final AssetManager assetManager;
    private final int dimension;
    private final int maxElementCount;
    private final HashMap<String, HashMap<Integer, Pair<Element,BatchMaterial>>> elements;
    private final HashMap<String, Integer> textureToIntMap;
    private int numberOfElements = 0;
    private int numberOfTextures = 0;
    private int zIndex;
    private final int textureLimit;
    private boolean hasRoom;


    public MeshBatch(AssetManager assetManager, String meshName, int maxElementCount, int zIndex){

        textureLimit = GL45.glGetInteger(GL45.GL_MAX_TEXTURE_IMAGE_UNITS);
        this.zIndex = zIndex;
        this.maxElementCount = maxElementCount;
        elements = new HashMap<>(textureLimit, 1);
        textureToIntMap = new HashMap<>(textureLimit, 1);
        this.assetManager = assetManager;
        Mesh mesh = assetManager.getMesh(meshName);
        this.mesh = mesh;
        this.dimension = mesh.getDimension();
        vertices = new float[mesh.getVertices().length * maxElementCount];
        textureCoords = new float[mesh.getTextureCoords().length * maxElementCount];
        normals = new float[mesh.getNormals().length * maxElementCount];
        colors = new float[ 4 * maxElementCount];
        texID = new float[maxElementCount];
        indices = new int[mesh.getIndices().length * maxElementCount];


    }


    public void genBatch(Scene scene, Window window){
        if(!VBOS.containsKey(scene))
            VBOS.put(scene, new Array<>());
        if(!ids.containsKey(scene))
            ids.put(scene, new HashMap<>());

        if(VBOS.get(scene).size == 0) {
            int id = GL30.glGenVertexArrays();
            GL30.glBindVertexArray(id);
            storeDataInAttributeList(scene,0, dimension, vertices);
            storeDataInAttributeList(scene,1, 2, textureCoords);
            storeDataInAttributeList(scene,2, 4, colors);
            storeDataInAttributeList(scene,3, 1, texID);
            if (dimension >= 3)
                storeDataInAttributeList(scene,4, dimension, normals);
            for(int i = 0; i < maxElementCount; i++)
                loadElementIndices(i);
            bindIndices(scene, indices);
            ids.get(scene).put(window, id);
        }else
        {
            int id = GL30.glGenVertexArrays();
            GL30.glBindVertexArray(id);
            GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, VBOS.get(scene).get(0));
            GL20.glVertexAttribPointer(0, dimension, GL11.GL_FLOAT, false, 0, 0);
            GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, VBOS.get(scene).get(1));
            GL20.glVertexAttribPointer(1, 2, GL11.GL_FLOAT, false, 0, 0);
            GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, VBOS.get(scene).get(2));
            GL20.glVertexAttribPointer(2, 4, GL11.GL_FLOAT, false, 0, 0);
            GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, VBOS.get(scene).get(3));
            GL20.glVertexAttribPointer(3, 1, GL11.GL_FLOAT, false, 0, 0);
            if(dimension == 3){
                GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, VBOS.get(scene).get(4));
                GL20.glVertexAttribPointer(4, dimension, GL11.GL_FLOAT, false, 0, 0);
            }
            GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, EBOS.get(scene));
            ids.get(scene).put(window, id);
        }
        GL30.glBindVertexArray(0);
    }



    public void addEntity(Entity entity){

    }

    public boolean addUIElement(UIElement element){
        if(numberOfElements < maxElementCount){
            BatchMaterial material = element.guiMaterial;
            String textureName = material.textureName;
            if(elements.containsKey(textureName)){
                HashMap<Integer, Pair<Element,BatchMaterial>> elemental = elements.get(textureName);
                elemental.put(numberOfElements, Pair.with(element, material));
                loadVertexProperties(textureName, numberOfElements);
                loadTextureProperties(textureName, numberOfElements);
                loadColorProperties(textureName, numberOfElements);
                if(dimension >= 3)
                    loadNormalProperties(textureName, numberOfElements);
                numberOfElements++;
                if(numberOfElements == maxElementCount)
                    hasRoom = false;
                return true;

            }else{
                if(numberOfTextures < textureLimit) {
                    HashMap<Integer, Pair<Element, BatchMaterial>> elemental = new HashMap<>();
                    elemental.put(numberOfElements, Pair.with(element, material));
                    elements.put(textureName, elemental);
                    textureToIntMap.put(textureName, numberOfTextures++);

                    loadVertexProperties(textureName, numberOfElements);
                    loadTextureProperties(textureName, numberOfElements);
                    loadColorProperties(textureName, numberOfElements);
                    if(dimension >= 3)
                        loadNormalProperties(textureName, numberOfElements);
                    numberOfElements++;
                    if(numberOfElements == maxElementCount)
                        hasRoom = false;
                    return true;
                }

                return false;
            }

        }else
            return false;
    }


    /**
     * Bind Mesh Batch
     * @param scene Scene that is loaded to
     * @param window Window that is currently rendering
     */
    public void bind(Scene scene, Window window){
        boolean rebufferVertex = false;
        boolean rebufferColor = false;
        boolean rebufferTexture = false;
        for(String textureName : elements.keySet()){
            for(int index : elements.get(textureName).keySet()){
                Pair<Element, BatchMaterial> element = elements.get(textureName).get(index);
                if(!element.getValue0().lastPosition.equals(element.getValue0().position) ||
                        !element.getValue0().lastRotation.equals(element.getValue0().rotation)||
                        !element.getValue0().lastScale.equals(element.getValue0().scale)){
                    loadVertexProperties(textureName, index);
                    element.getValue0().lastPosition.set(element.getValue0().position);
                    element.getValue0().lastRotation.set(element.getValue0().rotation);
                    element.getValue0().lastScale.set(element.getValue0().scale);
                    rebufferVertex = true;
                }

                if(!element.getValue1().lastColor.equals(element.getValue1().color)){
                    loadColorProperties(textureName, index);
                    element.getValue1().lastColor.set(element.getValue1().color);
                    rebufferColor = true;
                }

                if(!element.getValue1().lastTextureOffset.equals(element.getValue1().textureOffset) ||
                        !element.getValue1().lastTextureScale.equals(element.getValue1().textureScale)){
                    loadTextureProperties(textureName, index);
                    element.getValue1().lastTextureOffset.set(element.getValue1().textureOffset);
                    element.getValue1().lastTextureScale.set(element.getValue1().textureScale);
                    rebufferTexture = true;
                }

            }
        }
        if(rebufferVertex)
        {
            GL20.glBindBuffer(GL20.GL_ARRAY_BUFFER, VBOS.get(scene).get(0));
            GL20.glBufferSubData(GL20.GL_ARRAY_BUFFER, 0,vertices);
        }

        if(rebufferTexture){
            GL20.glBindBuffer(GL20.GL_ARRAY_BUFFER, VBOS.get(scene).get(1));
            GL20.glBufferSubData(GL20.GL_ARRAY_BUFFER, 0, textureCoords);
            GL20.glBindBuffer(GL20.GL_ARRAY_BUFFER, VBOS.get(scene).get(3));
            GL20.glBufferSubData(GL20.GL_ARRAY_BUFFER, 0, texID);
        }

        if(rebufferColor){
            GL20.glBindBuffer(GL20.GL_ARRAY_BUFFER, VBOS.get(scene).get(2));
            GL20.glBufferSubData(GL20.GL_ARRAY_BUFFER, 0, colors);
        }



        GL20.glEnable(GL11.GL_TEXTURE);
        for (String textureName : textureToIntMap.keySet()) {
            GL20.glActiveTexture(GL20.GL_TEXTURE0 + textureToIntMap.get(textureName));
            assetManager.getTexture(textureName).bind(scene);
        }

        GL30.glBindVertexArray(ids.get(scene).get(window));
        GL30.glEnableVertexAttribArray(0);
        GL30.glEnableVertexAttribArray(1);
        GL30.glEnableVertexAttribArray(2);
        GL30.glEnableVertexAttribArray(3);
        if(dimension >= 3)
            GL30.glEnableVertexAttribArray(4);

        GL30.glDrawElements(GL30.GL_TRIANGLES, mesh.getVertexCount() * numberOfElements, GL30.GL_UNSIGNED_INT, 0);

    }


    public void unbind(){

    }




    private void loadElementIndices(int index){

        int indicesLength = mesh.getIndices().length;

        int offsetArrayIndex = indicesLength * index;
        int offset = (mesh.getVertices().length / dimension) * index;

        for(int i = 0; i < indicesLength; i++){
            indices[offsetArrayIndex + i] = offset + mesh.getIndices()[i];
        }
    }

    private final Vector3f POS = new Vector3f();

    private void loadVertexProperties(String textureName, int index){
        HashMap<Integer, Pair<Element, BatchMaterial>> elementA = elements.get(textureName);
        Pair<Element, BatchMaterial> element = elementA.get(index);

        float[] vert = mesh.getVertices();
        float[] text = mesh.getTextureCoords();
        float[] norms = mesh.getNormals();


        int vertexSize = vert.length / dimension;
        int offset = index * vert.length;

        Vector3f position = element.getValue0().position;
        Quaternionf rotation = element.getValue0().rotation;
        Vector3f scale = element.getValue0().scale;


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

            if(dimension >= 3)
                POS.set(position.x + vert[i] * scale.x / 2f, position.y + vert[i + 1] * scale.y / 2f, position.z + vert[i + 2] * scale.z / 2f);
            else
                POS.set(position.x + vert[i] * scale.x / 2f, position.y + vert[i + 1] * scale.y / 2f, 0);

            POS.rotate(rotation);


            vertices[offset] = POS.x;
            vertices[offset + 1] = POS.y;
            if(dimension >= 3)
                vertices[offset + 2] = POS.z;

            offset += dimension;


        }




    }

    private final Vector2f TEX = new Vector2f();
    private void loadTextureProperties(String textureName, int index){
        HashMap<Integer, Pair<Element, BatchMaterial>> elementA = elements.get(textureName);
        Pair<Element, BatchMaterial> element = elementA.get(index);


        float[] text = mesh.getTextureCoords();
        Vector2f textureOffset = element.getValue1().textureOffset;
        Vector2f textureScale = element.getValue1().textureScale;
        int vertexSize = text.length / 2;
        int offset = index * text.length;
        int offsetA = index * vertexSize;
        for(int i = 0; i < vertexSize; i++){
            TEX.set(text[i], text[i + 1]).mul(textureScale).add(textureOffset);
            textureCoords[offset] = TEX.x;
            textureCoords[offset + 1] = TEX.y;
            offset += 2;

            texID[offsetA] = textureToIntMap.get(textureName);
            offsetA++;

        }

    }

    private void loadColorProperties(String textureName, int index){
        HashMap<Integer, Pair<Element, BatchMaterial>> elementA = elements.get(textureName);
        Pair<Element, BatchMaterial> element = elementA.get(index);

        float[] text = mesh.getTextureCoords();
        Vector4f color = element.getValue1().color;

        int vertexSize = text.length / 2;
        int offset = index * vertexSize * 4;

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

            colors[offset] = color.x;
            colors[offset + 1] = color.y;
            colors[offset + 2] = color.z;
            colors[offset + 3] = color.w;
            offset += 4;

        }
    }

    private void loadNormalProperties(String textureName, int index){
        HashMap<Integer, Pair<Element, BatchMaterial>> elementA = elements.get(textureName);
        Pair<Element, BatchMaterial> element = elementA.get(index);
        float[] norms = mesh.getNormals();

        int vertexSize = norms.length / dimension;
        int offset = index * norms.length;

        for(int i = 0; i < vertexSize; i++){
            normals[offset] = norms[i];
            normals[offset + 1] = norms[i];
            normals[offset + 2] = norms[i];
            offset += dimension;
        }

    }




    private void storeDataInAttributeList(Scene scene, int attributeNumber, int attributeSize, float[] data){
        int vboID = GL15.glGenBuffers();
        VBOS.get(scene).add(vboID);
        GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vboID);
        GL15.glBufferData(GL15.GL_ARRAY_BUFFER, data.length * Float.BYTES, GL15.GL_DYNAMIC_DRAW);
        GL20.glVertexAttribPointer(attributeNumber, attributeSize, GL11.GL_FLOAT, false, 0, 0);
        GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
    }

    private void bindIndices(Scene scene, int[] indices){
        int eboID = GL15.glGenBuffers();
        EBOS.put(scene, eboID);
        GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, eboID);
        GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, indices, GL15.GL_STATIC_DRAW);

    }

    /**
     * Get VAO ID
     * @param scene Scene its loaded to
     * @param window Window it's rendering to
     * @return VAO ID
     */
    public int getVAOid(Scene scene, Window window){
        if(ids.get(scene).get(window) != null)
            return ids.get(scene).get(window);
        else
            return 0;
    }


    /**
     * Dispose VAO
     * @param scene Scene it is loaded to
     * @param window Window its rendering to
     */
    public void disposeVAO(Scene scene, Window window){
        GL30.glDeleteVertexArrays(ids.get(scene).get(window));
        ids.get(scene).remove(window);

    }

    /**
     * Dispose the MeshBatch and clear
     * @param scene
     */
    public void dispose(Scene scene) {
        for (int vbo : VBOS.get(scene))
            GL15.glDeleteBuffers(vbo);
        GL15.glDeleteBuffers(EBOS.get(scene));
        VBOS.clear();
        EBOS.clear();
    }


    public boolean hasRoom() {
        return this.hasRoom;
    }

    public boolean hasTextureRoom() {
        return this.elements.keySet().size() < textureLimit;
    }

    public boolean hasTexture(String textureName) {
        return this.elements.containsKey(textureName);
    }

    public int zIndex() {
        return this.zIndex;
    }

    @Override
    public int compareTo(MeshBatch o) {
        return Integer.compare(this.zIndex, o.zIndex());
    }
}
public class GUIRenderer {

    private final Canvas canvas;
    private final BatchedSpriteShader shader;
    private final AssetManager assetManager;
    private final Array<MeshBatch> meshBatches;
    private final Scene scene;

    public GUIRenderer(AssetManager assetManager, Scene scene){
        this.scene = scene;
        this.assetManager = assetManager;
        meshBatches = new Array<>();

        scene.loadTexture("null");
        shader = assetManager.getShader(BatchedSpriteShader.class);
        scene.loadShader(BatchedSpriteShader.class);

        canvas = new Canvas(scene, assetManager, shader);
    }

    public void Update(float deltaTime){
        canvas.update(deltaTime);
    }

    public void Render(Window window, float deltaTime){
        canvas.render(window, deltaTime);
    }

    public void addUIElement(UIElement element){
        canvas.addChild(element);

        MeshBatch batchA = null;
        for(MeshBatch batch : meshBatches)
            if(batch.hasRoom()) {
                if (batch.hasTexture(element.guiMaterial.textureName)) {
                    batchA = batch;
                    break;
                }else
                {
                    if(batch.hasTextureRoom())
                        batchA = batch;
                }
            }

        if(batchA != null) {
            if(!batchA.addUIElement(element)){
                MeshBatch mb = new MeshBatch(assetManager, "Quad", 32, 0);
                scene.loadMeshBatch(meshBatches.size + "", mb);
                meshBatches.add(mb);
                mb.addUIElement(element);
            }
        }
        else{
                MeshBatch mb = new MeshBatch(assetManager, "Quad", 32, 0);
                scene.loadMeshBatch(meshBatches.size + "", mb);
                meshBatches.add(mb);
                mb.addUIElement(element);
        }
    }

    public void removeUIElement(UIElement element){
        canvas.removeChild(element);
    }


    private class Canvas extends UIElement{

        private Canvas(Scene scene, AssetManager assetManager, BatchedSpriteShader shader){
            this.scene = scene;
            this.assetManager = assetManager;
        }

        private final Matrix4f projectionMatrix = new Matrix4f();
        private final Matrix4f viewMatrix = new Matrix4f();

        private final Vector3f position = new Vector3f();
        private final Vector3f direction = new Vector3f(0,0,-1);
        private final Vector3f up = new Vector3f(0,1,0);
        private final Vector3f tmp = new Vector3f();


        @Override
        void render(Window window, float deltaTime) {
            Vector2i size = window.getSize();
            shader.start(scene);
            shader.loadProjectionMatrix(projectionMatrix.identity().setOrtho( -size.x / 2f,  (size.x / 2f),  -(size.y / 2f),  size.y / 2f, 0, 1000));
            shader.loadViewMatrix(viewMatrix.identity().lookAt(position, tmp.set(position).add(direction), up));
            for(MeshBatch batch : meshBatches)
                batch.bind(scene, window);
            super.render(window, deltaTime);
            shader.stop();
        }

        @Override
        protected void Update(float deltaTime) { }
        @Override
        protected void Render(Window window, float deltaTime) { }
    }
}
#shader vertex
#version 400 core
layout (location=0) in vec2 position;
layout (location=1) in vec2 textureCoords;
layout (location=2) in vec4 color;
layout (location=3) in float texID;

out vec4 pass_color;
out vec2 pass_texCoords;
out float pass_texId;

uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;


void main(){

    pass_color = color;
    pass_texCoords = textureCoords;
    pass_texId = texID;

    gl_Position = projectionMatrix * viewMatrix * vec4(position, 0.0, 1.0);

}

#shader fragment
#version 400 core


in vec4 pass_color;
in vec2 pass_texCoords;
in float pass_texId;
out vec4 out_Color;

uniform sampler2D textureSampler[gl_MaxTextureImageUnits];

void main(){
    int id = int(pass_texId);
    out_Color = pass_color * texture(textureSampler[id], pass_texCoords);
}

Advertisement

Answer

Turns out I was mapping textures wrong so I changed the load Texture Properties from

private void loadTextureProperties(String textureName, int index){
        HashMap<Integer, Pair<Element, BatchMaterial>> elementA = elements.get(textureName);
        Pair<Element, BatchMaterial> element = elementA.get(index);


        float[] text = mesh.getTextureCoords();
        Vector2f textureOffset = element.getValue1().textureOffset;
        Vector2f textureScale = element.getValue1().textureScale;
        int vertexSize = text.length / 2;
        int offset = index * text.length;
        int offsetA = index * vertexSize;
        for(int i = 0; i < vertexSize; i++){
            TEX.set(text[offset], text[offset + 1]).mul(textureScale).add(textureOffset);
            textureCoords[i] = TEX.x;
            textureCoords[i+ 1] = TEX.y;
            offset += 2;

            texID[offsetA] = textureToIntMap.get(textureName);
            offsetA++;

        }
private void loadTextureProperties(String textureName, int index){
        HashMap<Integer, Pair<Element, BatchMaterial>> elementA = elements.get(textureName);
        Pair<Element, BatchMaterial> element = elementA.get(index);


        float[] text = mesh.getTextureCoords();
        Vector2f textureOffset = element.getValue1().textureOffset;
        Vector2f textureScale = element.getValue1().textureScale;
        int vertexSize = text.length / 2;
        int offset = index * text.length;
        int offsetA = index * vertexSize;
        for(int i = 0; i < vertexSize; i++){
            TEX.set(text[offset], text[offset + 1]).mul(textureScale).add(textureOffset);
            textureCoords[offset] = TEX.x;
            textureCoords[offset + 1] = TEX.y;
            offset += 2;

            texID[offsetA] = textureToIntMap.get(textureName);
            offsetA++;

        }
Advertisement