JOGL Support (JOGL2 that is)

I am running the demo separately. And I don’t see any event being fired… weird.

I’ll check again but it has been tested under Debian, Ubuntu and Mageia Linux 5. Oops, I press ESC to quit…

Hi

@Momoko_Fan I don’t even see the mouse pointer, I don’t see how I could click on the close button.

I’ll update the JOGL renderer to use JOGL 2.3.2 which is stable. I have no idea of how to implement the support of multiple monitor devices in JMonkeyEngine, is there an abstraction for the displays, the screens, the monitor devices and the monitor modes in JMonkeyEngine? As this engine is more game oriented, does the support of multiple monitor devices make sense?

Unfortunately no such abstraction exists … I guess you can say it is out of scope since jME3 is after all a game engine and so multiple windows / screens is not a common scenario.

I like to be able to choose on which monitor I run the game. Also in game’settings, I like to propose the adapted list of resolution supported by the “active” monitor.

Nay,

in fact many RTS games do support multi screen systems.
Also I would like to specify which monitor to play on.

Having such a system would be really nice, even if the current lwjgl backend would just ignore it.
(lwjgl3 has better support for this as well I think)

I use 3 monitors and the NVIDIA surround display function which makes one single virtual monitor out of this. It is quite fun to have those 6000 x 1000 pixels with jME :chimpanzee_smile:

Selection of single monitors sounds great. In usual AWT code which is built-in in Java you can iterate over the screens and take desktop screenshots on any of the 3 displays. But this is somehow not possible with hardware accelerated render contexts (black screenshot). :chimpanzee_sad:

Actually, AWT is quite buggy on some platforms, the fullscreen mode is half broken under GNU Linux with some window managers, I failed to fix this bug in OpenJDK, sorry :frowning:

Actually, GLFW allows to get some information on the monitor devices, you can know which one is the primary monitor but I have found nothing in its multi-monitor guide to use a virtual screen composed of several physical monitor devices whereas I can confirm that it’s supported by NEWT (JOGL cross-platform desktop/embedded windowing toolkit).

Some developers expressed a wish of using multiple windows for sure.

I’m just happy to see that it works like a charm, you can even use a rotated monitor mode. Sven did a great job with NEWT :smile: and it is becoming better in each new release.

Hi

I have just updated the Gradle build in order to use JOGL 2.3.2.

Now it’s possible to choose between the forward compatible profile and the backward compatible profile, see JOGL_OPENGL_FORWARD_COMPATIBLE and JOGL_OPENGL_BACKWARD_COMPATIBLE in AppSettings.

3 Likes

Hi

The offscreen buffer support seems to be broken, I’m going to fix it very soon. Best regards.

Hi

As you know, I’m responsible for engine support in the JogAmp community. As I have less spare time to spend in open source projects and as I think that JMonkeyEngine 3 deserves a dedicated maintainer actively using it in her/his own project(s), I’m looking for a developer to develop and maintain the JogAmp backend more regularly than me.

In the meantime, I’ll fix the offscreen support, it won’t take a lot of time, I know what I have to do but I won’t start implementing advanced features. I won’t abandon JMonkeyEngine users, I can’t find enough time to do what has to be done for this excellent engine but I’ll always be here if something goes really wrong. Moreover, there is already a dedicated maintainer for LibGDX, I have realized that it’s more efficient than having a single developer maintaining at least 3 JogAmp backends at the same time. Finally, as time goes by, I need much time to have a rest and to have a social life. I’m sure that I won’t be available as much as now when I have to take care of my children (it’s just an example), relying on several people is better than relying on a single one.

Best regards.

I’m running the jme3test.awt.TestCanvas example with the JOGL renderer, and the app crashes with the following output when one of the following “Canvas Torture Methods” are used:

  • “Remove Canvas”
  • “Switch to tab #2
  • “Stop/Start Canvas”

It appears the JOGL canvas does not like being re-parented.

finishLifecycleAction(com.jogamp.opengl.util.Animator$3): ++++++ timeout reached ++++++ main-Display-.windows_nil-1-EDT-1
finishLifecycleAction(com.jogamp.opengl.util.Animator$3): OK false- pollPeriod 1000, blocking true -> res false, waited 1000/1000 - main-Display-.windows_nil-1-EDT-1
 - com.jogamp.opengl.util.Animator[started true, animating true, paused true, drawable 1, totals[dt 0, frames 0, fps 0.0], modeBits 1, init'ed true, animThread Thread[AWT-EventQueue-0-AWTAnimator#00,6,main], exclCtxThread false(null)]
    [2]: com.jogamp.opengl.util.AnimatorBase.finishLifecycleAction(AnimatorBase.java:633)
    [3]: com.jogamp.opengl.util.Animator.pause(Animator.java:332)
    [4]: com.jogamp.newt.opengl.GLWindow$GLLifecycleHook.pauseRenderingAction(GLWindow.java:696)
    [5]: jogamp.newt.WindowImpl$ReparentAction.run(WindowImpl.java:1585)
    [6]: com.jogamp.common.util.RunnableTask.run(RunnableTask.java:145)
    [7]: jogamp.newt.DefaultEDTUtil$NEDT.run(DefaultEDTUtil.java:375)
Exception in thread "AWT-EventQueue-0" java.lang.RuntimeException: java.lang.RuntimeException: Waited 5000ms for: <5a1698f1, 193246c>[count 2, qsz 0, owner <AWT-EventQueue-0-AWTAnimator#00>] - <main-Display-.windows_nil-1-EDT-1>
    at jogamp.newt.DefaultEDTUtil.invokeImpl(DefaultEDTUtil.java:252)
    at jogamp.newt.DefaultEDTUtil.invoke(DefaultEDTUtil.java:165)
    at jogamp.newt.DisplayImpl.runOnEDTIfAvail(DisplayImpl.java:442)
    at jogamp.newt.WindowImpl.runOnEDTIfAvail(WindowImpl.java:2782)
    at jogamp.newt.WindowImpl.reparentWindow(WindowImpl.java:1920)
    at com.jogamp.newt.opengl.GLWindow.reparentWindow(GLWindow.java:545)
    at com.jogamp.newt.awt.NewtCanvasAWT.detachNewtChild(NewtCanvasAWT.java:1101)
    at com.jogamp.newt.awt.NewtCanvasAWT.destroyImpl(NewtCanvasAWT.java:701)
    at com.jogamp.newt.awt.NewtCanvasAWT.removeNotify(NewtCanvasAWT.java:660)
    at com.jme3.system.jogl.JoglNewtCanvas$1.removeNotify(JoglNewtCanvas.java:68)
    at java.awt.Container.remove(Container.java:1199)
    at java.awt.Container.remove(Container.java:1257)
    at jme3test.awt.TestCanvas$3.actionPerformed(TestCanvas.java:123)
    at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2018)
    at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2341)
    at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:402)
    at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:259)
    at javax.swing.AbstractButton.doClick(AbstractButton.java:376)
    at javax.swing.plaf.basic.BasicMenuItemUI.doClick(BasicMenuItemUI.java:833)
    at javax.swing.plaf.basic.BasicMenuItemUI$Handler.mouseReleased(BasicMenuItemUI.java:877)
    at java.awt.Component.processMouseEvent(Component.java:6516)
    at javax.swing.JComponent.processMouseEvent(JComponent.java:3320)
    at java.awt.Component.processEvent(Component.java:6281)
    at java.awt.Container.processEvent(Container.java:2229)
    at java.awt.Component.dispatchEventImpl(Component.java:4872)
    at java.awt.Container.dispatchEventImpl(Container.java:2287)
    at java.awt.Component.dispatchEvent(Component.java:4698)
    at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4832)
    at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4492)
    at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4422)
    at java.awt.Container.dispatchEventImpl(Container.java:2273)
    at java.awt.Window.dispatchEventImpl(Window.java:2719)
    at java.awt.Component.dispatchEvent(Component.java:4698)
    at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:735)
    at java.awt.EventQueue.access$200(EventQueue.java:103)
    at java.awt.EventQueue$3.run(EventQueue.java:694)
    at java.awt.EventQueue$3.run(EventQueue.java:692)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:87)
    at java.awt.EventQueue$4.run(EventQueue.java:708)
    at java.awt.EventQueue$4.run(EventQueue.java:706)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:705)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:242)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:161)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:150)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:146)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:138)
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:91)
Caused by: java.lang.RuntimeException: Waited 5000ms for: <5a1698f1, 193246c>[count 2, qsz 0, owner <AWT-EventQueue-0-AWTAnimator#00>] - <main-Display-.windows_nil-1-EDT-1>
    at jogamp.common.util.locks.RecursiveLockImpl01Unfairish.lock(RecursiveLockImpl01Unfairish.java:198)
    at jogamp.newt.WindowImpl$ReparentAction.reparent(WindowImpl.java:1608)
    at jogamp.newt.WindowImpl$ReparentAction.run(WindowImpl.java:1587)
    at com.jogamp.common.util.RunnableTask.run(RunnableTask.java:145)
    at jogamp.newt.DefaultEDTUtil$NEDT.run(DefaultEDTUtil.java:375)

In case it’s helpful, here’s the modified TestCanvas class (added two lines of code to enable JOGL):

package jme3test.awt;

import com.jme3.app.LegacyApplication;
import com.jme3.app.SimpleApplication;
import com.jme3.system.AppSettings;
import com.jme3.system.JmeCanvasContext;
import com.jme3.util.JmeFormatter;
import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.concurrent.Callable;
import java.util.logging.ConsoleHandler;
import java.util.logging.Handler;
import java.util.logging.Logger;
import javax.swing.*;

public class TestCanvas {

    private static JmeCanvasContext context;
    private static Canvas canvas;
    private static LegacyApplication app;
    private static JFrame frame;
    private static Container canvasPanel1, canvasPanel2;
    private static Container currentPanel;
    private static JTabbedPane tabbedPane;
    private static final String appClass = "jme3test.post.TestRenderToTexture";

    private static void createTabs(){
        tabbedPane = new JTabbedPane();

        canvasPanel1 = new JPanel();
        canvasPanel1.setLayout(new BorderLayout());
        tabbedPane.addTab("jME3 Canvas 1", canvasPanel1);

        canvasPanel2 = new JPanel();
        canvasPanel2.setLayout(new BorderLayout());
        tabbedPane.addTab("jME3 Canvas 2", canvasPanel2);

        frame.getContentPane().add(tabbedPane);

        currentPanel = canvasPanel1;
    }

    private static void createMenu(){
        JMenuBar menuBar = new JMenuBar();
        frame.setJMenuBar(menuBar);

        JMenu menuTortureMethods = new JMenu("Canvas Torture Methods");
        menuBar.add(menuTortureMethods);

        final JMenuItem itemRemoveCanvas = new JMenuItem("Remove Canvas");
        menuTortureMethods.add(itemRemoveCanvas);
        itemRemoveCanvas.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if (itemRemoveCanvas.getText().equals("Remove Canvas")){
                    currentPanel.remove(canvas);

                    itemRemoveCanvas.setText("Add Canvas");
                }else if (itemRemoveCanvas.getText().equals("Add Canvas")){
                    currentPanel.add(canvas, BorderLayout.CENTER);

                    itemRemoveCanvas.setText("Remove Canvas");
                }
            }
        });

        final JMenuItem itemHideCanvas = new JMenuItem("Hide Canvas");
        menuTortureMethods.add(itemHideCanvas);
        itemHideCanvas.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if (itemHideCanvas.getText().equals("Hide Canvas")){
                    canvas.setVisible(false);
                    itemHideCanvas.setText("Show Canvas");
                }else if (itemHideCanvas.getText().equals("Show Canvas")){
                    canvas.setVisible(true);
                    itemHideCanvas.setText("Hide Canvas");
                }
            }
        });

        final JMenuItem itemSwitchTab = new JMenuItem("Switch to tab #2");
        menuTortureMethods.add(itemSwitchTab);
        itemSwitchTab.addActionListener(new ActionListener(){
           public void actionPerformed(ActionEvent e){
               if (itemSwitchTab.getText().equals("Switch to tab #2")){
                   canvasPanel1.remove(canvas);
                   canvasPanel2.add(canvas, BorderLayout.CENTER);
                   currentPanel = canvasPanel2;
                   itemSwitchTab.setText("Switch to tab #1");
               }else if (itemSwitchTab.getText().equals("Switch to tab #1")){
                   canvasPanel2.remove(canvas);
                   canvasPanel1.add(canvas, BorderLayout.CENTER);
                   currentPanel = canvasPanel1;
                   itemSwitchTab.setText("Switch to tab #2");
               }
           }
        });

        JMenuItem itemSwitchLaf = new JMenuItem("Switch Look and Feel");
        menuTortureMethods.add(itemSwitchLaf);
        itemSwitchLaf.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e){
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (Throwable t){
                    t.printStackTrace();
                }
                SwingUtilities.updateComponentTreeUI(frame);
                frame.pack();
            }
        });

        JMenuItem itemSmallSize = new JMenuItem("Set size to (0, 0)");
        menuTortureMethods.add(itemSmallSize);
        itemSmallSize.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e){
                Dimension preferred = frame.getPreferredSize();
                frame.setPreferredSize(new Dimension(0, 0));
                frame.pack();
                frame.setPreferredSize(preferred);
            }
        });

        JMenuItem itemKillCanvas = new JMenuItem("Stop/Start Canvas");
        menuTortureMethods.add(itemKillCanvas);
        itemKillCanvas.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                currentPanel.remove(canvas);
                app.stop(true);

                createCanvas(appClass);
                currentPanel.add(canvas, BorderLayout.CENTER);
                frame.pack();
                startApp();
            }
        });

        JMenuItem itemExit = new JMenuItem("Exit");
        menuTortureMethods.add(itemExit);
        itemExit.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                frame.dispose();
                app.stop();
            }
        });
    }

    private static void createFrame(){
        frame = new JFrame("Test");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.addWindowListener(new WindowAdapter(){
            @Override
            public void windowClosed(WindowEvent e) {
                app.stop();
            }
        });

        createTabs();
        createMenu();
    }

    public static void createCanvas(String appClass){
        AppSettings settings = new AppSettings(true);
        settings.setWidth(640);
        settings.setHeight(480);
        
        // Added for JOGL
        settings.setAudioRenderer(null);
        settings.setRenderer("JOGL");

        try{
            Class<? extends LegacyApplication> clazz = (Class<? extends LegacyApplication>) Class.forName(appClass);
            app = clazz.newInstance();
        }catch (ClassNotFoundException ex){
            ex.printStackTrace();
        }catch (InstantiationException ex){
            ex.printStackTrace();
        }catch (IllegalAccessException ex){
            ex.printStackTrace();
        }

        app.setPauseOnLostFocus(false);
        app.setSettings(settings);
        app.createCanvas();
        app.startCanvas();

        context = (JmeCanvasContext) app.getContext();
        canvas = context.getCanvas();
        canvas.setSize(settings.getWidth(), settings.getHeight());
    }

    public static void startApp(){
        app.startCanvas();
        app.enqueue(new Callable<Void>(){
            public Void call(){
                if (app instanceof SimpleApplication){
                    SimpleApplication simpleApp = (SimpleApplication) app;
                    simpleApp.getFlyByCamera().setDragToRotate(true);
                }
                return null;
            }
        });

    }

    public static void main(String[] args){
        JmeFormatter formatter = new JmeFormatter();

        Handler consoleHandler = new ConsoleHandler();
        consoleHandler.setFormatter(formatter);

        Logger.getLogger("").removeHandler(Logger.getLogger("").getHandlers()[0]);
        Logger.getLogger("").addHandler(consoleHandler);

        createCanvas(appClass);

        try {
            Thread.sleep(500);
        } catch (InterruptedException ex) {
        }

        SwingUtilities.invokeLater(new Runnable(){
            public void run(){
                JPopupMenu.setDefaultLightWeightPopupEnabled(false);

                createFrame();

                currentPanel.add(canvas, BorderLayout.CENTER);
                frame.pack();
                startApp();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

}

Found another bug, this time with the NewtMouseInput. There is some code in that class that forces the mouse cursor to the center of the screen when the cursor’s visibility is changed. This causes odd behavior with the FlyByCamera as can be observed with the following test class:

public class HelloJME3 extends SimpleApplication {

    public static void main(String[] args){
        AppSettings appSettings = new AppSettings(true);
        appSettings.setWidth(800);
        appSettings.setHeight(600);
        appSettings.setAudioRenderer(null);
        appSettings.setRenderer("JOGL");

        final HelloJME3 app = new HelloJME3();
        app.setSettings(appSettings);
        app.setShowSettings(false);
        app.start();
    }

    @Override
    public void simpleInitApp() {
        flyCam.setDragToRotate(true);

        Box b = new Box(1, 1, 1); // create cube shape
        Geometry geom = new Geometry("Box", b);  // create cube geometry from the shape
        Material mat = new Material(assetManager,
          "Common/MatDefs/Misc/Unshaded.j3md");  // create a simple material
        mat.setColor("Color", ColorRGBA.Blue);   // set color of material to blue
        geom.setMaterial(mat);                   // set the cube's material
        rootNode.attachChild(geom);              // make the cube appear in the scene
    }
}

In the test, the camera will rotate immediately to the bottom right corner of the screen when the left mouse button is pressed. The only way to avoid this initial rotation is to keep the cursor at the center of the screen before pressing the left mouse button.

It would be fine to make a small test without JMonkeyEngine 3 in pure JOGL with NewtCanvasAWT to see whether it is expected to work.

Maybe it’s a regression, I suspect this commit:
https://github.com/jMonkeyEngine/jmonkeyengine/commit/836bf856303dff607fd5700961c5b2766780842f#diff-1f750733002669c0916734c6e4b1ecb7

Reminder: I still look for someone else to maintain the JogAmp backend of JMonkeyEngine 3 but I’ll see what I can do for those bugs.

@gouessej I’ve got a fix for the mouse cursor auto-centering in this pull request.

@gouessej I mistakenly made the the last pull request for the master branch (but it should probably also have the fix). I’ve made another pull request for the v3.1 branch since this is the one I’m currently using.

Hey

I’m going to leave Github as I prefer hosting my source code on my own server rather using the (possibly unsustainable) free of charge services of a for profit corporation, I don’t want to rely on a proprietary software (whereas Sourceforge relies on Apache Allura and Gitlab is open source too) and I disapprove its recent acquisition by Microsoft. I was a bit reluctant to use it, I did it mainly on projects involving other developers who wanted to stick to it or on forks of projects already hosted on this code sharing platform.

As a consequence, don’t expect me to reply on Github. Note that any help to maintain the JOGL backend of JMonkeyEngine is warmly welcome. I’m rarely here, don’t hesitate to contact me on the official JogAmp forum.

3 Likes