I can't believe I can't make Skybox work

Ok, got my terrain to display… I ended up subclassing SimplePassGame and using the terrain from TestIsland.  It looks beautiful.



Then I added a skybox.  I have it following the camera around… but it displays in front of everything.



I know enough to know that a ZBufferState should fix things… 



I've tried different combinations (the ZBufferState on the skybox, on the terrain, on the rootnode) and the best I could do was to make the skybox render behind the black background (is that even possible? It just disappeared).



So, I'm asking my stupid question. I hope that the answer isn't as simple as I suspect it will be :confused:



And additionally, does the order in which you add things matter much? Can I add everything to the root and then modify the properties without worrying about the order?


 
public class MyGame extends SimplePassGame {
MyGameState gameState;
Skybox skybox;

public static void main(String[] args) {
    MyGame app = new MyGame();
        app.setDialogBehaviour(ALWAYS_SHOW_PROPS_DIALOG);
        app.start();
    }

  protected void simpleInitGame() {
        display.setTitle("Test Terrainsplatting");
        GameStateManager.create();
       
        // create a game state
        gameState = new MyGameState();
        Node scene = gameState.getRootNode();

/** Create a ZBuffer to display pixels closest to the camera above farther ones.  */
    ZBufferState buf = display.getRenderer().createZBufferState();
    buf.setEnabled(true);
    buf.setFunction(ZBufferState.CF_LEQUAL);
    scene.setRenderState(buf);
             
        // Set up the lighting and camera
        setupEnvironment();
       
        TerrainManager tm = new TerrainManager();
     
        // attach terrain
        Spatial terrain = tm.createTerrain();   
        scene.attachChild(terrain);     
       
        // attach skybox
        skybox = tm.createSkybox();
        skybox.setLocalTranslation(cam.getLocation());
        scene.attachChild(skybox);         
       
        RenderPass rootPass = new RenderPass();
        rootPass.add(scene);
        pManager.add(rootPass);
     
       
// update the scene graph for rendering
        scene.updateGeometricState(0.0f, true);
        scene.updateRenderState();
    }



    private void setupEnvironment() {
        cam.setFrustumPerspective(50.0f, (float) display.getWidth()
                / (float) display.getHeight(), 1f, 1000f);
        cam.setLocation( new Vector3f( -270, 180, -270 ) );
        cam.lookAt( new Vector3f( 0, 0, 0 ), Vector3f.UNIT_Y );
        cam.update();

        CullState cs = display.getRenderer().createCullState();
        cs.setCullMode(CullState.CS_BACK);
        gameState.getRootNode().setRenderState(cs);

        gameState.getRootNode().setLightCombineMode(LightState.OFF);
    }
   
    protected void simpleUpdate(){
    // Update the GameStates
   
GameStateManager.getInstance().update(-1.0f);

        skybox.setLocalTranslation(cam.getLocation());
        skybox.updateGeometricState(0.0f, true);
    }

    protected void simpleRender(){
    GameStateManager.getInstance().render(-1.0f);
    }



    public Skybox createSkybox(){
   
    Skybox skybox = new Skybox("skybox", 10, 10, 10);

    String dir = "data/texture/";
    Texture north = TextureManager.loadTexture(
        this.getClass().getClassLoader().getResource(
        dir + "north.jpg"),
        Texture.MM_LINEAR,
        Texture.FM_LINEAR);
    Texture south = TextureManager.loadTexture(
    this.getClass().getClassLoader().getResource(
        dir + "south.jpg"),
        Texture.MM_LINEAR,
        Texture.FM_LINEAR);
    Texture east = TextureManager.loadTexture(
    this.getClass().getClassLoader().getResource(
        dir + "east.jpg"),
        Texture.MM_LINEAR,
        Texture.FM_LINEAR);
    Texture west = TextureManager.loadTexture(
    this.getClass().getClassLoader().getResource(
        dir + "west.jpg"),
        Texture.MM_LINEAR,
        Texture.FM_LINEAR);
    Texture up = TextureManager.loadTexture(
    this.getClass().getClassLoader().getResource(
        dir + "top.jpg"),
        Texture.MM_LINEAR,
        Texture.FM_LINEAR);
    Texture down = TextureManager.loadTexture(
    this.getClass().getClassLoader().getResource(
        dir + "bottom.jpg"),
        Texture.MM_LINEAR,
        Texture.FM_LINEAR);

       
    skybox.setTexture(Skybox.NORTH, north);
    skybox.setTexture(Skybox.WEST, west);
    skybox.setTexture(Skybox.SOUTH, south);
    skybox.setTexture(Skybox.EAST, east);
    skybox.setTexture(Skybox.UP, up);
    skybox.setTexture(Skybox.DOWN, down);
    skybox.preloadTextures();

    return skybox;
}

And, sure enough, it does matter. I added the skybox to the scene before the terrain, and now it works. 



I'm going to leave this here and go read about the root node and rendering and stuff. I guess I assumed that the order wouldn't matter, and that it would render things based on their location in the world, not in the order in which they were added.  I realize that the skybox is only 10 units away from the camera at all times, so will usually be closer than anything else, but wouldn't a skybox have a different ZBuffer state that would put it behind everything?



Again, fundamental misunderstandings on my part.

However, in looking at other examples (Lesson4 from the flagrush tut) he added the skybox after the terrain, and it didn't seem to matter there.



Still confused!

One thing you might want to do is set the ZBufferState on the Skybox spatial, and not on the root node. It gives me the impression that CF_LEQUAL is order dependent, and thus the root node is making assumptions about which thing to render, be it terrain or skybox.

By default the render queue is used to sort the spatials by distance to camera (Renderer.QUEUE_OPAQUE), to reduce rendering fragments occluded by closer objects. If you set the render queue mode to Renderer.QUEUE_SKIP (with Spatial.setRenderQueueMode) then the geometry will render immediately on the call to Renderer.draw, otherwise you must call Renderer.renderQueue() after drawing your scene in order for it to get rendered.

The ZBufferState defines the operation OpenGL does to render the fragment against the depth-buffer, if ZBufferState.CF_LESS function is used, OpenGL writes the fragment color to the color buffer only if the incoming z value is less then the current value (closer to the camera).



In your case you want to render your scene as usual but keep the skybox always behind the scene, in that case you can skip queuing by distance (you know the skybox is always the farthest object), use the CF_LESS depth function, and disable writing to the zbuffer for the skybox, then it will always get rendered behind objects even if it is closer to the camera.