Second guinode and mouse listener

A control that can be added to any Spatial to provide standard MouseListener/MouseEvent support. The only requirement is that the Spatial must be somewhere in a hierarchy that has been provided to the MouseAppState or GuiGlobals class and that MouseAppState is active (either manually attached to the StateManager or done automatically by GuiGlobals.initialize())

When managing my own gui node, ie, its not attached to root node and has its own viewport, how do I get the MouseEventControl to recieve mouse events?

self managed = not attached to root node.

To clarify, I have a self managed second viewport.

I use the camera from that viewport to create another viewport for a self managed gui node.

Calling

    public void addCollisionRoot( ViewPort viewPort ) {
        session.addCollisionRoot(viewPort);
    }

from BasePickState must do something other than what it sounds like?

/**
 *  Consolidates the PickEventSession management for doing
 *  scene picking.  This is the base class for the MouseAppState
 *  and the TouchAppState.
 *
 *  @author    Paul Speed
 */
public abstract class BasePickState extends BaseAppState

Add that guiNode and/or its viewport to the mouse state.

Something like:
stateManager.getState(PickState.class).addCollisionRoot(yourViewport);
Or:
stateManager.getState(PickState.class).addCollisionRoot(yourGuiNode, yourViewport);
Or:
stateManager.getState(PickState.class).addCollisionRoot(yourGuiNode, yourViewport, PickState.PICK_LAYER_GUI);
…if you want to make sure it gets events before the scene does.

There should also be a method for getting the current pick state on GuiGlobals but there isn’t… but anyway the state manager will work even for folks who aren’t initializing GuiGlobals.

If you’ve tried that and it didn’t work then I’d need to see code to comment more.

I am getting some click() fire but only after I spam the mouse and then only when clicking on the main viewport not, the second viewport, so I am doing something wrong setting things up somehow.

I will get back tomorrow so I don’t take up the rest of your weekend with this.

Ok, here’s how this is setup, hack of jme3,

I have a state that’s does nothing but initialize nodes for the game,

        //setup minimap
        miniMapNode                 = new Node("miniMapNode");
        miniMapGuiNode              = new Node("miniMapGuiNode");
        miniMapWaterNode            = new Node("miniMapWaterNode"); 
        
        miniMapNode.attachChild(miniMapWaterNode);
        miniMapGuiNode.setQueueBucket(RenderQueue.Bucket.Gui);
        miniMapGuiNode.setCullHint(Spatial.CullHint.Never);

The update method for the state,

    @Override
    public void update(float tpf) {
        miniMapNode.updateLogicalState(tpf);
        miniMapGuiNode.updateLogicalState(tpf);
    }

The render method,

    @Override
    public void render( RenderManager rm ) {
        miniMapNode.updateGeometricState();
        miniMapGuiNode.updateGeometricState();
    }

