Lemur popup issue

I have issues with this code and Lemur popup; I’ve omitted the actual popup code as it’s probably not relevant (it works as expected without this part).

What I’m doing: if the Display don’t have a 16/9 ratio, I add black bars and scale to fit.

public class HelloJME3 extends SimpleApplication {
    public Node scaledGuiNode = new Node("ScaledGUINode");

    public static void main(String[] args){
        HelloJME3 app = new HelloJME3();
        app.start(); // start the game
    }

    @Override
    public void simpleInitApp() {
       
        GuiGlobals.initialize(this);
        GuiGlobals globals = GuiGlobals.getInstance();
        BaseStyles.loadGlassStyle();
        globals.getStyles().setDefaultStyle("glass");
        
        guiNode.attachChild(scaledGuiNode);
        float scalingFactor = Display.getWidth() / 1920f;
        scaledGuiNode.scale(scalingFactor);
        int hOffset = (int) ((Display.getHeight() - (1080f * scalingFactor)) / 2);
        if (hOffset != 0) {
            Geometry g = new Geometry("blackbg", new Quad(Display.getWidth(), hOffset));
            Geometry gtop = new Geometry("blackbg", new Quad(Display.getWidth(), hOffset));
            Material matBg = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
            matBg.setColor("Color", ColorRGBA.Black);
            g.setMaterial(matBg);
            g.move(0, 0, 999);
            guiNode.attachChild(g);
            gtop.setMaterial(matBg);
            gtop.move(0, Display.getHeight() - hOffset, 999);
            guiNode.attachChild(gtop);
            scaledGuiNode.setLocalTranslation(0, hOffset, 0);
        }
        
        
        stateManager.attach(new MainMenuAppState());
    }
}

Forgot to mention: the issue is that events aren’t received by the popup (but they do close the popup).

Just quickly, maybe try:

scaledGuiNode.scale(scalingFactor, scalingFactor, 1);

Edit: wait… which part is the code that if you leave it out then the popup works as expected? I’m confused.

Edit 2: as an aside, placing black bars top/bottom is a little wasteful versus just adjusting the viewport top/bottom. It means you are taking time painting parts of the screen that will never be seen.

This is the full code:


import com.jme3.app.SimpleApplication;
import com.jme3.material.Material;
import com.jme3.scene.Geometry;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import com.jme3.scene.shape.Quad;
import static com.pesegato.p8s.FullHDApplication.DISPLAY_VERSION;
import com.pesegato.p8s.SaveSlot;
import com.pesegato.p8s.appstates.MainMenuAppState;
import com.simsilica.lemur.ActionButton;
import com.simsilica.lemur.CallMethodAction;
import com.simsilica.lemur.Container;
import com.simsilica.lemur.GuiGlobals;
import com.simsilica.lemur.HAlignment;
import com.simsilica.lemur.Insets3f;
import com.simsilica.lemur.Label;
import com.simsilica.lemur.style.BaseStyles;
import java.io.File;
import java.util.Date;
import org.lwjgl.opengl.Display;

/**
 * Sample 1 - how to get started with the most simple JME 3 application. Display
 * a blue 3D cube and view from all sides by moving the mouse and pressing the
 * WASD keys.
 */
public class HelloJME3 extends SimpleApplication {

    private Container mainWindow;
    private Container loadWindow;
    public Node scaledGuiNode = new Node("ScaledGUINode");

    public float getStandardScale() {
        int height = getCamera().getHeight();
        return height / 720f;
    }

    public static void main(String[] args) {
        HelloJME3 app = new HelloJME3();
        app.start(); // start the game
    }

