Question on adding fpsNode

I am trying to start using JME.  I found a good example of many things I wanted called TestIsland somewhere in the forums (nice water, land, skybox, etc).  I copied it verbatum and it seemed to work fine.  I wanted to display the fps - stuff to figure out how many triangles and such were being rendered on the scene and I have run across a problem.  My approach was to add a new StatisticsGameState to the initGame function setting the display system to enable statistics.  It sort of works but always displays the fps properly and "nothing displaying" for the statistics.  My question is am I doing this wrong?



Here is the Island code with my additional Game State 4 lines added at the end of simpleInitGame:


import java.nio.FloatBuffer;
import java.util.concurrent.Callable;

import jmetest.effects.water.TestQuadWater;

import com.jme.image.Texture;
import com.jme.math.Plane;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.renderer.pass.BasicPassManager;
import com.jme.renderer.pass.RenderPass;
import com.jme.scene.PassNode;
import com.jme.scene.PassNodeState;
import com.jme.scene.SceneElement;
import com.jme.scene.Skybox;
import com.jme.scene.Spatial;
import com.jme.scene.shape.Quad;
import com.jme.scene.state.AlphaState;
import com.jme.scene.state.CullState;
import com.jme.scene.state.FogState;
import com.jme.scene.state.LightState;
import com.jme.scene.state.RenderState;
import com.jme.scene.state.TextureState;
import com.jme.scene.state.ZBufferState;
import com.jme.system.DisplaySystem;
import com.jme.util.GameTaskQueueManager;
import com.jme.util.TextureManager;
import com.jmex.effects.water.WaterRenderPass;
import com.jmex.game.StandardGame;
import com.jmex.game.state.GameStateManager;
import com.jmex.game.state.StatisticsGameState;
import com.jmex.terrain.TerrainPage;
import com.jmex.terrain.util.RawHeightMap;

/**
 * TestTerrainSplatting shows multipass texturesplatting(6 passes) through usage
 * of the PassNode together with jME's water effect and a skybox. A simpler
 * version of the terrain without splatting is created and used for rendering
 * into the reflection/refraction of the water.
 *
 * @author Heightmap and textures originally from Jadestone(but heavily
 *         downsampled)
 * @author Rikard Herlitz (MrCoder)
 */

public class TestIsland
{

    private PassManagerGameState state;
    private BasicPassManager pManager;

    private StandardGame game;
    private WaterRenderPass waterEffectRenderPass;
    private Quad waterQuad;   
    private Spatial splatTerrain;
    private Spatial reflectionTerrain;
    private Skybox skybox;
   
    private float farPlane = 10000.0f;
    private float textureScale = 0.07f;
    private float globalSplatScale = 90.0f;
    private boolean showFps = false;

    public static void main(String[] args)
    {
        TestIsland app = new TestIsland();
    }

    public TestIsland()
    {
       game = new StandardGame( "Test Island" );
   pManager = new BasicPassManager();
   GameTaskQueueManager.getManager().update(  
                new Callable<Object>() { public Object call() throws Exception { simpleInitGame(); return null; } }
        );
   game.start();
    }
   
    protected void simpleInitGame()
    {
   pManager = new BasicPassManager();
       state = new PassManagerGameState()
                    {
                        @Override
         public void update( float tpf )
         {
            super.update(tpf);
            simpleUpdate();
                                state.getRootNode().updateGeometricState(tpf, true);
            pManager.updatePasses(tpf);
         }
                    };
       state.setPassManager( pManager );
   GameStateManager.getInstance().attachChild( state );
   //Activate game states
   state.setActive( true );

       game.getDisplay().setTitle("Test Island");

        setupEnvironment();
        createTerrain();
        createReflectionTerrain();

        buildSkyBox();

        state.getRootNode().attachChild(skybox);
        state.getRootNode().attachChild(splatTerrain);

        waterEffectRenderPass = new WaterRenderPass(game.getCamera(), 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(0);
        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);
        state.getRootNode().attachChild(waterQuad);

        waterEffectRenderPass.setReflectedScene(skybox);
        waterEffectRenderPass.addReflectedScene(reflectionTerrain);
        waterEffectRenderPass.setSkybox(skybox);
        pManager.add(waterEffectRenderPass);

        RenderPass rootPass = new RenderPass();
        rootPass.add(state.getRootNode());
        pManager.add(rootPass);

//        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(LightState.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 fpsPass = new RenderPass();
        //fpsPass.add(fpsNode);
        //pManager.add(fpsPass);
        state.getRootNode().setCullMode(SceneElement.CULL_NEVER);
        state.getRootNode().updateGeometricState( 0f, true );
        state.getRootNode().updateRenderState();
       
       
        DisplaySystem.getDisplaySystem().getRenderer().enableStatistics(true);
        StatisticsGameState stats = new StatisticsGameState();
        stats.setActive(true);
        GameStateManager.getInstance().attachChild(stats);
       
    }

