FPS cam going underground (not attached to the terrain)

Hi All,

I'm incrementally diving into JME, I modify flagrush to be FPS -

  1. removed chase cam
  2. add camNode (for cam)   and here starts the problems

    3.I have add input to be FirstPersonHandler -> then I was unable to move more then just first button pressed and cam look stucked

    4.I have changed input to NodeHandler - > much better(act like real FPS), but I'm NOT attached to the terrain
  3. I have input.update in the update method (and other updates - cam,tb,scene)
  4. I tryied to :

      - attach player model to camNode and attach camNode to scene (root)

      - attache camNode to player and attach player to scene

      - almost all other possible combination

    Still no luck, my final status is - I can move like FPS - WASD + mouse, but not attached to the terrain - can magically dive into it.

    Thanks in advance (I;m sure it is something small due developer blindness for its own code).

    P.S I delete some comments for space

package com.captiveimagination.jmenet.flagrush;

import com.jme.util.GameTaskQueue;
import com.jme.util.GameTaskQueueManager;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.URL;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.ImageIcon;

import jmetest.flagrushtut.lesson8.Lesson8;
import jmetest.flagrushtut.lesson9.Flag;
import jmetest.flagrushtut.lesson9.ForceFieldFence;
import jmetest.flagrushtut.lesson9.Vehicle;
import jmetest.renderer.TestSkybox;
import jmetest.terrain.TestTerrain;

import com.jme.app.BaseGame;
import com.jme.bounding.BoundingBox;
import com.jme.image.Texture;
import com.jme.input.ChaseCamera;
import com.jme.input.InputHandler;
import com.jme.input.KeyBindingManager;
import com.jme.input.KeyInput;
import com.jme.input.MouseInput;
import com.jme.input.NodeHandler;
import com.jme.input.thirdperson.ThirdPersonMouseLook;
import com.jme.light.DirectionalLight;
import com.jme.math.FastMath;
import com.jme.math.Vector3f;
import com.jme.renderer.Camera;
import com.jme.renderer.ColorRGBA;
import com.jme.renderer.Renderer;
import com.jme.renderer.pass.BasicPassManager;
import com.jme.renderer.pass.RenderPass;
import com.jme.renderer.pass.ShadowedRenderPass;
import com.jme.scene.CameraNode;
import com.jme.scene.Node;
import com.jme.scene.Skybox;
import com.jme.scene.Spatial;
import com.jme.scene.state.CullState;
import com.jme.scene.state.LightState;
import com.jme.scene.state.TextureState;
import com.jme.scene.state.ZBufferState;
import com.jme.system.DisplaySystem;
import com.jme.system.JmeException;
import com.jme.util.TextureManager;
import com.jme.util.Timer;
import com.jme.util.export.binary.BinaryImporter;
import com.jmex.terrain.TerrainBlock;

 * Lesson 9
 * @author Mark Powell