    @Override
    public void simpleInitApp() {

        GuiGlobals.initialize(this);
        GuiGlobals globals = GuiGlobals.getInstance();
        BaseStyles.loadGlassStyle();
        globals.getStyles().setDefaultStyle("glass");

// start comment here
        guiNode.attachChild(scaledGuiNode);
        float scalingFactor = Display.getWidth() / 1920f;
        scaledGuiNode.scale(scalingFactor);
        int hOffset = (int) ((Display.getHeight() - (1080f * scalingFactor)) / 2);
        if (hOffset != 0) {
            Geometry g = new Geometry("blackbg", new Quad(Display.getWidth(), hOffset));
            Geometry gtop = new Geometry("blackbg", new Quad(Display.getWidth(), hOffset));
            Material matBg = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
            matBg.setColor("Color", ColorRGBA.Black);
            g.setMaterial(matBg);
            g.move(0, 0, 999);
            guiNode.attachChild(g);
            gtop.setMaterial(matBg);
            gtop.move(0, Display.getHeight() - hOffset, 999);
            guiNode.attachChild(gtop);
            scaledGuiNode.setLocalTranslation(0, hOffset, 0);
        }
//end comment here
        mainWindow = new Container();

        Label title = mainWindow.addChild(new Label("MyApp"));
        title.setFontSize(32);
        title.setInsets(new Insets3f(10, 10, 0, 10));
        Label subtitle = mainWindow.addChild(new Label(DISPLAY_VERSION + " build"));
        subtitle.setInsets(new Insets3f(0, 10, 0, 10));
        subtitle.setTextHAlignment(HAlignment.Right);
        subtitle.setColor(new ColorRGBA(166 / 255f, 107 / 255f, 255 / 255f, 0.85f));
        ActionButton play = mainWindow.addChild(new ActionButton(new CallMethodAction("Campaign mode", this, "campaignMode")));
        play.setInsets(new Insets3f(10, 10, 10, 10));
        // Calculate a standard scale and position from the app's camera
        // height
        int height = getCamera().getHeight();
        Vector3f pref = mainWindow.getPreferredSize().clone();

        float standardScale = getStandardScale();
        pref.multLocal(1.5f * standardScale);

        // With a slight bias toward the top        
        float y = height * 0.6f + pref.y * 0.5f;

        mainWindow.setLocalTranslation(100 * standardScale, y, 0);
        mainWindow.setLocalScale(1.5f * standardScale);

        loadWindow = new Container();
        loadWindow.setLocalTranslation(400 * standardScale, y - 200, 0);
        loadWindow.setLocalScale(1.5f * standardScale);

        Node gui = ((SimpleApplication) this).getGuiNode();
        gui.attachChild(mainWindow);
        GuiGlobals.getInstance().requestFocus(mainWindow);

    }

    public void campaignMode() {
        for (int i = 0; i < 3; i++) {
            ActionButton load = loadWindow.addChild(new ActionButton(new CallMethodAction("aaa", this, "loadSlot")));
            load.setInsets(new Insets3f(10, 10, 10, 10));
        }
        GuiGlobals.getInstance().getPopupState().showPopup(loadWindow);
        //((SimpleApplication) getApplication()).getGuiNode().attachChild(loadWindow);

    }

    public void loadSlot() {
//if you don't comment the part above, you'll never get here
    }
}

Didn’t help, but is useful advice anyway.

For what it’s worth, another case of uniform scale where z should probably be 1.

The only thing I can think is because you’ve set those bars so high in Z, the popups will be rendered over them (they are always placed above anything else in the guiNode). It’s possible that this is beyond the range that the picking rays cast.

Actually, I’m almost sure that’s it. If you lower the black bars to 50 instead of 99 then I bet it works… but really this is a poor way to have a letter-boxed viewport.

1 Like

Indeed it does! :slight_smile:

Now that you made me interested into viewport: I think is set from the camera, but the app camera is supposed to work with the rootNode rather than the guiNode?

Is there a tutorial for that?

Don’t you want to letter box the scene?

If so, just get the main viewport, and set the top/bottom differently… then adjust the camera’s perspective settings to have the proper aspect ratio (16:9).

I don’t know the code off the top of my head but it shouldn’t be too difficult.

So I did

    cam.setViewPortBottom(0.2f);
    cam.setFrustumPerspective(40, 16/9f, 0.05f, 500f);        

and the popup was a bit brightened on the bottom part: :confounded:

Well, it’s hard to see what it’s doing to the 3D scene without a 3D scene.

The guiNode is done in a special way and may not be covered by this approach.

…there will be another problem though and I think that’s why it gets brighter. If nothing is drawing at the bottom then it won’t get cleared each frame… so you are seeing the panel drawn on top of itself for several frames.

If you don’t care about the wastefulness of just blocking off parts of the screen, another easy approach would be to create a post viewport on top of everything and then put your black bars in that. There is nothing else special about the gui node other than that it’s the root of its viewport and has Bucket.Gui. So you can create your own new GUI viewport for your black bars.

After checking (again) in the wiki, I didn’t found how to do what you suggest.
Maybe this:

ViewPort v=renderManager.createPostView("black bars", cam);

(but still not sure what to do next)

