Reputation: 37
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);
}
Upvotes: 0
Views: 66
Reputation: 37
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++;
}
Upvotes: 0