Reputation: 788
Before I refactored the code to display multiple different textures everything was working fine but now all I am getting are black boxes that are smaller than how their size supposed to be and obviously they don't have any textures! I didn't even touch the vertexes, I don't why the size is affected at all.
Screenshot (the black big box should've covered the whole screen and the small square at the bottom should've been way bigger. Have no clue what affected their size):
Shaders: (they are compiled before used)
const val vertexShader_Image = "uniform mat4 u_MVPMatrix;" +
"attribute vec4 a_Position;" +
"attribute vec2 a_texCoord;" +
"varying vec2 v_texCoord;" +
"void main() {" +
" gl_Position = u_MVPMatrix * a_Position;" +
" v_texCoord = a_texCoord;" +
"}"
const val fragmentShader_Image = "precision mediump float;" +
"uniform sampler2D u_texture;" +
"varying vec2 v_texCoord;" +
"void main() {" +
" gl_FragColor = texture2D(u_texture, v_texCoord);" +
"}"
TextureLoader: (object is basically a static class in Kotlin if you are not familiar with it)
object TextureLoader
{
var textures: Map<TextureName, Int> = mutableMapOf()
fun generateTextures(nameBitmapPair: List<Pair<TextureName, Bitmap>>, screenWidth: Int, screenHeight: Int)
{
val textureAmount = nameBitmapPair.size
val mutableTextureMap = mutableMapOf<TextureName, Int>()
if(textureAmount > 31 || textureAmount< 0)
{
throw IllegalStateException("Texture amount is bigger than 31 or smaller than 0. Texture limit for OPEN GL is 31, it can't be bigger than it")
}
val textureHandles = IntArray(textureAmount)
GLES20.glGenTextures(textureAmount, textureHandles, 0)
for (i in 0 until textureAmount)
{
if (textureHandles[i] != 0)
{
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandles[i])
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR)
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR)
// Load the bitmap into the bound texture.
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, nameBitmapPair.get(i).second, 0)
nameBitmapPair.get(i).second.recycle()
mutableTextureMap.put(nameBitmapPair.get(i).first, textureHandles[i])
Timber.i("created new texture, Name = ${nameBitmapPair.get(i).first}, ID = ${textureHandles[i]}")
}
else
{
throw RuntimeException("Error loading texture.")
}
}
textures = mutableTextureMap
}
}
Batcher: (The class that handles the OpenGL boilerplate code so the rest of the codebase doesn't bleed anyone's eyes)
class Batcher private constructor()
{
// Store the model matrix. This matrix is used to move models from object space (where each model can be thought
// of being located at the center of the universe) to world space.
private val mtrxModel = FloatArray(16)
// Allocate storage for the final combined matrix. This will be passed into the shader program.
private val mtrxMVP = FloatArray(16)
// Store the projection matrix. This is used to project the scene onto a 2D viewport.
private val mtrxProjection = FloatArray(16)
/* This was my UV array before I refactored the code to add animations. Now it's accessed by the
sprite.animator.getUvCoordinationForCurrentFrame()
private var uvArray = floatArrayOf(
0.0f, 0.0f,
0.0f, 0.20f,
0.20f, 0.20f,
0.20f, 0.0f)
*/
private var uvBuffer: FloatBuffer? = null
private var vertexBuffer: FloatBuffer? = null
private var indices = shortArrayOf(0, 1, 2, 0, 2, 3) // The order of vertexrendering.
private var indicesBuffer: ShortBuffer? = null
// NOTE: companion object is the static class of the Kotlin if you are not familiar with it. It's used to create a singleton here.
companion object
{
private var instance: Batcher? = null
fun getInstance(): Batcher
{
if (instance == null)
{
instance = Batcher()
}
return instance!!
}
}
//Constructor of the Kotlin classes if you aren't familiar with it
init
{
glEnable(GL_BLEND)
// initialize byte buffer for the draw list
indicesBuffer = ByteBuffer.allocateDirect(indices.size * 2)
.order(ByteOrder.nativeOrder())
.asShortBuffer()
indicesBuffer!!.put(indices)
.position(0)
val vertices = floatArrayOf(
0f, 0f, 0f,
0f, 1.0f, 0f,
1.0f, 1.0f, 0f,
1.0f, 0f, 0f)
// The vertex buffer.
vertexBuffer = ByteBuffer.allocateDirect(vertices.size * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
vertexBuffer!!.put(vertices)
.position(0)
}
fun render(sprites: Sprites)
{
//TODO should these be called on every draw call??
// Get handle to shape's transformation matrix
val u_MVPMatrix = glGetUniformLocation(ShaderHelper.programTexture, "u_MVPMatrix")
val a_Position = glGetAttribLocation(ShaderHelper.programTexture, "a_Position")
val a_texCoord = glGetAttribLocation(ShaderHelper.programTexture, "a_texCoord")
val u_texture = glGetUniformLocation(ShaderHelper.programTexture, "u_texture")
glEnableVertexAttribArray(a_Position)
glEnableVertexAttribArray(a_texCoord)
// "it" here is the currently iterated sprite object
sprites.forEach{
val uvArray = it.animator.getUvCoordinationForCurrentFrame()
if (uvArray != null)
{
updateUVBuffer(uvArray)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
// Matrix op - start
Matrix.setIdentityM(mtrxMVP, 0)
Matrix.setIdentityM(mtrxModel, 0)
Matrix.translateM(mtrxModel, 0, it.x.toFloat(), it.y.toFloat(), 0f)
Matrix.scaleM(mtrxModel, 0, it.scaledFrameWidth.toFloat(), it.scaledFrameHeight.toFloat(), 0f)
if (it.isHorizontallyFlipped)
{
Matrix.translateM(mtrxModel, 0, 1f, 0f, 0f)
Matrix.scaleM(mtrxModel, 0, -1f, 1f, 0f)
}
Matrix.multiplyMM(mtrxMVP, 0, mtrxModel, 0, mtrxMVP, 0)
Matrix.multiplyMM(mtrxMVP, 0, mtrxProjection, 0, mtrxMVP, 0)
// Matrix op - end
// Pass the data to shaders - start
// Prepare the triangle coordinate data
//Binds this vertex's data to a spot in the buffer
glVertexAttribPointer(a_Position, 3, GL_FLOAT, false, 0, vertexBuffer)
// Prepare the texture coordinates
glVertexAttribPointer(a_texCoord, 2, GL_FLOAT, false, 0, uvBuffer)
glUniformMatrix4fv(u_MVPMatrix, 1, false, mtrxMVP, 0)
// Set the sampler texture unit to where we have saved the texture.
// second param is the active texture index
glUniform1i(u_texture, TextureLoader.textures.get(it.textureName)!!)
// Draw the triangles
glDrawElements(GL_TRIANGLES, indices.size, GL_UNSIGNED_SHORT, indicesBuffer)
}
else
{
Timber.w("UV array of the sprite is null, skipping rendering. \nSprite: ${it}")
}
}
}
fun setScreenDimension(screenWidth: Int, screenHeight: Int)
{
Matrix.setIdentityM(mtrxProjection, 0)
Matrix.orthoM(mtrxProjection, 0, 0f, screenWidth.toFloat(), screenHeight.toFloat(), 0f, 0f, 1f)
}
private fun updateUVBuffer(uvArray: FloatArray)
{
uvBuffer = ByteBuffer.allocateDirect(uvArray.size * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
uvBuffer!!.put(uvArray)
.position(0)
}
Upvotes: 1
Views: 221
Reputation: 210878
The value to be assigned to the texture sampler uniform is not the object number of the texture. It must be the texture unit that the texture is bound to. Since your texture is bound to texture unit 0 (GL_TEXTURE0
), you need to assign 0 to the texture sampler uniform (0 is default):
glUniform1i(u_texture, TextureLoader.textures.get(it.textureName)!!)
glUniform1i(u_texture, 0)
glBindTexture
binds a texture to the specified target and the current texture unit. The texture unit can be set by glActiveTexture
.
If you do
glActiveTexture(GLES20.GL_TEXTURE0)
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandles[i])
then you have to assign 0 to the texture sampler. But if you do
glActiveTexture(GLES20.GL_TEXTURE1)
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandles[i])
then you have to assign 1 to the texture sampler.
You have to ensure that the texture is bound before drawing the mesh. Since you have different textures, glBindTexture
should be called each time when you are going to render a geometry with a texture: For instance:
glActiveTexture(GLES20.GL_TEXTURE0)
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, TextureLoader.textures.get(it.textureName)!!)
glUniform1i(u_texture, 0)
glDrawElements(GL_TRIANGLES, indices.size, GL_UNSIGNED_SHORT, indicesBuffer)
Upvotes: 1