The Case of the Disapearing GUI

I am trying to implement JMEDesktop in my application to provide display elements on the screen (such as chat and event messages on the left side of the screen) and I'm getting some strange issues in ORTHO mode where the GUI will just disapear when I move the mouse around some.  At the moment I don't really care about using mouse input with Swing, I want to keep my input the same and just display stuff up on the screen in front of the 3D.  Here is the sample code I put together to demonstrate the problem:


/*
 * Created on Dec 10, 2005
 */
package com.captiveimagination.rollarama;

import java.awt.*;

import javax.swing.*;
import javax.swing.border.*;

import com.jme.app.*;
import com.jme.renderer.Renderer;
import com.jme.scene.*;
import com.jme.scene.state.*;
import com.jmex.awt.swingui.*;

/**
 * @author Matthew D. Hicks
 */
public class SwingGameTest extends SimpleGame {
    private JMEDesktop desktop;
    private Node desktopNode;

    protected void simpleInitGame() {
        desktop = new JMEDesktop("Test Frame");
        desktop.setup(display.getWidth(), display.getHeight(), false);
        desktop.setLightCombineMode(LightState.OFF);
       
        desktopNode = new Node("Desktop Node");
        desktopNode.attachChild(desktop);
        rootNode.attachChild(desktopNode);
       
        desktopNode.getLocalTranslation().set(display.getWidth() / 2, display.getHeight() / 2, 0);
        desktopNode.setRenderQueueMode(Renderer.QUEUE_ORTHO);
       
        JButton button3 = new JButton("Test Button");
        button3.setLocation(300, 100);
        button3.setSize(button3.getPreferredSize());
       
        JDesktopPane pane = desktop.getJDesktop();
        pane.add(button3);
        pane.setBackground(new Color(0.5f, 0.5f, 1.0f, 0.0f));
       
        JTextPane text = new JTextPane();
        text.setSize(100, display.getHeight() - 25);
        text.setText("TestingrnTesting AgainrnTesting even more text hopefully wrapping to the next line.");
        //text.setOpaque(false);
        text.setBackground(new Color(1.0f, 1.0f, 1.0f, 0.2f));
        text.setEditable(false);
        text.setForeground(Color.WHITE);
        text.setLocation(0, 0);
        pane.add(text);
       
        pane.repaint();
        pane.revalidate();
       
        input.setEnabled(true);
    }
   
    protected void simpleUpdate() {
    }

    public static void main(String[] args) {
        SwingGameTest sgt = new SwingGameTest();
        sgt.setDialogBehaviour(BaseGame.ALWAYS_SHOW_PROPS_DIALOG);
        sgt.start();
    }
}



I know there were some bugs, but not sure if this was one of them or I was just doing something wrong.  My bet is on the latter, but I just wanted to check. :)

Thanks,

darkfrog

Your rootNode is being culled (probably when the mouse is moving off the screen). Because you attached the GUI to the rootNode it gets culled with it. Because your GUI elements don't have any boundings they don't keep the rootNode from being culled. In most situations (read full game, you'll always have something on screen to keep it from being culled), but even still, it might be a good idea to either:



a) break your scene up into GUI elements (similar to how FPS Node is rendered) and the main scene. Keep them seperate.

b) set rootNode's cullMode to CULL_NEVER. Be careful with this though because child nodes will inherit this, so create a child that is CULL_DYNAMIC below. Basically, this is the same as (a) but organizing at the rootNode's child level.



This become less of a problem when you start building a true scene.

Doh!  Hehe, I usually do a quick mock-up to make sure I know what I'm doing before adding the complication of the rest of the implementation, but it would seem the lack of complication was the source of my problem. :-p



Thanks for the assistance Mojo!



darkfrog

My method (which is still being prototyped) is to create attach a node to rootNode called "Gui", to which I attach all widgets. This Gui node is set to never be culled and also has lighting disabled. Not sure if this is ideal but it seems to work in the very early stages.

Yeah, after I added it to Roll-A-Rama it seemed to still get culled every now and then so after telling the desktopNode to never cull it seems to work great.  I might switch to doing something outside of the rootNode in the future, but for now this is easier.



darkfrog

I'm having the same problem:


//required import statements
import com.jme.app.SimpleGame;
import com.jme.bounding.BoundingBox;
import com.jme.image.Texture;
import com.jme.input.NodeHandler;
import com.jme.math.Quaternion;
import com.jme.math.Vector3f;
import com.jme.scene.shape.Sphere;
import com.jme.scene.state.TextureState;
import com.jme.util.LoggingSystem;
import com.jme.util.TextureManager;
import com.jme.scene.shape.Quad;
import com.jme.renderer.RenderQueue;
import com.jme.renderer.Renderer;
import com.jme.scene.state.LightState;
import com.jme.scene.state.CullState;
import com.jme.scene.Node;
import com.jme.scene.Spatial;


