NPE in simpleUpdate

Hello everyone! I'm trying to get familiar with jME2 and I've run into something that I don't quite understand. I'm playing around with TestIsland and I wanted to make it so that the camera wouldn't go below the terrain. I frankenstein'd a piece of code from the flag rush tutorial into TestIsland like so:

protected void simpleUpdate() {
        skybox.getLocalTranslation().set(cam.getLocation());
        skybox.updateGeometricState(0.0f, 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 < (page.getHeight(cam.getLocation())+2)) { // This line causes a NPE
            cam.getLocation().y = page.getHeight(cam.getLocation()) + 2;
            cam.update();
        }

        Vector3f transVec = new Vector3f(cam.getLocation().x,
                waterEffectRenderPass.getWaterHeight(), cam.getLocation().z);
        setTextureCoords(0, transVec.x, -transVec.z, textureScale);
        setVertexCoords(transVec.x, transVec.y, transVec.z);
    }



the code originally in the flag rush tutorial looks like this:

protected void update(float interpolation) {
        // update the time to get the framerate
        timer.update();
        interpolation = timer.getTimePerFrame();
        //update the keyboard input (move the player around)
        input.update(interpolation);
        //update the chase camera to handle the player moving around.
        chaser.update(interpolation);

        fence.update(interpolation);
       
        //we want to keep the skybox around our eyes, so move it with
        //the camera
        skybox.setLocalTranslation(cam.getLocation());
       
        // 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;
            cam.update();
        }
       
        //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
                .getLocalTranslation())+((BoundingBox)player.getWorldBound()).yExtent;
        if (!Float.isInfinite(characterMinHeight) && !Float.isNaN(characterMinHeight)) {
            player.getLocalTranslation().y = characterMinHeight;
        }
       
        //Because we are changing the scene (moving the skybox and player) we need to update
        //the graph.
        scene.updateGeometricState(interpolation, true);
    }



My question is why would it not cause problems in update, but causes the program to crash with a Null Pointer Exception when put in simpleUpdate? What is the difference between the two?

**edit**

Here is the error:

Jan 3, 2009 12:25:32 PM class jMonkeyEngineTest start()
SEVERE: Exception in game loop
java.lang.NullPointerException
   at jMonkeyEngineTest.simpleUpdate(jMonkeyEngineTest.java:117)
   at com.jme.app.SimplePassGame.update(SimplePassGame.java:62)
   at com.jme.app.BaseGame.start(BaseGame.java:84)
   at jMonkeyEngineTest.main(jMonkeyEngineTest.java:105)
Jan 3, 2009 12:25:32 PM com.jme.app.BaseSimpleGame cleanup
INFO: Cleaning up resources.
Jan 3, 2009 12:25:32 PM com.jme.app.BaseGame start
INFO: Application ending.

Something to do with the terrain page, since the camera was accessed directly above it (and it did not throw NPE)…

Whoops, you were right. I was slapping things together so quickly, I didn't realize I was having it try to access a global TerrainPage with no value. Now when it generates the terrain, it accesses the global instead of creating a new one. Thanks!!! :smiley:

A couple new issues though. I changed the game type from SimplePassGame to SimplePhysicsGame to play around with the physics engine. Upon doing that, I had to comment out the lines of code that made the very nice looking water. Is there any way of implementing physics into a SimplePassGame or any other way of having both the physics and the water?

My second issue, is that upon having the createTerrain function access the global TerrainPage, everything is now white. I remember reading about this having something to do with lighting, but I can't remember where I read that. Here is some of the code. If you can lend a hand, it would be much appreciated.

