scalaopengl3dlwjglnifty-gui

How do I render 3d objects with 2d GUI (nifty-gui) correctly?


I have this code: https://github.com/magicgoose/lwjgl-nifty-test-project When I render only GUI, it works like expected. When I try to render triangle (in perspective projection) and then render GUI, everything fails, and even GUI don't render correctly - i get only letters on black background. (it happens if I uncomment draw_something() statement in display(...) method)

private def draw_something() {
    glTranslatef(0, 0, -20)
    glBegin(GL_TRIANGLES)
    glVertex3f(0.0f, 1.0f, 0.0f)
    glVertex3f(-1.0f, -1.0f, 0.0f)
    glVertex3f(1.0f, -1.0f, 0.0f)
    glEnd()
}

What I am doing wrong? I tried searching for working examples with nifty-gui AND 3d graphics on background, but no luck.

Update 1

I've changed the code based on datenwolf's answer, now GUI is rendered OK, but I can see white triangle only for a couple of milliseconds (maybe actually it is in a single frame?), seems like 3d setup is being "damaged"... This happens only if I render GUI, if I comment the line gui.render(false), white triangle stays on the screen.

Update 2

I added some motion to the 3d part (see updates in repository), now I can see that the triangle is barely visible (it looks like z-fighting).


Solution

  • You must switch between orthographic and perspective projection. For this there are two functions prepared: display_ready2d (this sets up a ortho projection matrix) and display_ready3d (this sets up a perspective projection).

    Unfortunately the display_ready3d function does not reset the matrix stack before applying it's changes. You must add a glLoadIdentity, before the call to gluPerspective. Also you should not clear the framebuffer in those functions, as you want to be able to switch between matrix setups. So change it to this:

    private def display_ready3d(fov: Float, aspect: Float) {
        glMatrixMode(GL_PROJECTION)
                glLoadIdentity();
        gluPerspective(fov, aspect, 0.01f, 100.0f)
    
        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()
    
        glEnable(GL_DEPTH_TEST)
    }
    

    display_ready2d right before drawing the GUI and display_ready3d before draw_something. Also you must put the clear commands there, which also need to cover the depth buffer; also the clear color should have an alpha value of 1 (except if you've created a transparent window).

    def display(width: Int, height: Int, AR: Float, gui: Nifty) {
                glViewport(0, 0, width, height)
                glClearDepth(1.)
                glClearColor(0., 0., 0., 1.)
                glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    
        display_ready3d(90, width/height)
        draw_something()
    
        display_ready2d(width, height)
        gui.render(false)
    
        glFlush()
        Display.update()
    }