/**
 * TestSphere extends SimpleGame giving us a basic framework.
 */
public class TestQuad extends SimpleGame {
  //required values for rotating the sphere
  private Quaternion rotQuat = new Quaternion();
  private float angle = 0;
  private Vector3f axis = new Vector3f(1, 1, 0);
 
  //the Sphere to render
  private Sphere s;
 
  /**
   * Entry point for the test,
   * @param args
   */
  public static void main(String[] args) {
    LoggingSystem.getLogger().setLevel(java.util.logging.Level.OFF);
    TestQuad app = new TestQuad();
    app.setDialogBehaviour(ALWAYS_SHOW_PROPS_DIALOG);
    app.start();
  }
 
  /**
   * updates an angle and applies it to a quaternion
   * to rotate the Sphere.
   */
  protected void simpleUpdate() {
    if (tpf < 1) {
      angle = angle + (tpf * 1);
      if (angle > 360) {
        angle = 0;
      }
    }
    //rotQuat.fromAngleAxis(angle, axis);
    //s.setLocalRotation(rotQuat);
  }
 
  /**
   * builds the Sphere and applies the Monkey texture.
   */
  protected void simpleInitGame() {
    display.setTitle("jME - Sphere");

    s = new Sphere("Sphere", 63, 50, 25);
    s.setLocalTranslation(new Vector3f(0,0,-40));
    s.setModelBound(new BoundingBox());
    s.updateModelBound();
    rootNode.attachChild(s);
   
    TextureState ts = display.getRenderer().createTextureState();
    ts.setEnabled(true);
    ts.setTexture(
        TextureManager.loadTexture(
        TestQuad.class.getClassLoader().getResource(
        "jmetest/data/images/Monkey.jpg"),
        Texture.MM_LINEAR_LINEAR,
        Texture.FM_LINEAR));
   
    TextureState font = display.getRenderer().createTextureState();
    font.setEnabled(true);
    font.setTexture(
        TextureManager.loadTexture(
        TestQuad.class.getClassLoader().getResource(
        "jmetest/data/font/font.png"),
        Texture.MM_LINEAR_LINEAR,
        Texture.FM_LINEAR));
 
    s.setRenderState(ts);
   
    Node hudNode = new Node("hudNode");
    Quad hudQuad = new Quad("hud", 50f, 50f);
    hudNode.setRenderQueueMode(Renderer.QUEUE_ORTHO);       
   
    CullState cs = display.getRenderer().createCullState();
    cs.setCullMode( Spatial.CULL_NEVER );
    hudNode.setRenderState(cs); 

    LightState ls = display.getRenderer().createLightState();
    ls.setEnabled(false);
    hudNode.setRenderState(ls);

    hudNode.updateRenderState();

    hudNode.attachChild(hudQuad);
    rootNode.attachChild(hudNode);
    }
}

CullState does not take values from Spatial.  You are not looking for Cull state… What you are looking for is to do this:


hudNode.setCullMode(Spatial.CULL_NEVER);

Ok, I tried that but it's still getting culled.

Ok, well you attach it to rootnode, so rootnode will need the same flag set to prevent culling of the whole scene at that level.



You'll need to set other things you attach to rootNode (aside from hudNode) to CULL_DYNAMIC though if you want them to still cull when they are off screen.

Oh I get it now. I don't like it, but I understand it. :slight_smile:

The alternative is to not attach it to the rootNode and just do it seperately just like rootNode is drawn.



darkfrog

or like fpsNode… which was a basic model for how gui elements were meant to be done.  :slight_smile:

Doh! SimpleGame.Render is final, so I can't override it to try this out.

You could use something other than simplegame…  but why would you need to override render?

renanse said:

You could use something other than simplegame...  but why would you need to override render?


For very quick and very easy prototyping.  :D

either override simpleRender or extend BaseSimpleGame instead…

same Problem with StandardGameState.

stateRender is called BEFORE the rootnode is rendered

Seperate Nodes for the scene and the gui would probably require staterender to be called AFTER the rootnode gets rendered.



How about the following modification ?

In StandardGameState replace stateRender with preRootRender and postRootRender


public final void render(float tpf) {
      preRootRender(tpf);
      super.render(tpf);
      postRootRender(tpf);

   }

yuck



Why not start your game and then after the first update is called add your gamestate to the game?  That's what I'm doing in my game currently.  I wrote GameManager that handles all the initialization and update and render calls for the GameStateManager and I added a method waitForStatus that doesn't return until a certain status has been reached in the game, namely STATUS_MAIN_LOOP.  This is just an example of a way you can do it.



darkfrog