1

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)

Czyzby
  • 2,999
  • 1
  • 22
  • 40
shan
  • 3,035
  • 5
  • 34
  • 50
  • 2
    That camera setup doesn't look correct. Set `camera.near` to (at least) `1f` and camera.far to e.g. `100f` (or at least a value so you model would actually be visible to the camera). – Xoppa May 16 '18 at 19:14
  • I have tried many clipping combos to no avail. My model is at 0,0,0 and is 5f in all directions, so it should be ok. I switched it to 1f & 100f and still no dice. – shan May 16 '18 at 19:27
  • 1
    Remove the environment. You didn't show your glClear call, are you actually clearing the depth buffer. Also check this: https://github.com/libgdx/libgdx/wiki/Importing-Blender-models-in-LibGDX, it contains some general debugging tips. Also consider posting your mvce (https://stackoverflow.com/help/mcve), so we can actually see the problem. Also, if you follow my basic 3d tutorial (only translate to kotlin, dont modify anything functional) you should be able to get something visible. – Xoppa May 16 '18 at 19:45
  • ah, I found the problem, thank you for mentioning the glClear function. LibKTX provides a screenClear() method that I thought incorporated the buffer_bit clearing but it does not, it's just for color changing. Adding in the GL20.GL_COLOR_BUFFER_BIT clear showed the box. I'll have to do some more reading on what exactly this does.. thanks again! – shan May 16 '18 at 19:58
  • By the way, you do not have to both extend the `KtxGame` and implement `KtxApplicationAdapter`, as they both implement the same interface. Use `KtxGame` if you want to use `KtxScreens` to represent views in your application or use `KtxApplicationAdapter` if you want more control over the application. Also, latest KTX version call `glClear` with both flags. – Czyzby Mar 18 '20 at 13:52

1 Answers1

1
  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!

ekeitho
  • 109
  • 1
  • 2
  • 8
  • Ah, I see. I'll just forego to the begin() and end() functions then as I don't need them in this instance. Unfortunately, even with just createBox() and assigning a model instance to it & throwing it in the model batch, it is still not appearing when I run my project :( I've tried it with a loaded model as well. The model is loading, just not being rendered. – shan May 16 '18 at 19:28