    protected void simpleUpdate()
    {
   skybox.getLocalTranslation().set(game.getCamera().getLocation());
        skybox.updateGeometricState(0.0f, true);

        Vector3f transVec = new Vector3f(game.getCamera().getLocation().x,
                waterEffectRenderPass.getWaterHeight(), game.getCamera().getLocation().z);
        setTextureCoords(0, transVec.x, -transVec.z, textureScale);
        setVertexCoords(transVec.x, transVec.y, transVec.z);
    }
   
    private void createTerrain()
    {
        RawHeightMap heightMap = new RawHeightMap(
                TestIsland.class.getClassLoader().getResource("jmetest/data/texture/terrain/heights.raw").getFile(),
                129, RawHeightMap.FORMAT_16BITLE, false);

        Vector3f terrainScale = new Vector3f(5, 0.003f, 6);
        heightMap.setHeightScale(0.001f);
        TerrainPage page = new TerrainPage("Terrain", 33, heightMap.getSize(), terrainScale, heightMap.getHeightMap(), false);
        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
        AlphaState as = game.getDisplay().getRenderer().createAlphaState();
        as.setBlendEnabled(true);
        as.setSrcFunction(AlphaState.SB_SRC_ALPHA);
        as.setDstFunction(AlphaState.DB_ONE_MINUS_SRC_ALPHA);
        as.setTestEnabled(true);
        as.setTestFunction(AlphaState.TF_GREATER);
        as.setEnabled(true);

        // alpha used for blending the lightmap
        AlphaState as2 = game.getDisplay().getRenderer().createAlphaState();
        as2.setBlendEnabled(true);
        as2.setSrcFunction(AlphaState.SB_DST_COLOR);
        as2.setDstFunction(AlphaState.DB_SRC_COLOR);
        as2.setTestEnabled(true);
        as2.setTestFunction(AlphaState.TF_GREATER);
        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;
    }

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

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

        // create some interesting texturestates for splatting
        TextureState ts1 = game.getDisplay().getRenderer().createTextureState();
        Texture t0 = TextureManager.loadTexture(
                TestIsland.class.getClassLoader().getResource("jmetest/data/texture/terrain/terrainlod.jpg"),
                Texture.MM_LINEAR_LINEAR, Texture.FM_LINEAR);
        t0.setWrap(Texture.WM_WRAP_S_WRAP_T);
        t0.setApply(Texture.AM_MODULATE);
        t0.setScale(new Vector3f(1.0f, 1.0f, 1.0f));
        ts1.setTexture(t0, 0);

        // //////////////////// 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);
        // //////////////////// PASS STUFF END

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

        reflectionTerrain = splattingPassNode;

