I'm writing an app in Kotlin, which displays 2D object on user's phone. When I execute my code, I get Java.lang.nullpointerexceptionRun. Sadly, I don't know how to fix this issue. Run output:
E/com.a3dmodelap: Invalid ID 0x00000000.
E/AndroidRuntime: FATAL EXCEPTION: GLThread 633
Process: com.a3dmodelapp, PID: 7503
java.lang.NullPointerException: Attempt to invoke virtual method 'int android.graphics.Bitmap.getWidth()' on a null object reference
at com.a3dmodelapp.Texture.load(Texture.kt:80)
at com.a3dmodelapp.Stage$MyRenderer.onSurfaceCreated(Stage.kt:91)
at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1541)
at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1272)
So basically it fails at this line (Texture.kt:80):
Quote:
width = bmp.getWidth()
I'm new to Kotin and already spent hours trying to fix it. Would greatly appreciate the help. Here's the code in Texture class:
...
private var width : Int = 0
private var height : Int = 0
var resourceId = 0
...
constructor(resourceId: Int, mipmaps: Boolean)
{
this.resourceId = resourceId
this.textureId = -1
this.mipmaps = mipmaps
}
constructor(resourceId: Int) {
this(resourceId, false)
Texture(resourceId, false)
}
...
fun getWidth(): Int
{
return width
}
fun getHeight(): Int
{
return height
}
...
fun load(context: Context)
{
val opts = BitmapFactory.Options()
opts.inScaled = false
val bmp = BitmapFactory.decodeResource(context.getResources(), resourceId, opts)
width = bmp.getWidth()
height = bmp.getHeight()
...
}
UPDATE: It seems that resourceId is the problem. bmp variable is null, because of resourceId being 0. Followed my code step by step and I can't figure out where the problem may be. Right down below I provide more in depth walktrough of resourceId variable.
In Stage class I have a call to the constructor:
tex = Texture(getResources().getIdentifier(img, "drawable", context?.getPackageName()))
After checking, it is basically the same as this:
tex = Texture(2131165330)
So that means it's calling the second constructor in Texture class:
constructor(resourceId: Int)
{
Texture(resourceId, false)
}
And then the first one:
constructor(resourceId: Int, mipmaps: Boolean)
{
this.resourceId = resourceId
this.textureId = -1
this.mipmaps = mipmaps
}
resourceId shouldn't be equal to 0, but to 2131165330 (I've checked and it is being set in the first constructor). I don't know why it's equal to 0
UPDATE 2
Below I provide you Stage class (the most relevant to me lines of code have been bolded):
class Stage(context: Context?, attrs: AttributeSet?) : GLSurfaceView(context, attrs) {
private var w = 0.0F
private var h = 0.0F
private var screenHeight = 0
private var screenWidth = 0
private lateinit var vertexBuffer: FloatBuffer
private lateinit var tex: Texture
private var img = ""
private var xPos = 0.0F
private var yPos = 0.0F
private var r = 0.0F
init {
setEGLConfigChooser(8, 8, 8, 8, 0, 0)
setRenderer(MyRenderer())
val vertices = floatArrayOf(
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
-0.5f, 0.5f, 0.0f,
0.5f, 0.5f, 0.0f,
)
val vbb: ByteBuffer = ByteBuffer.allocateDirect(vertices.size * 4)
vbb.order(ByteOrder.nativeOrder())
vertexBuffer = vbb.asFloatBuffer()
vertexBuffer.put(vertices)
vertexBuffer.position(0)
img = "football"
tex = Texture(getResources().getIdentifier(img, "drawable", context?.getPackageName()))
}
private inner class MyRenderer : Renderer {
override fun onDrawFrame(gl: GL10) {
gl.glClear(GLES10.GL_COLOR_BUFFER_BIT)
tex.prepare(gl, GL10.GL_CLAMP_TO_EDGE)
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer)
tex.draw(gl, xPos, yPos, tex.getWidth()*r, tex.getHeight()*r, 0f)
}
override fun onSurfaceChanged(gl: GL10, width: Int, height: Int) {
gl.glClearColor(0f, 0f, 0f, 1.0f)
if (width > height) {
h = 600F
w = width * h / height
} else {
w = 600F
h = height * w / width
}
screenWidth = width
screenHeight = height
xPos = w/2
yPos = h/2
r= 1F
gl.glViewport(0, 0, screenWidth, screenHeight)
gl.glMatrixMode(GL10.GL_PROJECTION)
gl.glLoadIdentity()
gl.glOrthof(0f, w, h, 0f, -1f, 1f)
gl.glMatrixMode(GL10.GL_MODELVIEW)
gl.glLoadIdentity()
}
override fun onSurfaceCreated(gl: GL10?, p1: EGLConfig?) {
gl?.apply {
glEnable(GL10.GL_ALPHA_TEST)
glEnable(GL10.GL_BLEND)
glBlendFunc(GL10.GL_ONE, GL10.GL_ONE_MINUS_SRC_ALPHA)
glDisable(GL10.GL_DEPTH_TEST)
glEnableClientState(GL10.GL_VERTEX_ARRAY)
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY)
tex.load(getContext())
}
}
}
}
UPDATE 3
I think I've found where the issue is, but I don't know how to solve it. Right here I'm making a call to the second constructor:
tex = Texture(getResources().getIdentifier(img, "drawable", context?.getPackageName()))
In the second constructor, I call the first one, but it doesn't make any sense, because it doesn't save values back to the Stage class. What I want to do is create something like this:
constructor(resourceId: Int)
{
this(resourceId, false)
}
In Java it would work perfectly fine, but here in Kotlin it doesn't. This is the only line that needs work, because if I make a call like the one provided below everything works just fine:
tex = Texture(getResources().getIdentifier(img, "drawable", context?.getPackageName()), false)
What I have tried:
I've spent hours trying to solve this with no success