public class FlagRush extends BaseGame {
    private static final Logger logger = Logger.getLogger(FlagRush.class
    // the terrain we will drive over.
    private TerrainBlock tb;
    // fence that will keep us in.
    private ForceFieldFence fence;
    //Sky box (we update it each frame)
    private Skybox skybox;
    //the new player object
    //private Vehicle player;
    private Vehicle player;
    //the flag to grab
    private Flag flag;
    //private ChaseCamera chaser;
    protected InputHandler input;
    //the timer
    protected Timer timer;
    // Our camera object for viewing the scene
    private Camera cam;
    //The chase camera, this will follow our player as he zooms around the level
    private ChaseCamera chaser;
    // the root node of the scene graph
    private Node scene;

    //used to hold on the scene and not flying around
    private CameraNode camNode;

    // display attributes for the window. We will keep these values
    // to allow the user to change them
    private int width, height, depth, freq;
    private boolean fullscreen;
    //store the normal of the terrain
    private Vector3f normal = new Vector3f();
    //height above ground level
    private float agl;
    private static ShadowedRenderPass shadowPass = new ShadowedRenderPass();
    private BasicPassManager passManager;
    protected void update(float interpolation) {
       // Update the GameTaskQueue
        // update the time to get the framerate
        interpolation = timer.getTimePerFrame();
        //update the keyboard input (move the player around)
        //update the chase camera to handle the player moving around.
        //update the fence to animate the force field texture
        //update the flag to make it flap in the wind
        //we want to keep the skybox around our eyes, so move it with
        //the camera
        skybox.updateGeometricState(0, true);
        // if escape was pressed, we exit
        if (KeyBindingManager.getKeyBindingManager().isValidCommand("exit")) {
            finished = true;
        //We don't want the chase camera to go below the world, so always keep
        //it 2 units above the level.
        if(cam.getLocation().y < (tb.getHeight(cam.getLocation())+2)) {
            cam.getLocation().y = tb.getHeight(cam.getLocation()) + 2;
        //make sure that if the player left the level we don't crash. When we add collisions,
        //the fence will do its job and keep the player inside.
        float characterMinHeight = tb.getHeight(player
        if (!Float.isInfinite(characterMinHeight) && !Float.isNaN(characterMinHeight)) {
            player.getLocalTranslation().y = characterMinHeight;
        //get the normal of the terrain at our current location. We then apply it to the up vector
        //of the player.
        tb.getSurfaceNormal(player.getLocalTranslation(), normal);
        if(normal != null) {
        //Because we are changing the scene (moving the skybox and player) we need to update
        //the graph.
        scene.updateGeometricState(interpolation, true);

     * draws the scene graph
     * @see com.jme.app.BaseGame#render(float)
    protected void render(float interpolation) {
       // Render the GameTaskQueue
        // Clear the screen
        /** Have the PassManager render. */

     * initializes the display and camera.
     * @see com.jme.app.BaseGame#initSystem()
    protected void initSystem() {
        // store the properties information
        width = settings.getWidth();
        height = settings.getHeight();
        depth = settings.getDepth();
        freq = settings.getFrequency();
        fullscreen = settings.isFullscreen();
        try {
            display = DisplaySystem.getDisplaySystem(settings.getRenderer());
            display.createWindow(width, height, depth, freq, fullscreen);

            cam = display.getRenderer().createCamera(width, height);
        } catch (JmeException e) {
            logger.log(Level.SEVERE, "Could not create displaySystem", e);

        // set the background to black

        // initialize the camera
        cam.setFrustum(1.0f, 1000.0f, -0.55f, 0.55f, 0.4125f, -0.4125f);
        Vector3f loc = new Vector3f(4.0f, 0.0f, 0.0f);
        Vector3f left = new Vector3f(0.0f, -1.0f, 0.0f);
        Vector3f up = new Vector3f(0.0f, 0.0f, 1.0f);
        Vector3f dir = new Vector3f(-1.0f, 0f, 0.0f);
        cam.setFrame(loc, left, up, dir);
        /** Get a high resolution timer for FPS updates. */
        timer = Timer.getTimer();


     * initializes the scene
     * @see com.jme.app.BaseGame#initGame()
    protected void initGame() {
        display.setTitle("Flag Rush");
        scene = new Node("Scene graph node");
        /** Create a ZBuffer to display pixels closest to the camera above farther ones.  */
        ZBufferState buf = display.getRenderer().createZBufferState();
        CullState cs = display.getRenderer().createCullState();
        //Add terrain to the scene
        //Add a flag randomly to the terrain
        //Light the world
        //add the force field fence
        //Add the skybox

        //Build the player
        //build the chase cam
        //build the player input
        //set up passes


        // update the scene graph for rendering
        scene.updateGeometricState(0.0f, true);
    private void buildPassManager() {
        passManager = new BasicPassManager();

        // Add skybox first to make sure it is in the background
        RenderPass rPass = new RenderPass();

//        shadowPass.addOccluder(flag);

    private void buildFlag() {
        //create the flag and place it
        flag = new Flag(tb);
    private void buildPlayer() {
        Spatial model = null;
        try {
            URL bikeFile = Lesson8.class.getClassLoader().getResource("jmetest/data/model/bike.jme");
            BinaryImporter importer = new BinaryImporter();
            model = (Spatial)importer.load(bikeFile.openStream());
            model.setModelBound(new BoundingBox());
            //scale it to be MUCH smaller than it is originally
        } catch (IOException e) {
                    .throwing(this.getClass().toString(), "buildPlayer()",
        //set the vehicles attributes (these numbers can be thought
        //of as Unit/Second).
        player = new Vehicle("Player Node", model);
        player = new Vehicle(" Player ", model);
        player.setLocalTranslation(new Vector3f(0,0, 0));

        camNode = new CameraNode( "Camera Node", cam);
        camNode.setLocalTranslation(new Vector3f(100, 0, 100));

        //camNode.setModelBound( new BoundingBox() );
        //camNode.updateGeometricState(0, true);
        player.updateGeometricState(0, true);

        agl = ((BoundingBox)player.getWorldBound()).yExtent;


    private void buildEnvironment() {
        //This is the main node of our fence
        fence = new ForceFieldFence("fence");
        //now let's move the fence to to the height of the terrain and in a little bit.
        fence.setLocalTranslation(new Vector3f(25, tb.getHeight(25,25)+10, 25));

     * creates a light for the terrain.
    private void buildLighting() {
        /** Set up a basic, default light. */
        DirectionalLight light = new DirectionalLight();
        light.setDiffuse(new ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f));
        light.setAmbient(new ColorRGBA(0.5f, 0.5f, 0.5f, .5f));
        light.setDirection(new Vector3f(1,-1,0));

          /** Attach the light to a lightState and the lightState to rootNode. */
        LightState lightState = display.getRenderer().createLightState();
        lightState.setGlobalAmbient(new ColorRGBA(.2f, .2f, .2f, 1f));

     * build the height map and terrain block.
    private void buildTerrain() {
//        MidPointHeightMap heightMap = new MidPointHeightMap(64, 1f);
        int[] heightMap = null;
        ImageIcon imageIcon = null;
        try {
           ObjectInputStream ois = new ObjectInputStream(getClass().getClassLoader().getResourceAsStream("resource/heightmap.data"));
           heightMap = (int[])ois.readObject();
           imageIcon = (ImageIcon)ois.readObject();
        } catch(Exception exc) {
           throw new RuntimeException(exc);
        float[] tempMap = new float[heightMap.length];
        for(int i = 0; i < heightMap.length; i++){
            tempMap[i] = heightMap[i];
        // Scale the data
        Vector3f terrainScale = new Vector3f(4, 0.0575f, 4);
        // create a terrainblock
         tb = new TerrainBlock("Terrain", 64, terrainScale, tempMap,
                new Vector3f(0, 0, 0));

        tb.setModelBound(new BoundingBox());
        // assign the texture to the terrain
        TextureState ts = display.getRenderer().createTextureState();
        Texture t1 = TextureManager.loadTexture(imageIcon.getImage(),
                Texture.MinificationFilter.Trilinear, Texture.MagnificationFilter.Bilinear, true);
        ts.setTexture(t1, 0);
        //load a detail texture and set the combine modes for the two terrain textures.
        Texture t2 = TextureManager.loadTexture(

        ts.setTexture(t2, 1);



        //set the detail parameters.
        tb.setDetailTexture(1, 16);
    private void buildSkyBox() {
        skybox = new Skybox("skybox", 10, 10, 10);

        Texture north = TextureManager.loadTexture(
        Texture south = TextureManager.loadTexture(
        Texture east = TextureManager.loadTexture(
        Texture west = TextureManager.loadTexture(
        Texture up = TextureManager.loadTexture(
        Texture down = TextureManager.loadTexture(

        skybox.setTexture(Skybox.Face.North, north);
        skybox.setTexture(Skybox.Face.West, west);
        skybox.setTexture(Skybox.Face.South, south);
        skybox.setTexture(Skybox.Face.East, east);
        skybox.setTexture(Skybox.Face.Up, up);
        skybox.setTexture(Skybox.Face.Down, down);
       // scene.attachChild(skybox);
    private void buildInput() {
        //input = new CustomFirstPersonHandler(cam, 50, 20, player);
        input = new NodeHandler( camNode, 15f, 1 );
    protected void reinit() {
        display.recreateWindow(width, height, depth, freq, fullscreen);
    protected void quit() {

    protected void cleanup() {


In your update you are translating the player, you should be translating the cameraNode that the player is attached to using something like:


Hope this helps

Yep,that was it… I also tried to fix cam itself but didn't work, I really have to fix camNode.

Thanx komodo,that was the problem.

Kind Regards