protected void simpleInitGame() {
        display.setTitle("Test Island");

        setupEnvironment();

        createTerrain();
        createReflectionTerrain();

        buildSkyBox();

        rootNode.attachChild(skybox);
        rootNode.attachChild(splatTerrain);

        waterEffectRenderPass = new WaterRenderPass(cam, 6, false, true);
        waterEffectRenderPass.setWaterPlane(new Plane(new Vector3f(0.0f, 1.0f,
                0.0f), 0.0f));
        waterEffectRenderPass.setClipBias(-1.0f);
        waterEffectRenderPass.setReflectionThrottle(0.0f);
        waterEffectRenderPass.setRefractionThrottle(0.0f);

        waterQuad = new Quad("waterQuad", 1, 1);
        FloatBuffer normBuf = waterQuad.getNormalBuffer();
        normBuf.clear();
        normBuf.put(0).put(1).put(0);
        normBuf.put(0).put(1).put(0);
        normBuf.put(0).put(1).put(0);
        normBuf.put(0).put(1).put(0);

        waterEffectRenderPass.setWaterEffectOnSpatial(waterQuad);
        rootNode.attachChild(waterQuad);

        waterEffectRenderPass.setReflectedScene(skybox);
        waterEffectRenderPass.addReflectedScene(reflectionTerrain);
        waterEffectRenderPass.setSkybox(skybox);
        //pManager.add(waterEffectRenderPass); // add this back for water (seems to only work in SimplePassGame)

        RenderPass rootPass = new RenderPass();
        rootPass.add(rootNode);
        //pManager.add(rootPass); // add this back for water

        // BloomRenderPass bloomRenderPass = new BloomRenderPass(cam, 4);
        // if (!bloomRenderPass.isSupported()) {
        // Text t = new Text("Text", "GLSL Not supported on this computer.");
        // t.setRenderQueueMode(Renderer.QUEUE_ORTHO);
        // t.setLightCombineMode(Spatial.LightCombineMode.Off);
        // t.setLocalTranslation(new Vector3f(0, 20, 0));
        // fpsNode.attachChild(t);
        // } else {
        // bloomRenderPass.setExposurePow(2.0f);
        // bloomRenderPass.setBlurIntensityMultiplier(0.5f);
        //           
        // bloomRenderPass.add(rootNode);
        // bloomRenderPass.setUseCurrentScene(true);
        // pManager.add(bloomRenderPass);
        // }

        RenderPass statPass = new RenderPass();
        statPass.add(statNode);
        //pManager.add(statPass); // add this back for water

        rootNode.setCullHint(Spatial.CullHint.Never);
        rootNode.setCullHint(Spatial.CullHint.Never);
       
       
        input.addAction(new FireBullet(), "firebullet", KeyInput.KEY_F, false);

        /** Make bullet material */
        bulletMaterial = display.getRenderer().createMaterialState();
        bulletMaterial.setEmissive(ColorRGBA.green.clone());
       
     // first we will create the floor
        // as the floor can't move we create a _static_ physics node
        StaticPhysicsNode staticNode = getPhysicsSpace().createStaticNode();
       
     // attach the node to the root node to have it updated each frame
        staticNode.attachChild( page );
       
     // we have to attach the terrain to our node
        rootNode.attachChild( staticNode );
        staticNode.generatePhysicsGeometry();
       
     // second we create a box that should fall down on the floor
        // as the new box should move we create a _dynamic_ physics node
        DynamicPhysicsNode dynamicNode = getPhysicsSpace().createDynamicNode();
        rootNode.attachChild( dynamicNode );
       
        Box b = new Box("Mybox",
        new Vector3f(0,200,100),
        new Vector3f(100,300,150));
        rootNode.attachChild(b);
       
     // attach it to the dynamic node
        dynamicNode.attachChild( b );
       
     // and generate collision geometries again
        dynamicNode.generatePhysicsGeometry();
    }

