Fix for almost all known remaining bugs of the JOGL renderer and the AWT input

Hi!



The patch below fixes almost all known remaining bugs of the JOGL renderer and the AWT input system except those concerning the shaders (already fixed by Woody). I thank Mathias "cylab" Henze for his huge contribution. There is stil a problem when the angle is near zero (he is going to fix it as soon as possible). I'm building my game now to allow you to test the result in real conditions. Let me know your opinion about it.



Edit.: the patch is too long, I split it into 3 messages.


Index: /home/julien/workspace/jme/src/com/jme/renderer/jogl/JOGLRenderer.java
===================================================================
--- /home/julien/workspace/jme/src/com/jme/renderer/jogl/JOGLRenderer.java   (revision 4383)
+++ /home/julien/workspace/jme/src/com/jme/renderer/jogl/JOGLRenderer.java   (working copy)
@@ -113,6 +113,7 @@
 import com.jme.util.geom.BufferUtils;
 import com.jme.util.stat.StatCollector;
 import com.jme.util.stat.StatType;
+import javax.swing.SwingUtilities;
 
 /**
  * <code>JOGLRenderer</code> provides an implementation of the
@@ -428,6 +429,7 @@
      *            the color to set the background color to.
      */
     public void setBackgroundColor(ColorRGBA c) {
+        ensureCurrentContext();
         final GL gl = GLU.getCurrentGL();
 
         // if color is null set background to white.
@@ -449,6 +451,7 @@
      * @see com.jme.renderer.Renderer#clearZBuffer()
      */
     public void clearZBuffer() {
+      ensureCurrentContext();
         final GL gl = GLU.getCurrentGL();
 
         if (Renderer.defaultStateList[RenderState.StateType.ZBuffer.ordinal()] != null)
@@ -462,6 +465,7 @@
      * @see com.jme.renderer.Renderer#clearColorBuffer()
      */
     public void clearColorBuffer() {
+      ensureCurrentContext();
         final GL gl = GLU.getCurrentGL();
 
         gl.glClear(GL.GL_COLOR_BUFFER_BIT);
@@ -473,6 +477,7 @@
      * @see com.jme.renderer.Renderer#clearStencilBuffer()
      */
     public void clearStencilBuffer() {
+        ensureCurrentContext();
         final GL gl = GLU.getCurrentGL();
 
         // Clear the stencil buffer
@@ -491,6 +496,7 @@
      * @see com.jme.renderer.Renderer#clearBuffers()
      */
     public void clearBuffers() {
+      ensureCurrentContext();
         final GL gl = GLU.getCurrentGL();
 
         // make sure no funny business is going on in the z before clearing.
@@ -508,6 +514,7 @@
      * @see com.jme.renderer.Renderer#clearBuffers()
      */
     public void clearStrictBuffers() {
+        ensureCurrentContext();
         final GL gl = GLU.getCurrentGL();
 
         gl.glDisable(GL.GL_DITHER);
@@ -525,6 +532,7 @@
      * @see com.jme.renderer.Renderer#displayBackBuffer()
      */
     public void displayBackBuffer() {
+        ensureCurrentContext();
         final GL gl = GLU.getCurrentGL();
 
         renderQueue();
@@ -547,6 +555,13 @@
 
         vboMap.expunge();
 
+      // MHENZE (cylab) - FIX Issue 24: Release the context to give the controlled surface back to AWT, if JOGL is not driven from the EDT!
+        if(!SwingUtilities.isEventDispatchThread())
+        {
+            GLContext context = GLContext.getCurrent();
+            if(context!=null) context.release();
+        }
+
         if (Debug.stats) {
             StatCollector.addStat(StatType.STAT_FRAMES, 1);
         }
@@ -562,6 +577,25 @@
         return inOrthoMode;
     }
 
+   // MHENZE (cylab) - FIX Issue 24: Make sure, the context is current!
+    // This is only a workaround in the hope, that at least one the JOGLRenderer
+    // methods gets called, before other GL operations occur
+   private void ensureCurrentContext()
+   {
+        GLContext context = display.getGLContext();
+        if(context!=null && context!=GLContext.getCurrent())
+        {
+            // Make the GLContext the current.
+            while (context.makeCurrent() == GLContext.CONTEXT_NOT_CURRENT) {
+                try {
+                    Thread.sleep(500);
+                } catch (InterruptedException interruption) {
+                    logger.warning("Interruped while waiting for makeCurrent()");
+                }
+            }
+        }
+   }
+
     /**
      * <code>setOrtho</code> sets the display system to be in orthographic
      * mode. If the system has already been set to orthographic mode a
@@ -569,6 +603,7 @@
      * left of the screen.
      */
     public void setOrtho() {
+        ensureCurrentContext();
         final GL gl = GLU.getCurrentGL();
         final GLU glu = new GLU();
 
@@ -591,6 +626,7 @@
     }
 
     public void setOrthoCenter() {
+        ensureCurrentContext();
         final GL gl = GLU.getCurrentGL();
         final GLU glu = new GLU();
 
@@ -617,6 +653,7 @@
      * center of the screen.
      */
     public void unsetOrtho() {
+        ensureCurrentContext();
         final GL gl = GLU.getCurrentGL();
 
         if (!inOrthoMode) {
@@ -697,6 +734,7 @@
      */
     public void grabScreenContents(ByteBuffer buff, Image.Format format, int x,
             int y, int w, int h) {
+        ensureCurrentContext();
         final GL gl = GLU.getCurrentGL();
 
         int pixFormat = TextureStateRecord.getGLPixelFormat(format);
@@ -710,6 +748,7 @@
      *            the curve object to render.
      */
     public void draw(Curve curve) {
+        ensureCurrentContext();
         final GL gl = GLU.getCurrentGL();
 
         // set world matrix
@@ -777,6 +816,7 @@
      *            the lines to render.
      */
     public void draw(Line lines) {
+        ensureCurrentContext();
         final GL gl = GLU.getCurrentGL();
 
         if (!lines.predraw(this))
@@ -854,6 +894,7 @@
      *            the points to render.
      */
     public void draw(Point points) {
+        ensureCurrentContext();
         final GL gl = GLU.getCurrentGL();
 
         if (!points.predraw(this))
@@ -920,6 +961,7 @@
      *            the mesh to render.
      */
     public void draw(QuadMesh quads) {
+        ensureCurrentContext();
         final GL gl = GLU.getCurrentGL();
 
         if (!quads.predraw(this))
@@ -987,6 +1029,7 @@
      *            the mesh to render.
      */
     public void draw(TriMesh tris) {
+        ensureCurrentContext();
         final GL gl = GLU.getCurrentGL();
 
         if (!tris.predraw(this))
@@ -1053,6 +1096,7 @@
     }
 
     private synchronized void renderDisplayList(Geometry geom) {
+        ensureCurrentContext();
         final GL gl = GLU.getCurrentGL();
 
         applyStates(geom.states, geom);
@@ -1088,6 +1132,7 @@
      *            the geometry to initialize VBO for.
      */
     protected void prepVBO(Geometry g) {
+        ensureCurrentContext();
         final GL gl = GLU.getCurrentGL();
 
         if (!supportsVBO())
@@ -1317,6 +1362,7 @@
      * commands in the buffer.
      */
     public void flush() {
+        ensureCurrentContext();
         final GL gl = GLU.getCurrentGL();
 
         gl.glFlush();
@@ -1327,6 +1373,7 @@
      * waiting OpenGL commands have been finished.
      */
     public void finish() {
+        ensureCurrentContext();
         final GL gl = GLU.getCurrentGL();
 
         gl.glFinish();
@@ -1341,6 +1388,7 @@
      * @return true if VBO is used for indicis, false if not
      */
     protected boolean predrawGeometry(Geometry g) {
+        ensureCurrentContext();
         final GL gl = GLU.getCurrentGL();
 
         RenderContext<GLContext> context = display
@@ -1569,6 +1617,7 @@
     }
 
     private void applyNormalMode(Spatial.NormalsMode normMode, Geometry t) {
+        ensureCurrentContext();
         final GL gl = GLU.getCurrentGL();
 
         switch (normMode) {
@@ -1620,6 +1669,7 @@
     }
 
     protected boolean doTransforms(Spatial t) {
+        ensureCurrentContext();
         final GL gl = GLU.getCurrentGL();
 
         // set world matrix
@@ -1662,6 +1712,7 @@
     }
 
     protected void undoTransforms(Spatial t) {
+        ensureCurrentContext();
         final GL gl = GLU.getCurrentGL();
 
         if (!generatingDisplayList
@@ -1675,6 +1726,7 @@
 
     // inherited documentation
     public int createDisplayList(Geometry g) {
+        ensureCurrentContext();
         final GL gl = GLU.getCurrentGL();
 
         int listID = gl.glGenLists(1);
@@ -1704,6 +1756,7 @@
 
     // inherited documentation
     public void releaseDisplayList(int listId) {
+        ensureCurrentContext();
         final GL gl = GLU.getCurrentGL();
 
         gl.glDeleteLists(listId, 1);
@@ -1711,6 +1764,7 @@
 
     // inherited documentation
     public void setPolygonOffset(float factor, float offset) {
+        ensureCurrentContext();
         final GL gl = GLU.getCurrentGL();
 
         gl.glEnable(GL.GL_POLYGON_OFFSET_FILL);
@@ -1719,6 +1773,7 @@
 
     // inherited documentation
     public void clearPolygonOffset() {
+        ensureCurrentContext();
         final GL gl = GLU.getCurrentGL();
 
         gl.glDisable(GL.GL_POLYGON_OFFSET_FILL);
@@ -1814,6 +1869,7 @@
             final int dstY, final Image srcImage, final int srcX,
             final int srcY, final int width, final int height)
             throws JmeException {
+        ensureCurrentContext();
         GL gl = GLU.getCurrentGL();
 
         // Check that the texture type is supported.
@@ -1885,6 +1941,7 @@
 
     @Override
     public void checkCardError() throws JmeException {
+        ensureCurrentContext();
         final GL gl = GLU.getCurrentGL();
         final GLU glu = new GLU();
 
@@ -1901,6 +1958,7 @@
 
     @Override
     public void cleanup() {
+        ensureCurrentContext();
         // clear vbos
         RendererRecord rendRecord = (RendererRecord) DisplaySystem
                 .getDisplaySystem().getCurrentContext().getRendererRecord();

Index: /home/julien/workspace/jme/src/com/jme/system/jogl/JOGLDisplaySystem.java
===================================================================
--- /home/julien/workspace/jme/src/com/jme/system/jogl/JOGLDisplaySystem.java   (revision 4383)
+++ /home/julien/workspace/jme/src/com/jme/system/jogl/JOGLDisplaySystem.java   (working copy)
@@ -48,6 +48,7 @@
 import java.awt.event.MouseWheelListener;
 import java.awt.event.WindowAdapter;
 import java.awt.event.WindowEvent;
+import java.lang.reflect.InvocationTargetException;
 import java.util.HashMap;
 import java.util.logging.Level;
 import java.util.logging.Logger;
@@ -80,6 +81,7 @@
 import com.jmex.awt.input.AWTKeyInput;
 import com.jmex.awt.input.AWTMouseInput;
 import com.jmex.awt.jogl.JOGLAWTCanvas;
+import javax.swing.SwingUtilities;
 
 /**
  * @author Steve Vaughan
@@ -111,8 +113,8 @@
     }
 
     @Override
-    public synchronized void createWindow(int width, int height, int bpp,
-            int frq, boolean fs) {
+    public synchronized void createWindow(final int width, final int height, final int bpp,
+            final int frq, final boolean fs) {
         // For know, ensure that only one OpenGL surface is active at a time.
         if (autoDrawable != null) {
             throw new IllegalStateException(
@@ -141,10 +143,10 @@
         frame = new Frame();
 
         // Create the singleton's status.
-        GLCanvas glCanvas = createGLCanvas();
+      final GLCanvas glCanvas = createGLCanvas();
         glCanvas.setSize(width, height);
-        glCanvas.setIgnoreRepaint(true);
-        glCanvas.setAutoSwapBufferMode(false);
+//      glCanvas.setIgnoreRepaint(true);
+//      glCanvas.setAutoSwapBufferMode(false);
 
         // GLContext glContext = glCanvas.getContext();
         // glContext.makeCurrent();
@@ -149,6 +151,15 @@
         // GLContext glContext = glCanvas.getContext();
         // glContext.makeCurrent();
         frame.add(glCanvas);
+
+      // MHENZE (cylab) - FIX Issue 24: Modify and show the Frame from the AWT Event-Thread
+      try
+      {
+         SwingUtilities.invokeAndWait(new Runnable()
+         {
+
+            public void run()
+            {
         final boolean isDisplayModeModified;
         final GraphicsDevice gd = GraphicsEnvironment
                 .getLocalGraphicsEnvironment().getDefaultScreenDevice();
@@ -222,8 +233,8 @@
                      // and the screen size is not equal to the canvas size
                      Dimension screenSize=Toolkit.getDefaultToolkit().getScreenSize();
                      if (screenSize.width != width || screenSize.height != height) {
-                         this.width = screenSize.width;
-                         this.height = screenSize.height;
+                            JOGLDisplaySystem.this.width = screenSize.width;
+                            JOGLDisplaySystem.this.height = screenSize.height;
                          glCanvas.setSize(screenSize);
                      }
                  }
@@ -236,8 +247,8 @@
                 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
                 // Resize the canvas
                 glCanvas.setSize(screenSize);
-                this.width = screenSize.width;
-                this.height = screenSize.height;
+                     JOGLDisplaySystem.this.width = screenSize.width;
+                     JOGLDisplaySystem.this.height = screenSize.height;
                 // Resize the frame so that it occupies the whole screen
                 frame.setSize(screenSize);
                 // Set its location at the top left corner
@@ -273,6 +284,20 @@
         // Make the window visible to realize the OpenGL surface.
         frame.setVisible(true);
 
+               // Now it is time to request the focus because the canvas
+               // is displayable, focusable, visible and its ancestor is
+               // visible too
+               glCanvas.requestFocusInWindow();
+            }
+         });
+      } catch (InterruptedException ex)
+      {
+         Logger.getLogger(JOGLDisplaySystem.class.getName()).log(Level.SEVERE, null, ex);
+      } catch (InvocationTargetException ex)
+      {
+         Logger.getLogger(JOGLDisplaySystem.class.getName()).log(Level.SEVERE, null, ex);
+      }
+
         // Make the GLContext the current.
         GLContext glContext = glCanvas.getContext();
         while (glContext.makeCurrent() == GLContext.CONTEXT_NOT_CURRENT) {
@@ -282,10 +307,6 @@
                 logger.warning("Interruped while waiting for makeCurrent()");
             }
         }
-        // Now it is time to request the focus because the canvas
-        // is displayable, focusable, visible and its ancestor is
-        // visible too
-        glCanvas.requestFocusInWindow();
 
         // Store singleton OpenGL canvas.
         autoDrawable = glCanvas;
@@ -394,7 +415,8 @@
         // FIXME All of this is boilerplate which should be a part of the
         // MouseInput, KeyInput, etc. classes if possible.
         MouseInput.setProvider(MouseInput.INPUT_AWT);
-        ((AWTMouseInput) MouseInput.get()).setDragOnly(true);
+      // MHENZE (cylab) - FIX Issue 35: AWTMouseInput.dragOnly interferes with MouseLook.buttonPressRequired...
+      //((AWTMouseInput) MouseInput.get()).setDragOnly(true);
         final MouseListener mouseListener = (MouseListener) MouseInput.get();
         glCanvas.addMouseListener(mouseListener);
         glCanvas.addMouseMotionListener((MouseMotionListener) MouseInput.get());
@@ -431,6 +453,11 @@
         return glCanvas;
     }
 
+   public GLContext getGLContext()
+   {
+      return autoDrawable==null? null:autoDrawable.getContext();
+   }
+
     @Override
     public JOGLRenderer getRenderer() {
         return renderer;
Index: /home/julien/workspace/jme/src/com/jmex/awt/input/AWTMouseInput.java
===================================================================
--- /home/julien/workspace/jme/src/com/jmex/awt/input/AWTMouseInput.java   (revision 4383)
+++ /home/julien/workspace/jme/src/com/jmex/awt/input/AWTMouseInput.java   (working copy)
@@ -56,6 +56,10 @@
 import com.jme.input.InputSystem;
 import com.jme.input.MouseInput;
 import com.jme.input.MouseInputListener;
+import java.awt.Robot;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.swing.SwingUtilities;
 
 /**
  * <code>AWTMouseInput</code>
@@ -65,6 +69,9 @@
  */
 public class AWTMouseInput extends MouseInput implements MouseListener, MouseWheelListener, MouseMotionListener {
 
+    private static final Logger logger = Logger.getLogger(AWTMouseInput.class.getName());
+    private static final Object awtMutex = new Object();
+
     public static int WHEEL_AMP = 40;   // arbitrary...  Java's mouse wheel seems to report something a lot lower than lwjgl's
 
     private int currentWheelDelta;
@@ -81,12 +88,30 @@
 
     private Component deltaRelative;
    
-    private boolean isCursorVisible = true;     
+    private boolean isCursorVisible = false;
     private static Cursor transparentCursor = null;  
     private Cursor opaqueCursor = null;
 
+    // MHenze (cylab) Fix Issue 35: Introduce a Robot te recenter the mouse on movement
+    private Robot robot;
+    private Point location;
+    private Point centerLocation;
+    private Point centerLocationOnScreen;
+    private Point lastKnownLocation;
+    private boolean isRecentering;
+    private int eventsSinceRecenter;
+
     protected AWTMouseInput() {
-        // Nothing to do
+        location = new Point();
+        centerLocation = new Point();
+        centerLocationOnScreen = new Point();
+        lastKnownLocation = new Point();
+
+        try {
+            robot = new java.awt.Robot();
+        } catch (java.awt.AWTException e) {
+            logger.log(Level.SEVERE, "Could not create a robot, so the mouse cannot be grabbed! ", e);
+    }
     }
 
     @Override
@@ -185,6 +210,14 @@
 
     @Override
     public void update() {
+        // MHenze (cylab) Fix Issue 35: In StandardGame, update is not called from the AWT Event Thread,
+        // so we need to synchronize on a mutex for event transfer
+        synchronized (awtMutex) {
+            unsafeUpdate();
+        }
+    }
+
+    protected void unsafeUpdate() {
         int x = lastEventX;
         int y = lastEventY;
 
@@ -192,6 +225,10 @@
         {
             while ( !swingEvents.isEmpty() )
             {
+                // MHenze (cylab) Fix Issue 35: use the stored corrected mouse coordinates instead of the ones in the events!
+                // this must be done for _all_ mouse events and not mouse moved alone
+                int newX = location.x;
+                int newY = location.y;
                 MouseEvent event = swingEvents.remove( 0 );
 
                 switch ( event.getID() ) {
@@ -199,10 +236,10 @@
                     case MouseEvent.MOUSE_MOVED:
                         for ( int i = 0; i < listeners.size(); i++ ) {
                             MouseInputListener listener = listeners.get( i );
-                            listener.onMove( event.getX() - x, y - event.getY(), event.getX(), event.getY() );
+                            listener.onMove(newX - x, y - newY, newX, newY);
                         }
-                        x = event.getX();
-                        y = event.getY();
+                        x = newX;
+                        y = newY;
                         break;
                     case MouseEvent.MOUSE_PRESSED:
                     case MouseEvent.MOUSE_RELEASED:
@@ -208,7 +245,7 @@
                     case MouseEvent.MOUSE_RELEASED:
                         for ( int i = 0; i < listeners.size(); i++ ) {
                             MouseInputListener listener = listeners.get( i );
-                            listener.onButton( getJMEButtonIndex( event ), event.getID() == MouseEvent.MOUSE_PRESSED, event.getX(), event.getY() );
+                            listener.onButton(getJMEButtonIndex(event), event.getID() == MouseEvent.MOUSE_PRESSED, newX, newY);
                         }
                         break;
                     case MouseEvent.MOUSE_WHEEL:
@@ -214,7 +251,7 @@
                     case MouseEvent.MOUSE_WHEEL:
                         for ( int i = 0; i < listeners.size(); i++ ) {
                             MouseInputListener listener = listeners.get( i );
-                            listener.onWheel( ((MouseWheelEvent)event).getUnitsToScroll()*WHEEL_AMP, event.getX(), event.getY() );
+                            listener.onWheel(((MouseWheelEvent) event).getUnitsToScroll() * WHEEL_AMP, newX, newY);
                         }
                         break;
                     default:
@@ -316,6 +353,13 @@
         deltaRelative = c;
     }
 
+    // MHenze (cylab) Fix Issue 35: introduced to handle the mouse centering conditionally,
+    // bound to a visible cursor for now
+   public boolean isMouseGrabbed()
+   {
+      return !isCursorVisible();
+   }
+
     /**
      * @return Returns the enabled.
      */
@@ -353,6 +397,8 @@
     }
 
     public void mousePressed(MouseEvent arg0) {
+        // MHenze (cylab) Fix Issue 35: all accesses to the swingEvents list have to be synchronized for StandardGame to work...
+        synchronized(awtMutex) {
       if (!enabled) {
          return;
       }
@@ -362,6 +408,7 @@
 
         swingEvents.add( arg0 );
     }
+    }
 
     private int getJMEButtonIndex( MouseEvent arg0 ) {
         int index;
@@ -381,6 +428,8 @@
     }
 
     public void mouseReleased(MouseEvent arg0) {
+        // MHenze (cylab) Fix Issue 35: all accesses to the swingEvents list have to be synchronized for StandardGame to work...
+        synchronized(awtMutex) {
       if (!enabled) {
          return;
       }
@@ -393,13 +442,22 @@
 
         swingEvents.add( arg0 );
     }
+    }
 
     public void mouseEntered(MouseEvent arg0) {
-        ; // ignore for now
+        // MHenze (cylab) Fix Issue 35: Workaround to set update the mouse cursor and grabbed status on mouse enter
+        setCursorVisible(isCursorVisible());
+        // MHenze (cylab) Fix Issue 35: if the mouse should be grabbed center the cursor to this component
+        if (isMouseGrabbed()) {
+            recenterMouse(arg0.getComponent());
+    }
     }
 
     public void mouseExited(MouseEvent arg0) {
-        ; // ignore for now
+        // MHenze (cylab) Fix Issue 35: if the mouse should be grabbed, prevent the mouse from leaving the component
+        if (isMouseGrabbed()) {
+            recenterMouse(arg0.getComponent());
+    }
     }
 
 
@@ -408,6 +466,8 @@
     // **********************************
 
     public void mouseWheelMoved(MouseWheelEvent arg0) {
+        // MHenze (cylab) Fix Issue 35: all accesses to the swingEvents list have to be synchronized for StandardGame to work...
+        synchronized(awtMutex) {
       if (!enabled) {
          return;
       }
@@ -418,6 +478,7 @@
 
         swingEvents.add( arg0 );
     }
+    }
 
 
     // **********************************
@@ -428,16 +489,42 @@
       if (!enabled) {
          return;
       }
+        if (isRecentering) {
+            // MHenze (cylab) Fix Issue 35:
+            // As long as the MouseInput is in recentering mode, nothing is done until the mouse is entered in the component
+            // by the events generated by the robot. If this happens, the last known location is resetted.
+            if ((centerLocation.x == arg0.getX() && centerLocation.y == arg0.getY()) || eventsSinceRecenter++ == 5) {
+                lastKnownLocation.x = arg0.getX();
+                lastKnownLocation.y = arg0.getY();
+                isRecentering = false;
+            }
+        } else {
+            // MHenze (cylab) Fix Issue 35:
+            // Compute the delta and absolute coordinates and recenter the mouse if necessary
+            int dx = arg0.getX() - lastKnownLocation.x;
+            int dy = arg0.getY() - lastKnownLocation.y;
+            location.x += dx;
+            location.y += dy;
+            if (isMouseGrabbed()) {
+                recenterMouse(arg0.getComponent());
+            }
+            lastKnownLocation.x = arg0.getX();
+            lastKnownLocation.y = arg0.getY();
 
-        absPoint.setLocation(arg0.getPoint());
+            // MHenze (cylab) Fix Issue 35: all accesses to the swingEvents list have to be synchronized for StandardGame to work...
+            synchronized (awtMutex) {
+                // TODO MHenze (cylab): Cleanup. this members seem obsolete by the newly introduced above.
+                absPoint.setLocation(location);
       if (lastPoint.x == Integer.MIN_VALUE) {
          lastPoint.setLocation(absPoint.x, absPoint.y);
       }
-        currentDeltaPoint.x = absPoint.x-lastPoint.x;
-        currentDeltaPoint.y = -(absPoint.y-lastPoint.y);
-        lastPoint.setLocation(arg0.getPoint());
+                currentDeltaPoint.x = absPoint.x - lastPoint.x;
+                currentDeltaPoint.y = -(absPoint.y - lastPoint.y);
+                lastPoint.setLocation(location);
 
-        swingEvents.add( arg0 );
+                swingEvents.add(arg0);
+    }
+        }
     }
 
     public void mouseMoved(MouseEvent arg0) {
@@ -446,6 +533,21 @@
       }
     }
 
+    // MHenze (cylab) Fix Issue 35: A method to generate recenter the mouse to allow the InputSystem to "grab" the mouse
+    private void recenterMouse(final Component component) {
+        if (robot != null) {
+            eventsSinceRecenter = 0;
+            isRecentering = true;
+            SwingUtilities.invokeLater(new Runnable() {
+                public void run() {
+                    centerLocation.setLocation(component.getWidth()/2, component.getHeight()/2);
+                    centerLocationOnScreen.setLocation(centerLocation);
+                    SwingUtilities.convertPointToScreen(centerLocationOnScreen, component);
+                    robot.mouseMove(centerLocationOnScreen.x, centerLocationOnScreen.y);
+                }
+            });
+        }
+    }
    
    
 
Index: /home/julien/workspace/jme/src/com/jmex/awt/jogl/JOGLAWTCanvas.java
===================================================================
--- /home/julien/workspace/jme/src/com/jmex/awt/jogl/JOGLAWTCanvas.java   (revision 4383)
+++ /home/julien/workspace/jme/src/com/jmex/awt/jogl/JOGLAWTCanvas.java   (working copy)
@@ -228,7 +228,8 @@
     @Override
     public void paint(Graphics arg0) {
         dirty = true;
-        super.paint(arg0);
+        // MHENZE (cylab) - FIX Issue 24: DON'T call super.paint() to suppress the jogl painting from the AWTEvent-Thread!
+      // super.paint(arg0);   
     }
 
     public void setTargetRate(int fps) {

Index: /home/julien/workspace/jme/src/jmetest/util/JMEJOGLAWTInputTest.java
===================================================================
--- /home/julien/workspace/jme/src/jmetest/util/JMEJOGLAWTInputTest.java   (revision 0)
+++ /home/julien/workspace/jme/src/jmetest/util/JMEJOGLAWTInputTest.java   (revision 0)
@@ -0,0 +1,111 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package jmetest.util;
+
+import java.awt.Component;
+import java.awt.Frame;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.util.logging.Logger;
+import java.util.prefs.Preferences;
+
+import com.jme.input.InputHandler;
+import com.jme.input.InputSystem;
+import com.jme.input.KeyInput;
+import com.jme.input.KeyInputListener;
+import com.jme.input.action.InputAction;
+import com.jme.input.action.InputActionEvent;
+import com.jme.input.controls.GameControl;
+import com.jme.input.controls.GameControlManager;
+import com.jme.input.controls.binding.KeyboardBinding;
+import com.jme.input.controls.controller.ActionChangeController;
+import com.jme.input.controls.controller.ControlChangeListener;
+import com.jme.system.PreferencesGameSettings;
+import com.jme.system.jogl.JOGLSystemProvider;
+import com.jmex.game.StandardGame;
+import com.jmex.game.StandardGame.GameType;
+import com.jmex.game.state.BasicGameState;
+import com.jmex.game.state.GameStateManager;
+
+public class JMEJOGLAWTInputTest {
+
+    private StandardGame game;
+
+    private static final Logger logger = Logger.getLogger(JMEJOGLAWTInputTest.class.getName());
+
+    private JMEJOGLAWTInputTest(){
+        PreferencesGameSettings pgs=new PreferencesGameSettings(Preferences.userRoot());
+        pgs.setRenderer(JOGLSystemProvider.SYSTEM_IDENTIFIER);
+        //pgs.setRenderer(LWJGLSystemProvider.LWJGL_SYSTEM_IDENTIFIER);
+        pgs.setMusic(false);
+        pgs.setSFX(false);
+        pgs.setWidth(640);
+        pgs.setHeight(480);
+        pgs.setFullscreen(false);
+        this.game=new StandardGame("testcase",GameType.GRAPHICAL,pgs);
+        this.game.start();
+        BasicGameState state=new BasicGameState("testcase"){
+            private InputHandler input=null;
+
+            public final void update(final float tpf) {
+                super.update(tpf);
+                if(input==null)
+                    {GameControlManager gcm=new GameControlManager();
+                     GameControl exitControl=gcm.addControl("exit");
+                     exitControl.addBinding(new KeyboardBinding(KeyInput.KEY_ESCAPE));
+                     this.rootNode.addController(new ActionChangeController(exitControl,new ControlChangeListener(){
+                        public void changed(GameControl control, float oldValue,float newValue, float time) {
+                            logger.info("[CONTROL]"+control.getName());
+                        }
+
+                     }));
+                     KeyAdapter ka=new KeyAdapter(){
+                         @Override
+                         public void keyPressed(KeyEvent e){
+                             logger.info(e.toString());
+                         }
+                     };
+                     for(Frame frame:Frame.getFrames())
+                         {frame.addKeyListener(ka);
+                          for(Component c:frame.getComponents())
+                              c.addKeyListener(ka);
+                         }
+                     KeyInput.get().addListener(new KeyInputListener(){
+
+                         @Override
+                         public void onKey(char character, int keyCode, boolean pressed) {
+                             logger.info("[onKey] "+character+" "+keyCode+" "+pressed);
+                         }
+
+                     });
+                     this.input=new InputHandler(){
+                         boolean init=false;
+                         public void update(float tpf){
+                             super.update(tpf);
+                             if(!init)
+                                 {init=true;
+                                  addAction(new InputAction(){
+                                    public void performAction(
+                                            InputActionEvent evt) {
+                                                logger.info("[performAction] "+evt);
+                                    }
+                                  },InputHandler.DEVICE_KEYBOARD,InputHandler.BUTTON_ALL,InputHandler.AXIS_ALL,true);
+                                 }
+                         }
+                     };
+                    }
+                this.input.update(tpf);
+                InputSystem.update();
+            }
+        };
+        GameStateManager.getInstance().attachChild(state);
+        GameStateManager.getInstance().activateAllChildren();
+    }
+
+    public static final void main(String[] args){
+        new JMEJOGLAWTInputTest();
+    }
+}
Index: /home/julien/workspace/jme/src/jmetest/util/JMEJOGLAWTTest.java
===================================================================
--- /home/julien/workspace/jme/src/jmetest/util/JMEJOGLAWTTest.java   (revision 4383)
+++ /home/julien/workspace/jme/src/jmetest/util/JMEJOGLAWTTest.java   (working copy)
@@ -34,6 +34,7 @@
 
 import java.awt.Component;
 import java.util.HashMap;
+import java.util.logging.Level;
 import java.util.logging.Logger;
 
 import javax.media.opengl.GLAutoDrawable;
@@ -43,6 +44,10 @@
 import com.jme.image.Texture;
 import com.jme.input.FirstPersonHandler;
 import com.jme.input.InputHandler;
+import com.jme.input.KeyBindingManager;
+import com.jme.input.KeyInput;
+import com.jme.input.action.InputAction;
+import com.jme.input.action.InputActionEvent;
 import com.jme.math.FastMath;
 import com.jme.math.Quaternion;
 import com.jme.math.Vector3f;
@@ -57,6 +62,7 @@
 import com.jmex.awt.jogl.JOGLAWTCanvasConstructor;
 import com.jmex.swt.lwjgl.LWJGLSWTConstants;
 import com.sun.opengl.util.Animator;
+import javax.swing.SwingUtilities;
 
 /**
  * Test for JOGL AWT Canvas implementation. Based upon {@link JMESWTTest}.
@@ -70,6 +76,7 @@
 
     private static final Logger logger = Logger.getLogger(JMEJOGLAWTTest.class
             .getName());
+    private static Animator animator;
 
     static int width = 640, height = 480;
 
@@ -119,12 +126,12 @@
         // canvas.init();
         // canvas.render();
 
+        frame.setVisible(true);
+
         // FIXME Encapsulate this within the canvas in some fashion?
-        Animator animator = new Animator((GLAutoDrawable) jmeCanvas);
+        animator = new Animator((GLAutoDrawable) jmeCanvas);
         animator.start();
 
-        frame.setVisible(true);
-
         // TODO Remove when complete (original SWT code).
         // while (!shell.isDisposed()) {
         // if (!display.readAndDispatch())
@@ -155,6 +162,7 @@
             super(width, height);
         }
 
+      @Override
         public void simpleSetup() {
             // Normal Scene setup stuff...
             rotQuat = new Quaternion();
@@ -185,8 +193,28 @@
             startTime = System.currentTimeMillis() + 5000;
 
             input = new FirstPersonHandler(cam, 50, 1);
+            input.addAction(
+                new InputAction(){
+                    public void performAction(InputActionEvent evt) {
+                        DisplaySystem.getDisplaySystem().getRenderer().cleanup();
+                        TextureManager.doTextureCleanup();
+                        TextureManager.clearCache();
+                        // Run this on another thread than the opengl thread to
+                        // make sure the call to Animator.stop() completes before
+                        // exiting
+                        new Thread(new Runnable() {
+                            public void run() {
+                                animator.stop();
+                                System.exit(0);
+        }
+                        }).start();
+                    }
+                },
+                InputHandler.DEVICE_KEYBOARD, KeyInput.KEY_ESCAPE,InputHandler.AXIS_NONE,false
+            );
         }
 
+      @Override
         public void simpleUpdate() {
             input.update(tpf);
 

Hi!



As the fixes are not completely stable (except under Linux), we (I'm not alone) need much time to improve them.

take your time :slight_smile:



Because of the size of the patch, its pretty much impossible for someone else to evaluate it.

When your patch is 'final', it would be good if you could make a small summary of the changes or the bugs it fixes.



That way we can apply the patch and verify the fixes.



Great would be a TestCase which shows the different problems.

Hi!


Core-Dump said:

take your time :)

Some people need this patch, I convinced some students to use JME 2 with JOGL for their projects.

Core-Dump said:

Because of the size of the patch, its pretty much impossible for someone else to evaluate it.
When your patch is 'final', it would be good if you could make a small summary of the changes or the bugs it fixes.

That way we can apply the patch and verify the fixes.

Great would be a TestCase which shows the different problems.

TUER is the best testcase. Before using these fixes:
- the mouse look worked only when a mouse button was pressed
- the mouse look did not work anymore when the mouse pointer reached the edge of the window
- the StandardGame class did not work with the JOGL renderer, we got only a black screen everywhere and the AWT input handling did not work at least under Linux

These fixes work at least under Linux and Windows XP. It has not been tested under Mac and there is still a problem with Windows Vista. Windows Seven displays a very strange message and the program behaves like a  non-graphical service.

// MHENZE (cylab) - FIX Issue 24: Release the context to give the controlled surface back to AWT, if JOGL is not driven from the EDT!


I see that you're using makeCurrent() inside every renderer method. It is considered bad practice to use makeCurrent in single-context applications. But you're using it because BaseGame/StandardGame create a thread which does rendering, while JOGL prefers to handle rendering in EDT, right? I think this is just too much, perhaps instead of trying to hack around this dependency on LWJGL's preferred method of doing things, you should simply change the LWJGL renderer to create a seperate thread to run the rendering, while the JOGL renderer runs in the EDT.
This is the approach I took in jME3, and I did not have issues with it.
Momoko_Fan said:

// MHENZE (cylab) - FIX Issue 24: Release the context to give the controlled surface back to AWT, if JOGL is not driven from the EDT!


I see that you're using makeCurrent() inside every renderer method. It is considered bad practice to use makeCurrent in single-context applications. But you're using it because BaseGame/StandardGame create a thread which does rendering, while JOGL prefers to handle rendering in EDT, right? I think this is just too much, perhaps instead of trying to hack around this dependency on LWJGL's preferred method of doing things, you should simply change the LWJGL renderer to create a seperate thread to run the rendering, while the JOGL renderer runs in the EDT.
This is the approach I took in jME3, and I did not have issues with it.

I suggested the approach you took in JME3 to cylab but he preferred implementing the approach you can see in this patch. Actually, he explained to me that makeCurrent() is rarely called. Maybe I should ask him to explain to you his choices of implementation. One other reason is that your approach would have required a deeper refactoring of the abstraction layer and he wanted to avoid it.

The more I use the JOGL/AWT side of JMonkeyEngine, the more I find bugs. Lots of render states cannot be instantiated when using CloneImportExport (JOGLRenderState, JOGLLightState, JOGLFogState, JOGLBlendState, JOGLVertexProgramState, JOGLFragmentProgramState, JOGLShaderObjectsState, JOGLStencilState), I’m going to fix it too. It is nonsensical. I wonder whether anyone else really uses deeply this part of the engine.



Please use the JOGL renderer  :’(

gouessej said:

I wonder whether anyone else really uses deeply this part of the engine.

Please use the JOGL renderer  :'(

XD
I guess people are happy with the LWJGL renderer so they don't use JOGL.
Momoko_Fan said:

gouessej said:

I wonder whether anyone else really uses deeply this part of the engine.

Please use the JOGL renderer  :'(

XD
I guess people are happy with the LWJGL renderer so they don't use JOGL.

I assume lots of programmers don't even know JMonkeyEngine 2 has a JOGL renderer and the LWJGL renderer is the default renderer used by this engine. StandardGame with JOGL is completely unusable without our fixes. I have used LWJGL only a very few times and it has often turned into a nightmare (as I have already explained), that is why I think some people might be interested in using the JOGL renderer.

On the other hand, I won't submit anything without a clear approval. I'm not able to perform a huge refactoring of the JOGL renderer now. Please Momoko_Fan choose one of these options:
- when the fixes I propose work reliably on every operating systems supported by JMonkeyEngine 2 including Microsoft Windows Vista, you will allow me to submit our changes (it is better than nothing and if you're right, JME 3 already uses a better mechanism)
- you perform a refactoring of the JOGL renderer following the principle you evoked ("simply" change the LWJGL renderer to create a seperate thread to run the rendering, while the JOGL renderer runs in the EDT), a kind of back integration from JMonkeyEngine 3 to JMonkeyEngine 2
- when the fixes I propose work reliably on every operating systems supported by JMonkeyEngine 2 including Microsoft Windows Vista, you will allow me to submit our changes (it is better than nothing and if you're right, JME 3 already uses a better mechanism) but you will explain to me more precisely which changes you suggest and I will try to implement them later

A decision has to be made.
Momoko_Fan said:

// MHENZE (cylab) - FIX Issue 24: Release the context to give the controlled surface back to AWT, if JOGL is not driven from the EDT!


I see that you're using makeCurrent() inside every renderer method. It is considered bad practice to use makeCurrent in single-context applications. But you're using it because BaseGame/StandardGame create a thread which does rendering, while JOGL prefers to handle rendering in EDT, right? I think this is just too much, perhaps instead of trying to hack around this dependency on LWJGL's preferred method of doing things, you should simply change the LWJGL renderer to create a seperate thread to run the rendering, while the JOGL renderer runs in the EDT.
This is the approach I took in jME3, and I did not have issues with it.


I am the one who wrote the fix. The makeCurrent is only called on a render method it the context is not already current. As long as rendering stays on one thread it should be as realiable as LWJGL and for sure is much better than the current state of the JOGL renderer in JME2 (which essentially isn't working in a real life scenario).

You are right though, that a better approach would be to change the overall behaviour of the api in respect to threading, like providing a renderer-thread by the display system and not from StandardGame. In fact I think StandardGame in it's current form hinders a rendering abstraction, since it also calls update and render and some other stuff instead of calling some capsulating method via the display system.

However I don't feel able to implement such a change, since I don't want to break anything and I don't want to dive too deply into JME core stuff (for various reasons - like being a xith developer for example ;))

I think my changes improve the useability of the JOGL renderer in JME2 and should be submitted to the code base. Otherwise you should backport your JME3 approach that achieves the same result with the same low risk of breaking anything...

Hi!



I have just committed JMEJOGLAWTInputTest as it does not break anything and it is useful. I think I know why the remaining changes don't work under Vista, it may come from JOGLDisplaySystem.

Hey I have recently implemented AWT mouse support in jME3 and these fixes seem to work fine. I am using Windows 7 64 bit (which is nearly identical to vista) and except for the mouse escaping for a millisecond when moved very fast it works okay.



EDIT: It seems to fail in fullscreen though. Moving the mouse around and its like lagging by 1 second, anybody know what's going on?

Momoko_Fan said:

Hey I have recently implemented AWT mouse support in jME3 and these fixes seem to work fine. I am using Windows 7 64 bit (which is nearly identical to vista) and except for the mouse escaping for a millisecond when moved very fast it works okay.

EDIT: It seems to fail in fullscreen though. Moving the mouse around and its like lagging by 1 second, anybody know what's going on?


In case it helps you to delimit the problem, I have stripped out the jME input system, replaced the GLCanvas with a GLJPanel, eliminated the SimpleGame/StandardGame stuff in order to render in correct threads, and replaced the Frame with a JFrame-- and mouse input and every other input works perfectly this way, fullscreen/windowwed/dynamically-resizing/everything.  This indicates the the issues you describe, minor and major, are likely to be within the jME threading or input mapping system, which I do not use (I.e. not with the JOGL library itself).

... I'm assuming that this Topic is still about JOGL, as indicated in the Topic title.


If other work does not interrupt, I'll post a Java6 Webstart demo today which will show a jME JOGL MDI app running with perfect Swing behavior and high frame rate.

A little late, but this examplifies pretty much all Swing behavior one could ask for.



http://pub.admc.com/modeler/jogldemo01.jnlp



Requires Java 6.



I’m getting widely varying performance on Windows.  Linux is consistently high.  I’m getting from 15 to 65 FPS on different Windows PCs here.  It would be useful if anybody could hit “t” (for Timer) and report their average frame rates (and what platform they’re on).



See what the keys “p”, “i”, “o”, “O”, “r”, “R” do.  Definitely try different settings on the Settings screen (in the File pulldown).

Hi Blaine,



works very well on Vista 64Bit, Gforce GTX 260, core i7 at 64 Fps.

But what does "O" do?



Regards,

Snare

snareoj2 said:

Hi Blaine,

works very well on Vista 64Bit, Gforce GTX 260, core i7 at 64 Fps.
But what does "O" do?

Regards,
Snare


Thanks very much for the feedback.  Good to know that it works with Intel 64 on Win.  "O" opens a dummy internal frame which is jus like "o", but is modal.  Is that not working?  Perhaps some other field or internal frame has focus when you are typing "O"?

Ah now i see, the popup of o vs O looks different.



Its a bit strange if a modal window is open everything outside of the modal window has a "waiting" cursor - at first startup, i thought it is still doing sth…



Greets



Snare

snareoj2 said:

Ah now i see, the popup of o vs O looks different.

Its a bit strange if a modal window is open everything outside of the modal window has a "waiting" cursor - at first startup, i thought it is still doing sth...

Greets

Snare


Valid point.  I should change that cursor to something to indicate inactiveness instead of waiting.