How to reposition skybox with camera movement

How do I reposition the skybox when the camera moves? I do not want the camera to get any closer to the skybox. My code currently looks like the following:



    protected void simpleInitGame() {
        // The terrain loaded from a greyscale image with fancy textures on it.
        complexTerrain();

   // Create a skybox to suround our world
   setupSky();

        // Attach the skybox to our root node, and force the rootnode to show
   // so that the skybox will always show
   rootNode.attachChild(skybox);
   rootNode.setCullHint(Spatial.CullHint.Never);
    }


    protected void simpeUpdate() {
        skybox.getLocalTranslation().set(cam.getLocation());
        skybox.updateGeometricState(0.0f, true);
    }

Why do you update the geometric state of the sky box each frame when it's already attached to the root node?

I think that could be causing you problems, since you update it with a time of 0.

Try removing that line.



Also, I'm wondering why you're setting the culling to never on the root node. Wouldn't that cause all the objects in the scene never to be culled, which is probably not what you want?

Instead, perhaps try putting the sky box in the SKIP render queue first of all objects and set up the frustum so that the sky box is within it.


Why do you update the geometric state of the sky box each frame when it's already attached to the root node?

I am trying to keep the sky box at the same distance from the camera when I move the camera. I do not want it to get any closer of farther away from the camera.


I think that could be causing you problems, since you update it with a time of 0. Try removing that line.

Ok, I commented it out (see the code below).


Also, I'm wondering why you're setting the culling to never on the root node. Wouldn't that cause all the objects in the scene never to be culled, which is probably not what you want?

I see what you mean. Should it be set on the sky box instead?


    protected void simpleInitGame() {
        // The terrain loaded from a greyscale image with fancy textures on it.
        complexTerrain();

        // Create a skybox to suround our world
        setupSky();

        // Attach the skybox to our root node, and force the rootnode to show
        // so that the skybox will always show
        skybox.setCullHint(Spatial.CullHint.Never);
        rootNode.attachChild(skybox);
    }


    protected void simpeUpdate() {
        skybox.getLocalTranslation().set(cam.getLocation());
        //skybox.updateGeometricState(0.0f, true);
    }




Instead, perhaps try putting the sky box in the SKIP render queue first of all objects and set up the frustum so that the sky box is within it.


Can anyone point me to a tutorial, or at least some code, that implements a 'skip render queue'? Thank you.

Hi,

there's an explanation, in the doc, about how to put objects in a renderqueue:

http://www.jmonkeyengine.com/wiki/doku.php/renderqueue



In order to optimize, I'm using this:

skybox.lockBounds();
skybox.lockMeshes();



Probably the effect of that overlaps with putting in the skip render queue (?)

Ok, I've looked at Skybox and it doesn't override updateGeometricState so it doesn't use the time variable passed to that method. So keep that line since you're not using a controller for the skybox.

The setting of the cull hint I don't really understand why you need.

If you set the skybox inside your camera's frustum it shouldn't be a problem.



When I look at the Flagrush examples (in the jmetest package or jgn source), the skybox isn't even attached to the root node, but it's updated every frame as you do with 0 for time.

It seems to work well there, but what problems are you having?

I have changed HelloTerrain.java to render my heightmap with the TerrainPage function (and removed the other heightmaps). Then I tried to add a skybox from the TestIsland.java code because it moves the sky box when you move the camera.



My problem is that the sky box is not moving when I move the camera. So I can walk toward or away from the sky box. Which looks wrong. And I want the sky box to always be within the frustum as I move around my very_large terrain.

Still can't get the sky box to follow the camera. The original code was from HelloTerrain.java. Here is what it looks like after I added the sky box:


package gltest;

/**
 *
 * @author Patrick Chapman
 */
//import java.awt.Image;
import java.net.URL;
import java.util.logging.Logger;

import javax.swing.ImageIcon;

import com.jme.app.SimpleGame;
import com.jme.bounding.BoundingBox;
import com.jme.image.Texture;
import com.jme.math.Vector3f;
import com.jme.scene.Spatial;
import com.jme.scene.Skybox;
import com.jme.scene.state.CullState;
import com.jme.scene.state.TextureState;
import com.jme.util.resource.ResourceLocatorTool;
import com.jme.util.resource.SimpleResourceLocator;
import com.jme.util.TextureManager;
import com.jmex.terrain.TerrainPage;
import com.jmex.terrain.util.ImageBasedHeightMap;
//import com.jmex.terrain.util.AbstractHeightMap;
import com.jmex.terrain.util.ProceduralTextureGenerator;

/**
 * Started Date: Aug 19, 2004<br><br>
 *
 * This program introduces jME's terrain utility classes and how they are used.  It
 * goes over ProceduralTextureGenerator, ImageBasedHeightMap, MidPointHeightMap, and
 * TerrainBlock.
 *
 * @author Jack Lindamood
 */
public class HelloTerrain extends SimpleGame {

    private static final Logger logger = Logger
   .getLogger(HelloTerrain.class.getName());

    /** A sky box for our scene. */
    Skybox skybox;