private void createTerrain() {
        RawHeightMap heightMap = new RawHeightMap(jMonkeyEngineTest.class
                .getClassLoader().getResource(
                        "jmetest/data/texture/terrain/heights.raw"),
                129, RawHeightMap.FORMAT_16BITLE, false);

        Vector3f terrainScale = new Vector3f(5, 0.003f, 6);
        heightMap.setHeightScale(0.001f);
        page = new TerrainPage("Terrain", 33, heightMap.getSize(),
                terrainScale, heightMap.getHeightMap());
        page.getLocalTranslation().set(0, -9.5f, 0);
        page.setDetailTexture(1, 1);

        // create some interesting texturestates for splatting
        TextureState ts1 = createSplatTextureState(
                "jmetest/data/texture/terrain/baserock.jpg", null);

        TextureState ts2 = createSplatTextureState(
                "jmetest/data/texture/terrain/darkrock.jpg",
                "jmetest/data/texture/terrain/darkrockalpha.png");

        TextureState ts3 = createSplatTextureState(
                "jmetest/data/texture/terrain/deadgrass.jpg",
                "jmetest/data/texture/terrain/deadalpha.png");

        TextureState ts4 = createSplatTextureState(
                "jmetest/data/texture/terrain/nicegrass.jpg",
                "jmetest/data/texture/terrain/grassalpha.png");

        TextureState ts5 = createSplatTextureState(
                "jmetest/data/texture/terrain/road.jpg",
                "jmetest/data/texture/terrain/roadalpha.png");

        TextureState ts6 = createLightmapTextureState("jmetest/data/texture/terrain/lightmap.jpg");

        // alpha used for blending the passnodestates together
        BlendState as = display.getRenderer().createBlendState();
        as.setBlendEnabled(true);
        as.setSourceFunction(BlendState.SourceFunction.SourceAlpha);
        as.setDestinationFunction(BlendState.DestinationFunction.OneMinusSourceAlpha);
        as.setTestEnabled(true);
        as.setTestFunction(BlendState.TestFunction.GreaterThan);
        as.setEnabled(true);

        // alpha used for blending the lightmap
        BlendState as2 = display.getRenderer().createBlendState();
        as2.setBlendEnabled(true);
        as2.setSourceFunction(BlendState.SourceFunction.DestinationColor);
        as2.setDestinationFunction(BlendState.DestinationFunction.SourceColor);
        as2.setTestEnabled(true);
        as2.setTestFunction(BlendState.TestFunction.GreaterThan);
        as2.setEnabled(true);

        // //////////////////// PASS STUFF START
        // try out a passnode to use for splatting
        PassNode splattingPassNode = new PassNode("SplatPassNode");
        splattingPassNode.attachChild(page);

        PassNodeState passNodeState = new PassNodeState();
        passNodeState.setPassState(ts1);
        splattingPassNode.addPass(passNodeState);

        passNodeState = new PassNodeState();
        passNodeState.setPassState(ts2);
        passNodeState.setPassState(as);
        splattingPassNode.addPass(passNodeState);

        passNodeState = new PassNodeState();
        passNodeState.setPassState(ts3);
        passNodeState.setPassState(as);
        splattingPassNode.addPass(passNodeState);

        passNodeState = new PassNodeState();
        passNodeState.setPassState(ts4);
        passNodeState.setPassState(as);
        splattingPassNode.addPass(passNodeState);

        passNodeState = new PassNodeState();
        passNodeState.setPassState(ts5);
        passNodeState.setPassState(as);
        splattingPassNode.addPass(passNodeState);

        passNodeState = new PassNodeState();
        passNodeState.setPassState(ts6);
        passNodeState.setPassState(as2);
        splattingPassNode.addPass(passNodeState);
        // //////////////////// PASS STUFF END

        // lock some things to increase the performance
        splattingPassNode.lockBounds();
        splattingPassNode.lockTransforms();
        splattingPassNode.lockShadows();

        splatTerrain = splattingPassNode;
        splatTerrain.setCullHint(Spatial.CullHint.Dynamic);

    }

Okay, I fixed the whiteness problem by moving the static object physics declarations into the createTerrain function. I'm still not really sure what was causing that problem. My other question about the water still stands, though. :slight_smile:

edit

Okay, I fixed the water thing too. I just added the multi-pass rendering management stuff from SimplePassGame into SimplePhysicsGame. Thanks for your help with the other thing, Basixs.