Or create a post processor and add it to the viewport?

You will need to add a root node to the viewport and set it to the gui bucket.

If you never change the contents in the viewport, I think you can get away with calling updateLogicalState() and updateGeometricState() just once on that root. Else you will have to manage the viewport by calling those once per frame. There are example app states around on the forum somewhere.

I think for static content, you don’t need to do that, though.

Full black screen


import com.jme3.app.SimpleApplication;
import com.jme3.material.Material;
import com.jme3.scene.Geometry;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Node;
import com.jme3.scene.shape.Quad;
import static com.pesegato.p8s.FullHDApplication.DISPLAY_VERSION;
import com.pesegato.p8s.SaveSlot;
import com.pesegato.p8s.appstates.MainMenuAppState;
import com.simsilica.lemur.ActionButton;
import com.simsilica.lemur.CallMethodAction;
import com.simsilica.lemur.Container;
import com.simsilica.lemur.GuiGlobals;
import com.simsilica.lemur.HAlignment;
import com.simsilica.lemur.Insets3f;
import com.simsilica.lemur.Label;
import com.simsilica.lemur.style.BaseStyles;
import java.io.File;
import java.util.Date;
import org.lwjgl.opengl.Display;

/**
 * Sample 1 - how to get started with the most simple JME 3 application. Display
 * a blue 3D cube and view from all sides by moving the mouse and pressing the
 * WASD keys.
 */
public class HelloJME3 extends SimpleApplication {

    private Container mainWindow;
    private Container loadWindow;
    public Node scaledGuiNode = new Node("ScaledGUINode");

    public float getStandardScale() {
        int height = getCamera().getHeight();
        return height / 720f;
    }

    public static void main(String[] args) {
        HelloJME3 app = new HelloJME3();
        app.start(); // start the game
    }

    @Override
    public void simpleInitApp() {

        GuiGlobals.initialize(this);
        GuiGlobals globals = GuiGlobals.getInstance();
        BaseStyles.loadGlassStyle();
        globals.getStyles().setDefaultStyle("glass");

        Node myRoot=new Node();
        
        myRoot.attachChild(scaledGuiNode);
        
        /*
        float scalingFactor = Display.getWidth() / 1920f;
        scaledGuiNode.scale(scalingFactor,scalingFactor,1);
        int hOffset = (int) ((Display.getHeight() - (1080f * scalingFactor)) / 2);
        if (hOffset != 0) {
            Geometry g = new Geometry("blackbg", new Quad(Display.getWidth(), hOffset));
            Geometry gtop = new Geometry("blackbg", new Quad(Display.getWidth(), hOffset));
            Material matBg = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
            matBg.setColor("Color", ColorRGBA.Black);
            g.setMaterial(matBg);
            g.move(0, 0, 50);
            guiNode.attachChild(g);
            gtop.setMaterial(matBg);
            gtop.move(0, Display.getHeight() - hOffset, 50);
            guiNode.attachChild(gtop);
            scaledGuiNode.setLocalTranslation(0, hOffset, 0);
        }
*/
        mainWindow = new Container();

        Label title = mainWindow.addChild(new Label("MyApp"));
        title.setFontSize(32);
        title.setInsets(new Insets3f(10, 10, 0, 10));
        Label subtitle = mainWindow.addChild(new Label(DISPLAY_VERSION + " build"));
        subtitle.setInsets(new Insets3f(0, 10, 0, 10));
        subtitle.setTextHAlignment(HAlignment.Right);
        subtitle.setColor(new ColorRGBA(166 / 255f, 107 / 255f, 255 / 255f, 0.85f));
        ActionButton play = mainWindow.addChild(new ActionButton(new CallMethodAction("Campaign mode", this, "campaignMode")));
        play.setInsets(new Insets3f(10, 10, 10, 10));
        // Calculate a standard scale and position from the app's camera
        // height
        int height = getCamera().getHeight();
        Vector3f pref = mainWindow.getPreferredSize().clone();

        float standardScale = getStandardScale();
        pref.multLocal(1.5f * standardScale);

        // With a slight bias toward the top        
        float y = height * 0.6f + pref.y * 0.5f;

        mainWindow.setLocalTranslation(100 * standardScale, y, 0);
        mainWindow.setLocalScale(1.5f * standardScale);

        loadWindow = new Container();
        loadWindow.setLocalTranslation(400 * standardScale, y - 200, 0);
        loadWindow.setLocalScale(1.5f * standardScale);

        myRoot.attachChild(mainWindow);
        GuiGlobals.getInstance().requestFocus(mainWindow);
        viewPort.attachScene(myRoot);
        myRoot.updateLogicalState(0);
        myRoot.updateGeometricState();

    }