        initSpatial(reflectionTerrain);
    }

    private void setupEnvironment()
    {
        game.getCamera().setFrustumPerspective(45.0f, (float) game.getDisplay().getWidth()
                / (float) game.getDisplay().getHeight(), 1f, farPlane);
        game.getCamera().setLocation(new Vector3f(-320, 80, -270));
        game.getCamera().lookAt(new Vector3f(0, 0, 0), Vector3f.UNIT_Y);
        game.getCamera().update();

        CullState cs = game.getDisplay().getRenderer().createCullState();
        cs.setCullMode(CullState.CS_BACK);
        state.getRootNode().setRenderState(cs);

        ( (LightState)state.getRootNode().getRenderState( RenderState.RS_LIGHT ) ).detachAll();
        state.getRootNode().setLightCombineMode(LightState.OFF);

        FogState fogState = game.getDisplay().getRenderer().createFogState();
        fogState.setDensity(1.0f);
        fogState.setEnabled(true);
        fogState.setColor(new ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f));
        fogState.setEnd(farPlane);
        fogState.setStart(farPlane / 10.0f);
        fogState.setDensityFunction(FogState.DF_LINEAR);
        fogState.setApplyFunction(FogState.AF_PER_VERTEX);
        state.getRootNode().setRenderState(fogState);
    }

    private void addAlphaSplat(TextureState ts, String alpha)
    {
        Texture t1 = TextureManager.loadTexture(TestIsland.class
                .getClassLoader().getResource(alpha), Texture.MM_LINEAR_LINEAR,
                Texture.FM_LINEAR);
        t1.setWrap(Texture.WM_WRAP_S_WRAP_T);
        t1.setApply(Texture.AM_COMBINE);
        t1.setCombineFuncRGB(Texture.ACF_REPLACE);
        t1.setCombineSrc0RGB(Texture.ACS_PREVIOUS);
        t1.setCombineOp0RGB(Texture.ACO_SRC_COLOR);
        t1.setCombineFuncAlpha(Texture.ACF_REPLACE);
        ts.setTexture(t1, ts.getNumberOfSetTextures());
    }

    private TextureState createSplatTextureState(String texture, String alpha)
    {
        TextureState ts = game.getDisplay().getRenderer().createTextureState();

        Texture t0 = TextureManager.loadTexture(TestIsland.class
                .getClassLoader().getResource(texture),
                Texture.MM_LINEAR_LINEAR, Texture.FM_LINEAR);
        t0.setWrap(Texture.WM_WRAP_S_WRAP_T);
        t0.setApply(Texture.AM_MODULATE);
        t0.setScale(new Vector3f(globalSplatScale, globalSplatScale, 1.0f));
        ts.setTexture(t0, 0);

        if (alpha != null)
            addAlphaSplat(ts, alpha);

        return ts;
    }

    private TextureState createLightmapTextureState(String texture)
    {
        TextureState ts = game.getDisplay().getRenderer().createTextureState();

        Texture t0 = TextureManager.loadTexture(TestIsland.class
                .getClassLoader().getResource(texture),
                Texture.MM_LINEAR_LINEAR, Texture.FM_LINEAR);
        t0.setWrap(Texture.WM_WRAP_S_WRAP_T);
        ts.setTexture(t0, 0);

        return ts;
    }

    private void buildSkyBox()
    {
        skybox = new Skybox("skybox", 10, 10, 10);

        String dir = "jmetest/data/skybox1/";
        Texture north = TextureManager.loadTexture(TestQuadWater.class
                .getClassLoader().getResource(dir + "1.jpg"),
                Texture.MM_LINEAR, Texture.FM_LINEAR);
        Texture south = TextureManager.loadTexture(TestQuadWater.class
                .getClassLoader().getResource(dir + "3.jpg"),
                Texture.MM_LINEAR, Texture.FM_LINEAR);
        Texture east = TextureManager.loadTexture(TestQuadWater.class
                .getClassLoader().getResource(dir + "2.jpg"),
                Texture.MM_LINEAR, Texture.FM_LINEAR);
        Texture west = TextureManager.loadTexture(TestQuadWater.class
                .getClassLoader().getResource(dir + "4.jpg"),
                Texture.MM_LINEAR, Texture.FM_LINEAR);
        Texture up = TextureManager.loadTexture(TestQuadWater.class
                .getClassLoader().getResource(dir + "6.jpg"),
                Texture.MM_LINEAR, Texture.FM_LINEAR);
        Texture down = TextureManager.loadTexture(TestQuadWater.class
                .getClassLoader().getResource(dir + "5.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();

        CullState cullState = game.getDisplay().getRenderer().createCullState();
        cullState.setCullMode(CullState.CS_NONE);
        cullState.setEnabled(true);
        skybox.setRenderState(cullState);

        ZBufferState zState = game.getDisplay().getRenderer().createZBufferState();
        zState.setEnabled(false);
        skybox.setRenderState(zState);

        FogState fs = game.getDisplay().getRenderer().createFogState();
        fs.setEnabled(false);
        skybox.setRenderState(fs);

        skybox.setLightCombineMode(LightState.OFF);
        skybox.setCullMode(SceneElement.CULL_NEVER);
        skybox.setTextureCombineMode(TextureState.REPLACE);
        skybox.updateRenderState();

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

    private void setVertexCoords(float x, float y, float z)
    {
        FloatBuffer vertBuf = waterQuad.getVertexBuffer(0);
        vertBuf.clear();

        vertBuf.put(x - farPlane).put(y).put(z - farPlane);
        vertBuf.put(x - farPlane).put(y).put(z + farPlane);
        vertBuf.put(x + farPlane).put(y).put(z + farPlane);
        vertBuf.put(x + farPlane).put(y).put(z - farPlane);
    }

    private void setTextureCoords(int buffer, float x, float y, float textureScale)
    {
        x *= textureScale * 0.5f;
        y *= textureScale * 0.5f;
        textureScale = farPlane * textureScale;
        FloatBuffer texBuf;
        texBuf = waterQuad.getTextureBuffer(0, buffer);
        texBuf.clear();
        texBuf.put(x).put(textureScale + y);
        texBuf.put(x).put(y);
        texBuf.put(textureScale + x).put(y);
        texBuf.put(textureScale + x).put(textureScale + y);
    }

    private void initSpatial(Spatial spatial)
    {
        ZBufferState buf = game.getDisplay().getRenderer().createZBufferState();
        buf.setEnabled(true);
        buf.setFunction(ZBufferState.CF_LEQUAL);
        spatial.setRenderState(buf);

        CullState cs = game.getDisplay().getRenderer().createCullState();
        cs.setCullMode(CullState.CS_BACK);
        spatial.setRenderState(cs);

        spatial.setCullMode(SceneElement.CULL_NEVER);

        spatial.updateGeometricState(0.0f, true);
        spatial.updateRenderState();
    }

}


Thanks in advance

You will also need the PassManagerGameState class I had to reply with it due to size restrictions


import com.jme.image.Texture;
import com.jme.renderer.pass.BasicPassManager;
import com.jme.system.DisplaySystem;
import com.jme.util.geom.Debugger;
import com.jmex.game.state.DebugGameState;

public class PassManagerGameState extends DebugGameState
{
   private BasicPassManager pManager;
   
   public PassManagerGameState() { super(); }

   public void setPassManager( BasicPassManager pManager ) {this.pManager = pManager;}
   @Override
   public void render( float tpf )
   {
      DisplaySystem.getDisplaySystem().getRenderer().clearBuffers();
      pManager.renderPasses( DisplaySystem.getDisplaySystem().getRenderer() );

      //Instead of calling super.render() which would cause everything to be double
      //rendered, I do everything here myself.
      if (showBounds)
                    Debugger.drawBounds(rootNode, DisplaySystem.getDisplaySystem().getRenderer(), true);
                if (showNormals)
                    Debugger.drawNormals(rootNode, DisplaySystem.getDisplaySystem().getRenderer());
                if (showDepth)
                {
                    DisplaySystem.getDisplaySystem().getRenderer().renderQueue();
                    Debugger.drawBuffer(Texture.RTT_SOURCE_DEPTH, Debugger.NORTHEAST, DisplaySystem.getDisplaySystem().getRenderer());
                }
   }
}



Maybe this is the problem… you forgot to call updateGeometricState on the GameState root node, this is automatically done in SimpleGame, but since you are using a StandardGame, you need to do it manually.

if you add a:


System.out.println(DisplaySystem.getDisplaySystem().getRenderer().getStatistics());


at the end of your PassManagerGameState.render() Method. you will see the correct statistics.

It seems that the statistics text is updated before the statistics were available.