    public static void main(String[] args) {
        HelloTerrain app = new HelloTerrain();
        app.setConfigShowMode(ConfigShowMode.AlwaysShow);
        app.start();
    }

    protected void simpleInitGame() {
        // The terrain loaded from a greyscale image with fancy textures on it.
        complexTerrain();

        // Create a skybox to suround our world
        setupSky();

        // Attach the skybox to our root node
        skybox.setCullHint(Spatial.CullHint.Never);
        rootNode.attachChild(skybox);
    }


    protected void simpeUpdate() {
        skybox.getLocalTranslation().set(cam.getLocation());
        skybox.updateGeometricState(0.0f, true);
    }

   
    private void complexTerrain() {
        // This grayscale image will be our terrain
        URL grayScale=this.getClass().getClassLoader().getResource("gltest/data/texture/terrain/height.png");

        // These will be the textures of our terrain.
        URL waterImage=HelloTerrain.class.getClassLoader().getResource("gltest/data/texture/water.png");
        URL dirtImage=HelloTerrain.class.getClassLoader().getResource("gltest/data/texture/dirt.jpg");
        URL highest=HelloTerrain.class.getClassLoader().getResource("gltest/data/texture/highest.jpg");

        //  Create an image height map based on the gray scale of our image.
        //AbstractHeightMap map = new ImageBasedHeightMap(new ImageIcon(grayScale).getImage());
        ImageBasedHeightMap map = new ImageBasedHeightMap(new ImageIcon(grayScale).getImage());

        // Create a terrain block from the image's grey scale
        TerrainPage tp = new TerrainPage("image icon",
            33,
            map.getSize(),
            new Vector3f(20f, .2f, 20f),
            map.getHeightMap()
        );

        //  Create an object to generate textured terrain from the image based height map.
        ProceduralTextureGenerator pg = new ProceduralTextureGenerator(map);
        //  Look like water from height 0-60 with the strongest "water look" at 30
        pg.addTexture(new ImageIcon(waterImage),0,30,60);
        //  Look like dirt from height 40-120 with the strongest "dirt look" at 80
        pg.addTexture(new ImageIcon(dirtImage),40,80,120);
        //  Look like highest (pure white) from height 110-256 with the strongest "white look" at 130
        pg.addTexture(new ImageIcon(highest),110,130,256);

        //  Tell pg to create a texture from the ImageIcon's it has recieved.
        pg.createTexture(256);
        TextureState ts=display.getRenderer().createTextureState();
        // Load the texture and assign it.
        ts.setTexture(
                TextureManager.loadTexture(
                        pg.getImageIcon().getImage(),
                        Texture.MinificationFilter.Trilinear,
                        Texture.MagnificationFilter.Bilinear,
                        true
                )
        );
        tp.setRenderState(ts);

        // Give the terrain a bounding box
        tp.setModelBound(new BoundingBox());
        tp.updateModelBound();

        // Move the terrain in front of the camera
        //tp.setLocalTranslation(new Vector3f(0,0,-45));

        // Attach the terrain to our rootNode.
        rootNode.attachChild(tp);
    }


    private void setupSky() {
   skybox = new Skybox("skybox", 200, 200, 200);

   try {
            ResourceLocatorTool.addResourceLocator(
      ResourceLocatorTool.TYPE_TEXTURE,
      new SimpleResourceLocator(getClass().getResource(
      "/gltest/data/texture/skybox/")));
   } catch (Exception e) {
            logger.warning("Unable to access texture directory.");
      e.printStackTrace();
   }

   skybox.setTexture(Skybox.Face.North, TextureManager.loadTexture(
            "north.jpg", Texture.MinificationFilter.BilinearNearestMipMap,
            Texture.MagnificationFilter.Bilinear));
   skybox.setTexture(Skybox.Face.West, TextureManager.loadTexture("west.jpg",
            Texture.MinificationFilter.BilinearNearestMipMap,
            Texture.MagnificationFilter.Bilinear));
   skybox.setTexture(Skybox.Face.South, TextureManager.loadTexture(
            "south.jpg", Texture.MinificationFilter.BilinearNearestMipMap,
            Texture.MagnificationFilter.Bilinear));
   skybox.setTexture(Skybox.Face.East, TextureManager.loadTexture("east.jpg",
            Texture.MinificationFilter.BilinearNearestMipMap,
            Texture.MagnificationFilter.Bilinear));
   skybox.setTexture(Skybox.Face.Up, TextureManager.loadTexture("top.jpg",
            Texture.MinificationFilter.BilinearNearestMipMap,
            Texture.MagnificationFilter.Bilinear));
   skybox.setTexture(Skybox.Face.Down, TextureManager.loadTexture(
            "bottom.jpg", Texture.MinificationFilter.BilinearNearestMipMap,
            Texture.MagnificationFilter.Bilinear));
   skybox.preloadTextures();

   CullState cullState = display.getRenderer().createCullState();
   cullState.setCullFace(CullState.Face.None);
   cullState.setEnabled(true);
   skybox.setRenderState(cullState);

   skybox.updateRenderState();

        skybox.lockBounds();
        skybox.lockMeshes();
    }

}