ModelBuilder end() not working as it should "com.badlogic.gdx.utils.GdxRuntimeException: Call end() first"

254 Views Asked by At

I'm just trying to get a basic box rendered with LibGDX (using Kotlin & LibKTX) but am running into some issues.

If I call the ModelBuilder createBox function without the specified begin() and end() functions my box is not rendered. I checked the materials, camera position, the bounding box, added a light source, etc. but it's just.. not there. I figured the issue was with the way I was building the nodes, as I can't find an issue with the Material.

This is how I am trying to render my box:

class HomeView(private val baseUI: BaseUI) : KtxScreen {

    private val cam by lazy { PerspectiveCamera(67f, baseUI.aWidth, baseUI.aHeight) }
    private val boxInstance: ModelInstance
    private val modelBatch: ModelBatch
    private val modelBuilder: ModelBuilder by lazy { ModelBuilder() }
    private val vertexAttributes =
        VertexAttributes.Usage.Position.toLong() or
        VertexAttributes.Usage.Normal.toLong() or
        VertexAttributes.Usage.TextureCoordinates.toLong()
    private val greenMat by lazy { Material(ColorAttribute.createDiffuse(Color.GREEN)) }
    private val environment by lazy { Environment() }
    //------ end global

    init {
        cam.position.set(vec3(-10f, -10f, 10f))
        cam.lookAt(0f, 0f, 0f)
        cam.near = .1f
        cam.far = 10f
        cam.update()

        modelBatch = ModelBatch()

        modelBuilder.begin()
        modelBuilder.createBox(5f, 5f, 5f, greenMat, vertexAttributes)
        boxInstance = ModelInstance(modelBuilder.end())

        environment.set(ColorAttribute(ColorAttribute.AmbientLight, 0.4f, 0.4f, 0.4f, 1f))
        environment.add(DirectionalLight().set(0.8f, 0.8f, 0.8f, -1f, -0.8f, -0.2f))
    }

    override fun render(delta: Float) {
        Gdx.gl.glViewport(0, 0, baseUI.aWidth.toInt(), baseUI.aHeight.toInt())
        //this is working, screen appears grey
        clearScreen(.3f, .3f, .3f, 1f)

        modelBatch.begin(cam)
        modelBatch.render(boxInstance, environment)
        modelBatch.end()
    }

    //rest omitted
}

and here is my BaseUI class that I'm using to add the screen (I'm just trying to test screens out, this is all just for testing purposes so ignore the inefficiency please)

class BaseUI : KtxGame<KtxScreen>(), KtxApplicationAdapter {
    val aWidth by lazy { Gdx.graphics.width.toFloat() }
    val aHeight by lazy { Gdx.graphics.height.toFloat() }

    override fun create() {
        addScreen(HomeView(this))
        setScreen<HomeView>()
    }

    override fun render() {
        super<KtxGame>.render()
    }

    //rest ommitted
}

When I run this I get the following error: Exception in thread "LWJGL Application" com.badlogic.gdx.utils.GdxRuntimeException: Call end() first

which makes it seem like I need to call modelBatch.end() before I even create the nodes, which is confusing. I feel like I am doing something very basic wrong here, as I was able to get the basic 3D examples working back when I was trying this with Java a few years back.

So, two questions:

  • Why is LibGDX saying that I need to call end() before I create the nodes with ModelBuilder?
  • Is using modelBuilder.begin() and modelBuilder.end() actually the best way to use the ModelBuilder? I've yet to see a 3D example do this. Admittedly, all the 3D examples I have found have been from like 2013 so this might just be something that's been added. The LibGDX 3D section says to use this set of tutorials that do not use the begin() and end() functions, so I'm a bit confused as to what is the "best practice".

Thanks for any help!

edit: I tried it with a loaded model and it's having the same issue. Hmm..

edit2: Thank you Xoppa for helping me figure out what was wrong. The LibKTX specific function clearScreen() did not incorporate the GL20.GL_COLOR_BUFFER_BIT clear function as expected, which was my bad from reading their documentation. Adding this to my render function displayed the green box as expected:

Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT or GL20.GL_DEPTH_BUFFER_BIT)

1

There are 1 best solutions below

1
ekeitho On
  1. LibGDX is not saying to call end() before you create a node, but that there is already an existing model that was created. Therefore, before creating another one you need to call end().

  2. From your code sample, the reason why GdxRuntimeException is being thrown is that you call begin() and then createBox(). createBox() actually calls begin() in the function. (take a look here: https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/graphics/g3d/utils/ModelBuilder.java) Therefore, the next begin() call the model is not null and the exception is thrown.

For best practices, if createBox() can satisfy your request than just use that. If you need something more complicated, such that createSphere(), createCapsule() don't work for you, then you need to call begin(), part(...), and end().

Hope this helps!