[SOLVED]Multiple ViewPorts focus and picking

I am setting up a separate HUD compass class for navigation that uses a different viewport with top down view, ie camera is above character.

I have read everything I can on giving mouse focus to another ViewPort and found the info on the subject to be lacking but have come up with something that I feel is ugly as hell but works. I tend to reinvent the wheel and create excessive code when I don’t have the information I seek.

Steps I have taken.

  1. Two ViewPorts, Default and Compass.
  2. Picking ActionListener on the character class.
  3. Picking and Rotations ActionListener in the Compass camera class.
  4. Created a hasFocus() method to determine if the viewport has focus.
  5. If hasFocus() on the Default ViewPort is true, it uses the character ActionListener to set the target for navigation. If the Compass ViewPort has the focus, it uses its ActionListener to set the target for navigation.

The hasFocus() method.

    /**
     * Checks if the cursor position is within the given ViewPort.
     * @param vp ViewPort to check cursor position against.
     * @return true if the cursor is within the supplied ViewPort.
     */
    private boolean viewPortHasFocus(ViewPort vp) {
        
        //No ViewPort to check.
        if (vp == null) {
            LOG.log(Level.SEVERE, "viewPortHasFocus: Null ViewPort.");
            return false;
        }
        
        float x1 = vp.getCamera().getViewPortLeft();
        float x2 = vp.getCamera().getViewPortRight();
        float y1 = vp.getCamera().getViewPortBottom();
        float y2 = vp.getCamera().getViewPortTop();
        int width = vp.getCamera().getWidth();
        int height = vp.getCamera().getHeight();
        float x = inputManager.getCursorPosition().getX();
        float y = inputManager.getCursorPosition().getY();
        
        return x >= (x1*width) && x <= (x2*width) && y >= (y1*height) && y <= (y2*height);
    }

Is using the Compass ViewPort and character ActionListener to pass picking targets for navigation a flawed approach?

Is using this hasFocus() method every mouse click the correct way to do this?

Are there better ways to do this using existing jme methods?

Edit: Refactored hasFocus code.

Is this using Lemur’s picking or rolling your own?

For Lemur, it’s supposed to be as easy as registering your viewport to whatever pick app state is being used:
http://jmonkeyengine-contributions.github.io/Lemur/javadoc/Lemur/com/simsilica/lemur/event/BasePickState.html#addCollisionRoot(com.jme3.renderer.ViewPort)

If it’s straight JME then I guess your approach is ok. JME doesn’t provide any built in GUI support like this so if you don’t use an existing library then you will have to roll your own stuff.

No, its mine.

I wanted to use Lemur for things that require scene interaction like picking and Nifty for GUI overlays for things like chat and inventory.

The problem I am having with Lemur picking is that it captures the mouse event before nifty, which then requires a check for isEnabled on an AppState to circumvent.

The other problem I ran into was with scrolling and text blowing out the constraints of the tab panel.

Just not skilled enough with Lemur yet.

This may have more to do with initialization order… but I don’t remember how nifty registers for events so I can’t be sure. There should be a way to initialize Nifty first, though.

I was only using this method inside an AppState that is initialized after Nifty.

MouseEventControl.addListenersToSpatial(charNode, new DefaultMouseListener()

It is added to charNode in state 32 of 35 and the Nifty State is 26 of 35 with the last state being the KeyboardRunState(Input mappings). Nifty is initialized from the initialize method.

I moved Nifty to 1 of 35 and it still does it.

I separated Nifty from my InitStates state, ie from SimpleApplication I init the Nifty state first, then Init the InitStates state and still happens.

I set visibleToMouse=“true” on a layer or panels in Nifty XML and jme itself works fine. Nifty captures the mouse event, exception being if that node with the listener is clicked. Nothing stops it.

As always though, its probably something I am doing wrong without realizing it.

The reason MouseEventControl works at all is because the MouseAppState was attached at some point and added its stuff.

…but now that I think about it, it’s firing picking events on update and not as part of the normal JME event chain. So it could be that it just doesn’t want to play nice with nifty.

If you put quads underneath your nifty sections and added a mouse listener to them (especially the consuming one) then the events won’t make it through to MouseEventControl in your scene. I don’t know if that’s easy/hard for your particular GUI, though.

I think I figured it out. I was using the method by itself.

If I use your example AppState that initializes

        // Create a simple container for our elements
        // Initialize the globals access so that the defualt
        // components can find what they need.
        GuiGlobals.initialize(app);
            
        // Load the 'glass' style
        BaseStyles.loadGlassStyle();
            
        // Set 'glass' as the default style when not specified
        GuiGlobals.getInstance().getStyles().setDefaultStyle("glass");

Everything works perfectly with nifty.

Ive bastardized so many libraries it ridiculous.

1 Like

Actually, this is what got by me, it wasn’t being added at all by me but it was in your code,

   /**
     *  Convenience method that will add a MouseEventControl if it
     *  doesn't exist, while adding the specified listeners.
     */
    public static void addListenersToSpatial( Spatial s, MouseListener... listeners ) {
        if( s == null ) {
            return;
        }
        MouseEventControl mec = s.getControl(MouseEventControl.class);
        if( mec == null ) {
            s.addControl(new MouseEventControl(listeners));
        } else {
            mec.listeners.addAll(Arrays.asList(listeners));
        }
    }

Thanks for the help paul.