The state that creates characters adds a minimap,

    /**
     * Add a MiniMapCamera to a node. 
     * @param player The node to add the camera to. Also the node the camera 
     * will lookAt.
     * @param camera The cammera to clone for setting the MiniMap ViewPort scene.
     * @param chaseCam If non-null, new buttons with a mouseListener are added 
     * to the miniMapGuiNode. 
     */
    public void addMiniMap(Node player, Camera camera, ChaseCamera chaseCam ) {
        Camera cam = camera.clone();
        cam.setViewPort(.725f, .975f, 0.7f, 0.95f);
        
        ViewPort miniMapViewPort = getApplication().getRenderManager().createMainView("MiniMapView", cam);
        miniMapViewPort.setClearFlags(true, true, true);
        //Attach scenes
        miniMapViewPort.attachScene(getState(InitSceneGraphTreeState.class).getMiniMapNode());
        miniMapViewPort.attachScene(getState(InitSceneGraphTreeState.class).getTerrainNode());
        miniMapViewPort.setBackgroundColor(ColorRGBA.Black);
        
        //Create the FilterPostProcessor for the ViewPort
        FilterPostProcessor miniMapProcessor = new FilterPostProcessor(getApplication().getAssetManager());
        miniMapViewPort.addProcessor(miniMapProcessor);
        //Add filters
        createFilters(miniMapProcessor);
        createShallowWaterB(miniMapViewPort);
        createShallowWaterC(miniMapViewPort);

        //Add MiniMapCamera to player node. 
        MiniMapCamera miniMapCam = new MiniMapCamera(cam, player, getApplication().getInputManager());
        //Never hide cursor if used for picking.
        miniMapCam.setHideCursorOnRotate(false);
        //Set the camera to look down from above
        miniMapCam.setDefaultVerticalRotation(1.57f);
        //Set camera to face player on start.
        miniMapCam.setDefaultHorizontalRotation(1.57f);
        miniMapCam.setRotationSpeed(4f);
        miniMapCam.setDefaultDistance(40);
        
        // Create a new cam for the guiNode
        Camera miniMapGuiCam = cam.clone();
        ViewPort miniMapGuiViewPort = getApplication().getRenderManager().createPostView("MiniMapGuiViewPort", miniMapGuiCam);
        //Attach scenes
        miniMapGuiViewPort.attachScene(getState(InitSceneGraphTreeState.class).getMiniMapGuiNode());
        miniMapGuiViewPort.setClearFlags(false, false, false);
        
        //Add picking to the guiNode
        getState(PickState.class).addCollisionRoot(getState(InitSceneGraphTreeState.class).getMiniMapGuiNode(), miniMapGuiViewPort, PickState.PICK_LAYER_GUI);

        //If a ChaseCamera is supplied, add buttons to the GUI node and add a 
        //MouseListener for the click() method. 
        if (chaseCam != null) {
            Node compassNode = addButton("Compass", "Interface/compass64.png", 128, 128, 0.15f, 0.85f, true);
            getState(InitSceneGraphTreeState.class).getMiniMapGuiNode().attachChild(compassNode);
            
            //Set the node to rotate when the MiniMapCamera rotates.
            miniMapCam.setCompassButton(compassNode);
        
            //Add the MouseListener that will control what happens when user
            //clicks the compass button. Controls both the ChaseCamera and 
            //MiniMapCamera rotation by CameraInput.CHASECAM_TOGGLEROTATE, 
            //CameraInput.CHASECAM_MOVELEFT, or CameraInput.CHASECAM_MOVERIGHT 
            //mappings. The MouseListener click() method will reset both the 
            //ChaseCamera and MiniMapCamera to face north on the 
            //MouseInput.BUTTON_LEFT click or south on 
            //MouseInput.BUTTON_RIGHT click.
            MouseEventControl.addListenersToSpatial(compassNode, new DefaultMouseListener() {
                @Override
                protected void click( MouseButtonEvent event, Spatial target, Spatial capture ) {
                System.out.println("I've been clicked:" + target + " " + event);
                    if (event.getButtonIndex() == MouseInput.BUTTON_LEFT) {
                        //Point the miniMap and chaseCam in the North direction
                        miniMapCam.setDefaultHorizontalRotation(1.57f);
                        chaseCam.setDefaultHorizontalRotation(1.57f);
                    }
    
                    if (event.getButtonIndex() == MouseInput.BUTTON_RIGHT) {
                        //Point the miniMap and chaseCam in the South direction
                        miniMapCam.setDefaultHorizontalRotation(4.71f);
                        chaseCam.setDefaultHorizontalRotation(4.71f);
                    }
                }
            });
            
            //Add a button that the user can toggle run or walk off and on.
            Node runNode = addButton("Run", "Interface/run64.png", 128, 128, 0.15f, 0.15f, true);
            getState(InitSceneGraphTreeState.class).getMiniMapGuiNode().attachChild(runNode);

            MouseEventControl.addListenersToSpatial(runNode, new DefaultMouseListener() {
                @Override
                protected void click( MouseButtonEvent event, Spatial target, Spatial capture ) {
                    System.out.println("I've been clicked:" + target + " " + event);
                    UserDataControl udControl = player.getControl(UserDataControl.class);
                    StraightPathAgentControl spaControl = player.getControl(StraightPathAgentControl.class);

                    int walk = EnumPosType.POS_WALKING.pos();
                    int run = EnumPosType.POS_RUNNING.pos();

                    boolean isAutoRun = !udControl.getAutorun();
                    udControl.setAutorun(isAutoRun);

                    if (isAutoRun && udControl.getPositionType() == walk) {
                        udControl.setPositionType(run);
                        spaControl.setTarget(spaControl.getTarget());
                    } 
                    else if (!isAutoRun && udControl.getPositionType() == run) {
                        udControl.setPositionType(walk);
                        spaControl.setTarget(spaControl.getTarget());
                    }
                }
            });
        }
    }

The relevant lines for finishing setting up the self managed nodes,

        //Attach scenes
        miniMapViewPort.attachScene(getState(InitSceneGraphTreeState.class).getMiniMapNode()); //self managed
        miniMapViewPort.attachScene(getState(InitSceneGraphTreeState.class).getTerrainNode());
        
        // Create a new cam for the guiNode
        Camera miniMapGuiCam = cam.clone();
        ViewPort miniMapGuiViewPort = getApplication().getRenderManager().createPostView("MiniMapGuiViewPort", miniMapGuiCam);
        //Attach scenes
        miniMapGuiViewPort.attachScene(getState(InitSceneGraphTreeState.class).getMiniMapGuiNode()); //self managed
        miniMapGuiViewPort.setClearFlags(false, false, false);
        
        //Add picking to the guiNode
        getState(PickState.class).addCollisionRoot(getState(InitSceneGraphTreeState.class).getMiniMapGuiNode(), miniMapGuiViewPort, PickState.PICK_LAYER_GUI);

I have tried just using MouseAppState by itself.
I have tried just using GUIState by itself which inits GuiGlobals.
I have tried with both initialized.
I have tried all of the above with the methods for creating the miniMapCam inside GuiState and other states.
I have used all getState(PickState.class).addCollisionRoot(yourViewport); methods using PickState.class and MouseAppState.class.

  • The Listener on the runNode will fire when spamming the mouse inside the main viewport but nothing fires from the miniMapViewPort. It shouldn’t even fire at all from the main viewport.

  • Picking works fine in the miniMapViewPort.

  • The listeners work fine from normal guiNode or when creating a new Camera for the miniMapGuiNode.

I must of missed something somewhere.

This part is suspicious.

I wonder if it’s related to where the viewport is on the screen and maybe there is some bug with how the mouse coordinates are translated into a pick vector?

You might be able to see if it feels like it’s just clicking in the wrong part of the screen or whatever. Else adding cursor event listeners and dumping the collision information might be informative (They work just like mouse listeners except you get extra collision information, including the viewport, etc.)

Well this is weird.

Spamming the main viewport results in this output. If the click was registered in the minimap it would read You shot minimap Terrain_CQuad1Quad4Patch4 .

You shot Terrain_CQuad1Quad4Patch4 at (-39.39164, 13.338332, -7.338332), 11.928566 wu away.
I've been clicked:RunNode (Node) MiniMapGuiViewPort (94.0, 114.0)
  • cam.setViewPort(.725f, .975f, 0.7f, 0.95f);

Which is .25x.25 the size of main viewport. At 640x480 main size that is a 160x120 sized mini viewport.

  • Node runNode = addButton(“Run”, “Interface/run64.png”, 128, 128, 0.15f, 0.15f, true);

The button is at (24.0, 18.0) in the MinimapViewport .

Edit: That’s the lower left corner.

=====================

At 1280x960 main viewport thats a mini of 320x240 size so button is at (48.0, 36.0).

You shot Terrain_CQuad2Quad3Patch3 at (-45.066242, 13.0, 21.612719), 8.574048 wu away.
I've been clicked:RunNode (Node) MiniMapGuiViewPort (239.0, 80.0)

Spamming inside the minimap never fires the click of the listener and I spam it at all positions imaginable, including the buttons.

If you set that to full screen do clicks start working properly?

I’m just trying to narrow down where the issue is. I’m not actually suggesting that as a solution.

Yes, they worked.

Trying to setup a test case now.

Ok, one thing I am finding is that when I check each camera they all have the same width and height.

Shouldn’t setting the viewport for a camera reset its width and height also?

This does seem to be the problem. The width and height of the minimap cam are not reported correctly.

When I create a new Camera using the minimap cams width and height it works and reports this,

I've been clicked:RunNode (Node) MiniMapGuiViewPort (144.0, 90.0)

because the minimap viewport width and height never get changed.

Edit: But the buttons are in the lower left of the main view since the minMapCam is the same width and height as the main cam.

Using miniMapCam clone.

using new cam with miniMapCam width and height.

Click the images to expand and see the println();

Edit, both buttons do work as long as I click where they show on the larger imaged buttons.

My recollection is that camera widht/height is related to the frame buffer or whatever… the “device” if you will. ViewPort is then what part of that “device” you want to render.

I think there is a bug in Lemur. I’ve been able to get viewports to work before but I don’t remember if there was anything strange I had to do in the game code. I should create a test case (unless someone beats me to it).

Ultimately, I will probably add a demo state to the Lemur demos as this seems to be a desirable feature… and many just don’t know how to set it up.

Found another report of this searching for other things.

I have one started but it takes a lot of code to reproduce this. I will pm it to you when finished.

Weird, I’d think it could be done with one app state.