    public void campaignMode() {
        for (int i = 0; i < 3; i++) {
            ActionButton load = loadWindow.addChild(new ActionButton(new CallMethodAction("aaa", this, "loadSlot")));
            load.setInsets(new Insets3f(10, 10, 10, 10));
        }
        GuiGlobals.getInstance().getPopupState().showPopup(loadWindow);
        //((SimpleApplication) getApplication()).getGuiNode().attachChild(loadWindow);

    }

    public void loadSlot() {
    }
}

I don’t see anywhere that you are creating your own ViewPort… you are just mucking with the default ViewPort.

Sorry, now I can see that I misunderstood your suggestion. Made progress (must fix aspect ratio), but

  • mouse event aren’t processed
  • and pressing Enter gives

Uncaught exception thrown in Thread[jME3 Main,5,main]
IllegalStateException: Scene graph is not properly updated for rendering.
State was changed after rootNode.updateGeometricState() call.
Make sure you do not modify the scene from another thread!
Problem spatial name: null

Code:


import com.jme3.app.SimpleApplication;
import com.jme3.material.Material;
import com.jme3.scene.Geometry;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.renderer.ViewPort;
import com.jme3.renderer.queue.RenderQueue;
import com.jme3.scene.Node;
import com.jme3.scene.shape.Quad;
import static com.pesegato.p8s.FullHDApplication.DISPLAY_VERSION;
import com.pesegato.p8s.SaveSlot;
import com.pesegato.p8s.appstates.MainMenuAppState;
import com.simsilica.lemur.ActionButton;
import com.simsilica.lemur.CallMethodAction;
import com.simsilica.lemur.Container;
import com.simsilica.lemur.GuiGlobals;
import com.simsilica.lemur.HAlignment;
import com.simsilica.lemur.Insets3f;
import com.simsilica.lemur.Label;
import com.simsilica.lemur.style.BaseStyles;
import java.io.File;
import java.util.Date;
import org.lwjgl.opengl.Display;

/**
 * Sample 1 - how to get started with the most simple JME 3 application. Display
 * a blue 3D cube and view from all sides by moving the mouse and pressing the
 * WASD keys.
 */
public class HelloJME3 extends SimpleApplication {

    private Container mainWindow;
    private Container loadWindow;
    public Node scaledGuiNode = new Node("ScaledGUINode");

    public float getStandardScale() {
        int height = getCamera().getHeight();
        return height / 720f;
    }

    public static void main(String[] args) {
        HelloJME3 app = new HelloJME3();
        app.start(); // start the game
    }

