Well, simplified I'm setting up and rendering everything like this:
val shaderProgram = ShaderProgram("assets/default.vert", "assets/default.frag")
val texture = Texture("assets/libgdx-logo.png")
val texture2 = Texture("assets/libgdx-logo-mipmap.png")
val sprite = BufferSprite(texture)
val vertexData = MemoryUtil.memAllocFloat(8 * 4 * 8192)
fun setup() {
glEnable(GL_TEXTURE)
glColorMask(true, true, true, true)
glDepthMask(false)
//Setup vertex buffer and index buffer
stackPush().use { stack ->
val indices = MemoryUtil.memAllocInt(6 * 8192)
for(i in 1..8192)
indices.put(intArrayOf(
0 + 4*(i-1), 1 + 4*(i-1), 2 + 4*(i-1), 1 + 4*(i-1), 3 + 4*(i-1), 2 + 4*(i-1)
))
indices.flip()
val vao = stack.mallocInt(1)
glGenVertexArrays(vao)
glBindVertexArray(vao.get(0))
val vbos = stack.mallocInt(2)
glGenBuffers(vbos)
glBindBuffer(GL_ARRAY_BUFFER, vbos.get(0))
glBufferData(GL_ARRAY_BUFFER, vertexData, GL_DYNAMIC_DRAW)
glEnableVertexAttribArray(0)
glEnableVertexAttribArray(1)
glEnableVertexAttribArray(2)
glVertexAttribPointer(0, 2, GL_FLOAT, false, 8 * sizeof(Float), 0)
glVertexAttribPointer(1, 4, GL_FLOAT, false, 8 * sizeof(Float), 2.toLong() * sizeof(Float))
glVertexAttribPointer(2, 2, GL_FLOAT, false, 8 * sizeof(Float), 6.toLong() * sizeof(Float))
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbos.get(1))
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices, GL_STATIC_DRAW)
}
//Setup projection matrix
glUseProgram(shaderProgram.program)
stackPush().use { stack ->
val mat = stack.mallocFloat(16)
projView.get(mat)
val loc = glGetUniformLocation(shaderProgram.program, "u_projView")
glUniformMatrix4fv(loc, false, mat)
}
}
fun flush() {
if(vertexData.position() > 0) vertexData.flip()
glBufferSubData(GL_ARRAY_BUFFER, 0, vertexData)
glDrawElements(GL_TRIANGLES, 6 * mesh.vertexData.limit()/(8 * 4), GL_UNSIGNED_INT, 0)
vertexData.clear()
}
fun draw(sprite: BufferSprite) {
vertexData.put(sprite.vertexData)
}
fun render() {
glClear(GL_COLOR_BUFFER_BIT)
texture.bind()
for(i in 1..500)
draw(sprite)
flush()
texture2.bind()
for(i in 1..500)
draw(sprite)
flush()
}
//Texture loading
class Texture(file: String) {
val handle: Int
val width: Int
val height: Int
init {
val stack = stackPush()
val w = stack.mallocInt(1)
val h = stack.mallocInt(1)
val bpp = stack.mallocInt(1)
stbi_set_flip_vertically_on_load(true)
val image = stbi_load(file, w, h, bpp, 4)
?: throw RuntimeException("Failed to load a texture file!" + System.lineSeparator() + stbi_failure_reason())
width = w.get()
height = h.get()
val textureID = stack.mallocInt(1)
glGenTextures(textureID)
handle = textureID.get()
glBindTexture(GL_TEXTURE_2D, handle)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image)
glGenerateMipmap(GL_TEXTURE_2D)
glBindTexture(GL_TEXTURE_2D, 0)
stbi_image_free(image)
stack.pop()
}
fun bind(unit: Int = 0) {
glActiveTexture(GL_TEXTURE0 + unit)
glBindTexture(GL_TEXTURE_2D, handle)
}
}
//Sprite wrapping vertex data
class BufferSprite(val texture: Texture) {
companion object {
val VERTEXDATA_FLOAT_SIZE = 8 * 4
val VERTEXDATA_BYTE_SIZE = 8 * 4 * 4
}
private val vertexData: FloatBuffer = MemoryUtil.memAllocFloat(VERTEXDATA_FLOAT_SIZE)
init {
vertexData.put(floatArrayOf(
0f,0f, 1f,1f,1f,1f, 0f,0f,
texture.width.toFloat(),0f, 1f,1f,1f,1f, 1f,0f,
0f,texture.height.toFloat(), 1f,1f,1f,1f, 0f,1f,
texture.width.toFloat(),texture.height.toFloat(), 1f,1f,1f,1f, 1f,1f
))
vertexData.clear()
}
}