Hello JGO, this one’s pretty simple:
I have the basics of a 2d batch drawer. Currently there are no projection, view, or model matrices involved, which I’m attempting to fix today.
I changed the shader from this:
#version 330
layout (location=0) in vec2 inPosition;
layout (location=1) in vec2 inTexCoord;
out vec2 vTexCoord;
void main() {
gl_Position = vec4(inPosition, 0.0, 1.0);
vTexCoord = inTexCoord;
}
To this:
#version 330
layout (location=0) in vec2 inPosition;
layout (location=1) in vec2 inTexCoord;
out vec2 vTexCoord;
uniform mat4 projectionMatrix;
void main() {
gl_Position = projectionMatrix * vec4(inPosition, 0.0, 1.0);
vTexCoord = inTexCoord;
}
And I then supplied the shader with a bog-standard JOML matrix4f:
Matrix4f proj = new Matrix4f();
proj.identity().setOrtho(0, window.getWidth(), 0, window.getHeight(), -1f, 1f);
shader.setUniform("projectionMatrix", proj);
glViewport(0, 0, window.getWidth(), window.getHeight());
Now, no matter what corners or sizes of quads I supply, I don’t get anything rendered on the screen.
Am I missing something obvious here?
EDIT:
In the interest of providing a bit more insight, here is the code for the batcher:
package com.eli.game.render;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL15.*;
import static org.lwjgl.opengl.GL20.*;
import static org.lwjgl.opengl.GL30.*;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import org.joml.Matrix4f;
import org.joml.Vector2f;
import org.lwjgl.system.MemoryUtil;
public class Batcher {
private int vaoID;
private int indID, posID, texID;
private FloatBuffer posBuf;
private FloatBuffer texBuf;
private ShaderProgram shader;
Matrix4f ortho;
private boolean drawing;
private int drawCount;
public void init(int quadsPerBatch, Window window) {
// create VAO
vaoID = glGenVertexArrays();
glBindVertexArray(vaoID);
// Create index buffer, and initialize values; draw order won't be changing.
int[] indices = new int[quadsPerBatch * 6]; // # quads * 6 indices per quad, 3 per triangle
int j = 0;
for (int i = 0; i < indices.length; i+=6, j+=4) {
indices[i + 0] = j + 0;
indices[i + 1] = j + 1;
indices[i + 2] = j + 2;
indices[i + 3] = j + 3;
indices[i + 4] = j + 0;
indices[i + 5] = j + 2;
}
indID = glGenBuffers();
IntBuffer ib = MemoryUtil.memAllocInt(indices.length);
ib.put(indices).flip();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indID);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, ib, GL_DYNAMIC_DRAW);
MemoryUtil.memFree(ib);
// create position buffer
posID = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, posID);
posBuf = MemoryUtil.memAllocFloat(quadsPerBatch * 4 * 2); // # quads * 4 verts per quad * 2 attribs per vert
long size = posBuf.capacity() * Float.BYTES;
glBufferData(GL_ARRAY_BUFFER, size, GL_DYNAMIC_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, false, 0, 0);
// Create texture buffer
texID = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, texID);
texBuf = MemoryUtil.memAllocFloat(quadsPerBatch * 4 * 2); // # quads * 4 verts per quad * 2 attribs per vert
size = texBuf.capacity() * Float.BYTES;
glBufferData(GL_ARRAY_BUFFER, size, GL_DYNAMIC_DRAW);
glVertexAttribPointer(1, 2, GL_FLOAT, false, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
// Initialize variables
drawCount = 0;
drawing = false;
// Load shader
shader = new ShaderProgram();
shader.loadVertexShader("./resources/shaders/basic/basic.vs");
shader.loadFragmentShader("./resources/shaders/basic/basic.fs");
shader.link();
// Create and set shader uniforms
shader.createUniform("texture_sampler");
shader.createUniform("projectionMatrix");
shader.setUniform("texture_sampler", 0);
ortho = new Matrix4f();
ortho.identity().setOrtho(0, window.getWidth(), 0, window.getHeight(), -1f, 1f);
shader.setUniform("projectionMatrix", ortho);
glViewport(0, 0, window.getWidth(), window.getHeight());
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
public void clear() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
public void begin() {
if (drawing) {
throw new IllegalStateException("Already drawing.");
}
drawing = true;
drawCount = 0;
}
public void end() {
if (!drawing) {
throw new IllegalStateException("Not drawing.");
}
drawing = false;
flush();
}
/** Flush data to GPU and render. */
public void flush() {
// Only flush if there's information *to* flush.
if (drawCount > 0) {
// Bind VAO
glBindVertexArray(vaoID);
// Flip position buffer, and send to GPU
posBuf.flip();
glBindBuffer(GL_ARRAY_BUFFER, posID);
glBufferSubData(GL_ARRAY_BUFFER, 0, posBuf);
glVertexAttribPointer(0, 2, GL_FLOAT, false, 0, 0);
// Flip texture buffer, and send to GPU
texBuf.flip();
glBindBuffer(GL_ARRAY_BUFFER, texID);
glBufferSubData(GL_ARRAY_BUFFER, 0, texBuf);
glVertexAttribPointer(1, 2, GL_FLOAT, false, 0, 0);
// Bind shader
shader.bind();
// Set texture
shader.setUniform("texture_sampler", 0);
// Enable necessary vertex attributes, draw the data, and restore state.
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glDrawElements(GL_TRIANGLES, drawCount, GL_UNSIGNED_INT, 0);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
// Unbind VAO
glBindVertexArray(0);
// Clear vertices for next batch
posBuf.clear();
texBuf.clear();
drawCount = 0;
}
}
public void drawPolarRect(TextureRegion tex, float elevation, float height, float startAngle, float endAngle) {
if (posBuf.remaining() < 2 * 4 * 2) { // 2 vertex attribs, 4 vertices, 2 components per vert
System.out.println("Not enough data remaining in VBO, flushing.");
flush();
}
float angle1 = (float) Math.toRadians(startAngle); // lesser angle
float angle2 = (float) Math.toRadians(endAngle); // greater angle
// Draw order:
// 0 1--0
// |\ \ |
// | \ \|
// 1--2 2
// top left (greater angle, larger radius)
posBuf.put((float) Math.cos(angle2) * (elevation + height));
posBuf.put((float) Math.sin(angle2) * (elevation + height));
// bottom left (greater angle, smaller radius)
posBuf.put((float) Math.cos(angle2) * elevation);
posBuf.put((float) Math.sin(angle2) * elevation);
// bottom right (smaller angle, smaller radius)
posBuf.put((float) Math.cos(angle1) * elevation);
posBuf.put((float) Math.sin(angle1) * elevation);
// top right (smaller angle, greater radius
posBuf.put((float) Math.cos(angle1) * (elevation + height));
posBuf.put((float) Math.sin(angle1) * (elevation + height));
// Textures
texBuf.put(tex.getU1()).put(tex.getV2());
texBuf.put(tex.getU1()).put(tex.getV1());
texBuf.put(tex.getU2()).put(tex.getV1());
texBuf.put(tex.getU2()).put(tex.getV2());
drawCount += 6;
}
public void drawQuad(Vector2f topLeft, Vector2f bottomLeft, Vector2f bottomRight, Vector2f topRight, float u1, float u2, float v1, float v2) {
posBuf.put(topLeft.x).put(topLeft.y);
posBuf.put(bottomLeft.x).put(bottomLeft.y);
posBuf.put(bottomRight.x).put(bottomRight.y);
posBuf.put(topRight.x).put(topRight.y);
texBuf.put(u1).put(v2);
texBuf.put(u1).put(v1);
texBuf.put(u2).put(v1);
texBuf.put(u2).put(v2);
drawCount += 6;
}
public void drawQuad(float x, float y, float width, float height, float u1, float u2, float v1, float v2) {
posBuf.put(x) .put(y + height);
posBuf.put(x) .put(y);
posBuf.put(x + width) .put(y);
posBuf.put(x + width) .put(y + height);
texBuf.put(u1).put(v2);
texBuf.put(u1).put(v1);
texBuf.put(u2).put(v1);
texBuf.put(u2).put(v2);
drawCount += 6;
}
}