    @Override
    public void simpleInitApp() {

        GuiGlobals.initialize(this);
        GuiGlobals globals = GuiGlobals.getInstance();
        BaseStyles.loadGlassStyle();
        globals.getStyles().setDefaultStyle("glass");

        myRoot = new Node();

        ViewPort myView = renderManager.createPostView("black bars", cam);

        
        float scalingFactor = Display.getWidth() / 1920f;
        scaledGuiNode.scale(scalingFactor,scalingFactor,1);
        float hOffset = (int) ((Display.getHeight() - (1080f * scalingFactor)) / 2);
        /*
        if (hOffset != 0) {
            Geometry g = new Geometry("blackbg", new Quad(Display.getWidth(), hOffset));
            Geometry gtop = new Geometry("blackbg", new Quad(Display.getWidth(), hOffset));
            Material matBg = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
            matBg.setColor("Color", ColorRGBA.Black);
            g.setMaterial(matBg);
            g.move(0, 0, 50);
            guiNode.attachChild(g);
            gtop.setMaterial(matBg);
            gtop.move(0, Display.getHeight() - hOffset, 50);
            guiNode.attachChild(gtop);
            scaledGuiNode.setLocalTranslation(0, hOffset, 0);
        }
         */
        cam.setViewPortBottom(hOffset/Display.getHeight());
        cam.setViewPortTop((Display.getHeight()-hOffset)/Display.getHeight());
        cam.setFrustumPerspective(40, 16 / 9f, 0.05f, 500f);
        myRoot.attachChild(scaledGuiNode);
        
        mainWindow = new Container();

        Label title = mainWindow.addChild(new Label("MyApp"));
        title.setFontSize(32);
        title.setInsets(new Insets3f(10, 10, 0, 10));
        Label subtitle = mainWindow.addChild(new Label(DISPLAY_VERSION + " build"));
        subtitle.setInsets(new Insets3f(0, 10, 0, 10));
        subtitle.setTextHAlignment(HAlignment.Right);
        subtitle.setColor(new ColorRGBA(166 / 255f, 107 / 255f, 255 / 255f, 0.85f));
        ActionButton play = mainWindow.addChild(new ActionButton(new CallMethodAction("Campaign mode", this, "campaignMode")));
        play.setInsets(new Insets3f(10, 10, 10, 10));
        // Calculate a standard scale and position from the app's camera
        // height
        int height = getCamera().getHeight();
        Vector3f pref = mainWindow.getPreferredSize().clone();

        float standardScale = getStandardScale();
        pref.multLocal(1.5f * standardScale);

        // With a slight bias toward the top        
        float y = height * 0.6f + pref.y * 0.5f;

        mainWindow.setLocalTranslation(100 * standardScale, y, 0);
        mainWindow.setLocalScale(1.5f * standardScale);

        loadWindow = new Container();
        loadWindow.setLocalTranslation(400 * standardScale, y - 200, 0);
        loadWindow.setLocalScale(1.5f * standardScale);

        myRoot.attachChild(mainWindow);
        GuiGlobals.getInstance().requestFocus(mainWindow);
        myView.attachScene(myRoot);
        myRoot.setQueueBucket(RenderQueue.Bucket.Gui);
        myRoot.updateLogicalState(0);
        myRoot.updateGeometricState();

    }
    Node myRoot;

    public void update(float tpf) {
        myRoot.updateLogicalState(0);
        myRoot.updateGeometricState();
    }

    public void campaignMode() {
        for (int i = 0; i < 3; i++) {
            ActionButton load = loadWindow.addChild(new ActionButton(new CallMethodAction("aaa", this, "loadSlot")));
            load.setInsets(new Insets3f(10, 10, 10, 10));
        }
        GuiGlobals.getInstance().getPopupState().showPopup(loadWindow);
        //((SimpleApplication) getApplication()).getGuiNode().attachChild(loadWindow);

    }

    public void loadSlot() {
    }
}

Oh wait… maybe I should put the bars on the new viewport and leave the scenegraph as is? Must try…

Whoa, that worked! :slight_smile:

So: by adding this new viewport and node I’m actually improving performance?

No. This is the “simpler way that works but doesn’t improve performance”. You are still drawing parts of the screen you don’t need to just to cover them with a black bar.

The other approach is similar but more complicated… though you might be able to do it with a preview port that renders all black (just set the background color), making the regular viewport narrower, and then turning off the regular viewport’s clear flags.

…and you’d have to similarly limit the size of the gui viewport.

This put a big question mark on my head (if there are no benefits, why bother when my approach worked as well?), so I reread the thread, and realized that something went lost with the conversation… in short:

me: “I have a popup issue”
you: “bad way to letterbox, however with z=50 the issue should be fixed”
me: “this works, I want to improve letterbox”

in the end, the new viewport is an unnecessary complication. However thanks for all the help! :slight_smile:

The new viewport allows you to render black bars without affecting the guiNode at all. Thus you will never hit a “stack gets too high” issue caused by whatever level you chose to do the bars.

I’m pretty convinced if you made it an all black preview port then the camera viewport settings would work since then you’d have something clearing those bars on the screen. (Edit: and that really would be the faster way since then you aren’t spending CPU+GPU rendering things that will just be painted over in black.)

The bug mentioned on the first post was the “second” bug.
I’ve finally tracked down the “original” bug, and is again related to Z value. This is the offending code:

        version = new BitmapText(guiFont, false);
        version.setBox(new Rectangle(0, 0, Display.getWidth(), 215));
        version.setAlignment(BitmapFont.Align.Right);
        version.setLocalTranslation(0, version.getLineHeight(), 1000);
        version.setText(DISPLAY_VERSION + "/" + BUILD_REVISION + "@" + BUILD_DATE);
        guiNode.attachChild(version);

What happens if I add a lemur popup:

The popup never receive any mouse events
The area outside of the popup still gets mouse events

…basically a “reverse modal popup”: everything receive events except the popup :slight_smile: