From 8d33d4c33d436f634bcb918977dd0a058d7edbda Mon Sep 17 00:00:00 2001
From: Sven Gothel <sgothel@jausoft.com>
Date: Wed, 15 Mar 2023 03:12:23 +0100
Subject: GraphUI: Scene/Shape: Decouple PMVMatrix operations (picking,
 obj<->win) by using a local instance, enable performing on any thread (not
 using a blocking GL renderer thread)

Shape got 2 win<->obj and size-retrieval variants:
(a) using given PMVMatrix and viewport
(b) using a local PMVMatrix w/ Scene's viewport and using Scene's setupMatrix()
---
 .../classes/com/jogamp/graph/ui/gl/Scene.java      | 131 ++++++++++-----------
 .../classes/com/jogamp/graph/ui/gl/Shape.java      | 127 ++++++++++++++------
 .../com/jogamp/graph/ui/gl/shapes/GLButton.java    |   2 +-
 3 files changed, 149 insertions(+), 111 deletions(-)

(limited to 'src/graphui/classes/com')

diff --git a/src/graphui/classes/com/jogamp/graph/ui/gl/Scene.java b/src/graphui/classes/com/jogamp/graph/ui/gl/Scene.java
index 88e87b6cf..ac5258595 100644
--- a/src/graphui/classes/com/jogamp/graph/ui/gl/Scene.java
+++ b/src/graphui/classes/com/jogamp/graph/ui/gl/Scene.java
@@ -66,7 +66,7 @@ import com.jogamp.opengl.util.PMVMatrix;
  * </p>
  * @see Shape
  */
-public class Scene implements GLEventListener {
+public final class Scene implements GLEventListener {
     /** Default scene distance on z-axis to projection is -1/5f. */
     public static final float DEFAULT_SCENE_DIST = -1/5f;
     /** Default projection angle in degrees value is 45.0. */
@@ -208,7 +208,7 @@ public class Scene implements GLEventListener {
     public void removeShape(final Shape b) {
         shapes.remove(b);
     }
-    public final Shape getShapeByIdx(final int id) {
+    public Shape getShapeByIdx(final int id) {
         if( 0 > id ) {
             return null;
         }
@@ -303,19 +303,26 @@ public class Scene implements GLEventListener {
         renderer.enable(gl, false);
     }
 
+    /**
+     * Attempt to pick a {@link Shape} using the window coordinates and contained {@ling Shape}'s {@link AABBox} {@link Shape#getBounds() bounds}
+     * using a ray-intersection algorithm.
+     * <p>
+     * If {@link Shape} was found the given action is performed.
+     * </p>
+     * <p>
+     * Method performs on current thread and returns after probing every {@link Shape}.
+     * </p>
+     * @param glWinX window X coordinate, bottom-left origin
+     * @param glWinY window Y coordinate, bottom-left origin
+     * @param objPos storage for found object position in model-space of found {@link Shape}
+     * @param shape storage for found {@link Shape} or null
+     * @param runnable the action to perform if {@link Shape} was found
+     */
     public void pickShape(final int glWinX, final int glWinY, final float[] objPos, final Shape[] shape, final Runnable runnable) {
-        if( null == cDrawable ) {
-            return;
+        shape[0] = pickShapeImpl(glWinX, glWinY, objPos);
+        if( null != shape[0] ) {
+            runnable.run();
         }
-        cDrawable.invoke(false, new GLRunnable() {
-            @Override
-            public boolean run(final GLAutoDrawable drawable) {
-                shape[0] = pickShapeImpl(glWinX, glWinY, objPos);
-                if( null != shape[0] ) {
-                    runnable.run();
-                }
-                return true;
-            } } );
     }
     @SuppressWarnings({ "unchecked", "rawtypes" })
     private Shape pickShapeImpl(final int glWinX, final int glWinY, final float[] objPos) {
@@ -326,8 +333,8 @@ public class Scene implements GLEventListener {
             gl.glReadPixels( x, y, 1, 1, GL2ES2.GL_DEPTH_COMPONENT, GL.GL_FLOAT, winZRB);
             winZ1 = winZRB.get(0); // dir
         */
-        final PMVMatrix pmv = renderer.getMatrix();
-        pmv.glMatrixMode(GLMatrixFunc.GL_MODELVIEW);
+        final PMVMatrix pmv = new PMVMatrix();
+        setupMatrix(pmv);
 
         final Ray ray = new Ray();
 
@@ -370,26 +377,9 @@ public class Scene implements GLEventListener {
      * @param runnable action
      */
     public void winToObjCoord(final Shape shape, final int glWinX, final int glWinY, final float[] objPos, final Runnable runnable) {
-        if( null == cDrawable || null == shape ) {
-            return;
+        if( null != shape && shape.winToObjCoord(this, glWinX, glWinY, objPos) ) {
+            runnable.run();
         }
-        cDrawable.invoke(false, new GLRunnable() {
-            @Override
-            public boolean run(final GLAutoDrawable drawable) {
-                final boolean ok;
-                {
-                    final PMVMatrix pmv = renderer.getMatrix();
-                    pmv.glMatrixMode(GLMatrixFunc.GL_MODELVIEW);
-                    pmv.glPushMatrix();
-                    shape.setTransform(pmv);
-                    ok = shape.winToObjCoord(renderer, glWinX, glWinY, objPos);
-                    pmv.glPopMatrix();
-                }
-                if( ok ) {
-                    runnable.run();
-                }
-                return true;
-            } } );
     }
 
     /**
@@ -450,17 +440,7 @@ public class Scene implements GLEventListener {
         final int[] viewport = { 0, 0, width, height };
 
         final PMVMatrix pmv = new PMVMatrix();
-        {
-            final float ratio = (float)width/(float)height;
-            pmv.glMatrixMode(GLMatrixFunc.GL_PROJECTION);
-            pmv.glLoadIdentity();
-            pmv.gluPerspective(projAngle, ratio, zNear, zFar);
-        }
-        {
-            pmv.glMatrixMode(GLMatrixFunc.GL_MODELVIEW);
-            pmv.glLoadIdentity();
-            pmv.glTranslatef(0f, 0f, sceneDist);
-        }
+        setupMatrix(pmv, width, height);
         {
             final float orthoDist = -sceneDist;
             final float[] obj00Coord = new float[3];
@@ -477,10 +457,10 @@ public class Scene implements GLEventListener {
     /**
      * Reshape scene {@link #setupMatrix(PMVMatrix, int, int)}.
      * <p>
-     * Projection will be setup using perspective {@link #getProjAngle()} with {@link #getProjZNear()} and {@link #getProjZFar()}.
+     * {@link GLMatrixFunc#GL_PROJECTION} is setup using perspective {@link #getProjAngle()} with {@link #getProjZNear()} and {@link #getProjZFar()}.
      * </p>
      * <p>
-     * Modelview is translated to given {@link #getProjSceneDist()}
+     * {@link GLMatrixFunc#GL_MODELVIEW} is translated to given {@link #getProjSceneDist()}
      * and and origin 0/0 becomes the bottom-left corner.
      * </p>
      * @see #setupMatrix(PMVMatrix, int, int)
@@ -493,7 +473,8 @@ public class Scene implements GLEventListener {
         renderer.reshapeNotify(x, y, width, height);
 
         final PMVMatrix pmv = renderer.getMatrix();
-        setupMatrix(pmv, width, height);
+        setupMatrix0(pmv, width, height);
+        pmv.glTranslatef(0f, 0f, sceneDist);
         {
             final float orthoDist = -sceneDist;
             final float[] obj00Coord = new float[3];
@@ -549,32 +530,43 @@ public class Scene implements GLEventListener {
     public final int[/*4*/] getViewport(final int[/*4*/] target) { return renderer.getViewport(target); }
 
     /** Borrows the current int[4] viewport w/o copying. It is set after initial {@link #reshape(GLAutoDrawable, int, int, int, int)}. */
-    public final int[/*4*/] getViewport() { return renderer.getViewport(); }
+    public int[/*4*/] getViewport() { return renderer.getViewport(); }
 
     /** Returns the {@link #getViewport()}'s width, set after initial {@link #reshape(GLAutoDrawable, int, int, int, int)}. */
     public int getWidth() { return renderer.getWidth(); }
     /** Returns the {@link #getViewport()}'s height, set after initial {@link #reshape(GLAutoDrawable, int, int, int, int)}. */
     public int getHeight() { return renderer.getHeight(); }
 
+    /** Borrow the current {@link PMVMatrix}. */
+    public PMVMatrix getMatrix() { return renderer.getMatrix(); }
+
     /** Translate current matrix to {@link #getBounds()}'s origin (minx/miny) and {@link #getProjSceneDist()}, a convenience method. */
-    public void translate(final PMVMatrix pmv) {
+    private void translate(final PMVMatrix pmv) {
         pmv.glTranslatef(planeBoxCtr.getMinX(), planeBoxCtr.getMinY(), sceneDist);
     }
 
     /**
-     * Setup {@link PMVMatrix} projection and modelview using explicit surface width and height before {@link #reshape(GLAutoDrawable, int, int, int, int)} happened, a convenience method.
+     * Setup {@link PMVMatrix} {@link GLMatrixFunc#GL_PROJECTION} and {@link GLMatrixFunc#GL_MODELVIEW}
+     * using explicit surface width and height before {@link #reshape(GLAutoDrawable, int, int, int, int)} happened, a convenience method.
      * <p>
-     * Projection will be setup using perspective {@link #getProjAngle()} with {@link #getProjZNear()} and {@link #getProjZFar()}.
+     * {@link GLMatrixFunc#GL_PROJECTION} is setup using perspective {@link #getProjAngle()} with {@link #getProjZNear()} and {@link #getProjZFar()}.
      * </p>
      * <p>
-     * Modelview is translated to given {@link #getProjSceneDist()}
+     * {@link GLMatrixFunc#GL_MODELVIEW} is translated to given {@link #getProjSceneDist()}
      * and and origin 0/0 becomes the bottom-left corner.
      * </p>
+     * <p>
+     * At the end of operations, the {@link GLMatrixFunc#GL_MODELVIEW} matrix is selected.
+     * </p>
      * @param pmv the {@link PMVMatrix} to setup
      * @param surface_width explicit surface width
      * @param surface_height explicit surface height
      */
     public void setupMatrix(final PMVMatrix pmv, final int surface_width, final int surface_height) {
+        setupMatrix0(pmv, surface_width, surface_height);
+        translate(pmv);
+    }
+    private void setupMatrix0(final PMVMatrix pmv, final int surface_width, final int surface_height) {
         final float ratio = (float)surface_width/(float)surface_height;
         pmv.glMatrixMode(GLMatrixFunc.GL_PROJECTION);
         pmv.glLoadIdentity();
@@ -582,23 +574,26 @@ public class Scene implements GLEventListener {
 
         pmv.glMatrixMode(GLMatrixFunc.GL_MODELVIEW);
         pmv.glLoadIdentity();
-        translate(pmv);
     }
 
     /**
-     * Setup {@link PMVMatrix} projection and modelview using implicit {@link #getViewport()} surface dimension after {@link #reshape(GLAutoDrawable, int, int, int, int)} happened, a convenience method.
+     * Setup {@link PMVMatrix} {@link GLMatrixFunc#GL_PROJECTION} and {@link GLMatrixFunc#GL_MODELVIEW}
+     * using implicit {@link #getViewport()} surface dimension after {@link #reshape(GLAutoDrawable, int, int, int, int)} happened, a convenience method.
      * <p>
-     * Projection will be setup using perspective {@link #getProjAngle()} with {@link #getProjZNear()} and {@link #getProjZFar()}.
+     * {@link GLMatrixFunc#GL_PROJECTION} is setup using perspective {@link #getProjAngle()} with {@link #getProjZNear()} and {@link #getProjZFar()}.
      * </p>
      * <p>
-     * Modelview is translated to given {@link #getProjSceneDist()}
+     * {@link GLMatrixFunc#GL_MODELVIEW} is translated to given {@link #getProjSceneDist()}
      * and and origin 0/0 becomes the bottom-left corner.
      * </p>
+     * <p>
+     * At the end of operations, the {@link GLMatrixFunc#GL_MODELVIEW} matrix is selected.
+     * </p>
      * @param pmv the {@link PMVMatrix} to setup
      * @param surface_width explicit surface width
      * @param surface_height explicit surface height
      */
-    public void setup(final PMVMatrix pmv) {
+    public void setupMatrix(final PMVMatrix pmv) {
         setupMatrix(pmv, getWidth(), getHeight());
     }
 
@@ -651,11 +646,9 @@ public class Scene implements GLEventListener {
                     final int glWinY = getHeight() - e.getY() - 1;
                     final float[] objPos = new float[3];
                     final Shape shape = activeShape;
-                    winToObjCoord(shape, glWinX, glWinY, objPos, new Runnable() {
-                        @Override
-                        public void run() {
-                            shape.dispatchGestureEvent(renderer, gh, glWinX, glWinY, objPos);
-                        } } );
+                    winToObjCoord(shape, glWinX, glWinY, objPos, () -> {
+                        shape.dispatchGestureEvent(Scene.this, gh, glWinX, glWinY, objPos);
+                    });
                 }
             }
         }
@@ -684,14 +677,12 @@ public class Scene implements GLEventListener {
     final void dispatchMouseEventPickShape(final MouseEvent e, final int glWinX, final int glWinY, final boolean setActive) {
         final float[] objPos = new float[3];
         final Shape[] shape = { null };
-        pickShape(glWinX, glWinY, objPos, shape, new Runnable() {
-           @Override
-        public void run() {
+        pickShape(glWinX, glWinY, objPos, shape, () -> {
                if( setActive ) {
                    setActiveShape(shape[0]);
                }
                shape[0].dispatchMouseEvent(e, glWinX, glWinY, objPos);
-           } } );
+           } );
     }
     /**
      * Dispatch event to shape
@@ -702,11 +693,7 @@ public class Scene implements GLEventListener {
      */
     final void dispatchMouseEventForShape(final Shape shape, final MouseEvent e, final int glWinX, final int glWinY) {
         final float[] objPos = new float[3];
-        winToObjCoord(shape, glWinX, glWinY, objPos, new Runnable() {
-            @Override
-            public void run() {
-                shape.dispatchMouseEvent(e, glWinX, glWinY, objPos);
-            } } );
+        winToObjCoord(shape, glWinX, glWinY, objPos, () -> { shape.dispatchMouseEvent(e, glWinX, glWinY, objPos); });
     }
 
     private class SBCMouseListener implements MouseListener {
diff --git a/src/graphui/classes/com/jogamp/graph/ui/gl/Shape.java b/src/graphui/classes/com/jogamp/graph/ui/gl/Shape.java
index f8967e437..4f81f4ff7 100644
--- a/src/graphui/classes/com/jogamp/graph/ui/gl/Shape.java
+++ b/src/graphui/classes/com/jogamp/graph/ui/gl/Shape.java
@@ -235,9 +235,9 @@ public abstract class Shape {
      * <p>
      * No matrix operations (translate, scale, ..) are performed.
      * </p>
-     * @param gl
-     * @param renderer
-     * @param sampleCount
+     * @param gl the current GL object
+     * @param renderer the used {@link RegionRenderer}, also source of {@link RegionRenderer#getMatrix()} and {@link RegionRenderer#getViewport()}.
+     * @param sampleCount sample count if used by Graph renderModes
      */
     public void drawShape(final GL2ES2 gl, final RegionRenderer renderer, final int[] sampleCount) {
         final float r, g, b, a;
@@ -361,24 +361,25 @@ public abstract class Shape {
     }
 
     /**
-     * Retrieve window surface size of this shape
+     * Retrieve window surface size of this shape reusing a given setup {@link PMVMatrix}.
      * <p>
-     * The {@link RegionRenderer#getMatrix()} has to be setup properly for this object,
-     * i.e. reshape for {@link GLMatrixFunc#GL_PROJECTION} and {@link #setTransform(PMVMatrix)} for {@link GLMatrixFunc#GL_MODELVIEW}.
+     * The given {@link PMVMatrix} has to be setup properly for this object,
+     * i.e. its {@link GLMatrixFunc#GL_PROJECTION} and {@link GLMatrixFunc#GL_MODELVIEW} for the surrounding scene
+     * including this shape's {@link #setTransform(PMVMatrix)}.
      * </p>
-     * @param renderer source of viewport and {@link PMVMatrix}
+     * @param pmv well formed {@link PMVMatrix}, e.g. could have been setup via {@link Scene#setupMatrix(PMVMatrix) setupMatrix(..)} and {@link #setTransform(PMVMatrix)}.
+     * @param viewport the int[4] viewport
      * @param surfaceSize int[2] target surface size
      * @return true for successful gluProject(..) operation, otherwise false
+     * @see #getSurfaceSize(Scene, int[])
      */
-    public boolean getSurfaceSize(final RegionRenderer renderer, final int[/*2*/] surfaceSize) {
+    public boolean getSurfaceSize(final PMVMatrix pmv, final int[/*4*/] viewport, final int[/*2*/] surfaceSize) {
         boolean res = false;
-        final int[/*4*/] viewport = renderer.getViewport(new int[4]);
         // System.err.println("UIShape::getSurfaceSize.VP "+viewport[0]+"/"+viewport[1]+" "+viewport[2]+"x"+viewport[3]);
         final float[] winCoordHigh = new float[3];
         final float[] winCoordLow = new float[3];
         final float[] high = getBounds().getHigh();
         final float[] low = getBounds().getLow();
-        final PMVMatrix pmv = renderer.getMatrix();
 
         if( pmv.gluProject(high[0], high[1], high[2], viewport, 0, winCoordHigh, 0) ) {
             // System.err.printf("UIShape::surfaceSize.H: shape %d: obj [%f, %f, %f] -> win [%f, %f, %f]%n", getName(), high[0], high[1], high[2], winCoordHigh[0], winCoordHigh[1], winCoordHigh[2]);
@@ -394,22 +395,40 @@ public abstract class Shape {
     }
 
     /**
-     * Map given object coordinate relative to this shape to window coordinates
+     * Retrieve window surface size of this shape using a local {@link PMVMatrix}.
+     * <p>
+     * The {@link Scene} has be {@link Scene#reshape(com.jogamp.opengl.GLAutoDrawable, int, int, int, int) reshape(..)}ed once.
+     * </p>
+     * @param scene {@link Scene} source of viewport and local {@link PMVMatrix} {@link Scene#setupMatrix(PMVMatrix) setupMatrix(..)}.
+     * @param surfaceSize int[2] target surface size
+     * @return true for successful gluProject(..) operation, otherwise false
+     * @see #getSurfaceSize(PMVMatrix, int[], int[])
+     */
+    public boolean getSurfaceSize(final Scene scene, final int[/*2*/] surfaceSize) {
+        final PMVMatrix pmv = new PMVMatrix();
+        scene.setupMatrix(pmv);
+        setTransform(pmv);
+        return getSurfaceSize(pmv, scene.getViewport(), surfaceSize);
+    }
+
+    /**
+     * Map given object coordinate relative to this shape to window coordinates reusing a given setup {@link PMVMatrix}.
      * <p>
-     * The {@link RegionRenderer#getMatrix()} has to be setup properly for this object,
-     * i.e. reshape for {@link GLMatrixFunc#GL_PROJECTION} and {@link #setTransform(PMVMatrix)} for {@link GLMatrixFunc#GL_MODELVIEW}.
+     * The given {@link PMVMatrix} has to be setup properly for this object,
+     * i.e. its {@link GLMatrixFunc#GL_PROJECTION} and {@link GLMatrixFunc#GL_MODELVIEW} for the surrounding scene
+     * including this shape's {@link #setTransform(PMVMatrix)}.
      * </p>
-     * @param renderer source of viewport and {@link PMVMatrix}
+     * @param pmv well formed {@link PMVMatrix}, e.g. could have been setup via {@link Scene#setupMatrix(PMVMatrix) setupMatrix(..)} and {@link #setTransform(PMVMatrix)}.
+     * @param viewport the int[4] viewport
      * @param objPos float[3] object position relative to this shape's center
      * @param glWinPos int[2] target window position of objPos relative to this shape
      * @return true for successful gluProject(..) operation, otherwise false
+     * @see #objToWinCoord(Scene, float[], int[])
      */
-    public boolean objToWinCoord(final RegionRenderer renderer, final float[/*3*/] objPos, final int[/*2*/] glWinPos) {
+    public boolean objToWinCoord(final PMVMatrix pmv, final int[/*4*/] viewport, final float[/*3*/] objPos, final int[/*2*/] glWinPos) {
         boolean res = false;
-        final int[/*4*/] viewport = renderer.getViewport(new int[4]);
         // System.err.println("UIShape::objToWinCoordgetSurfaceSize.VP "+viewport[0]+"/"+viewport[1]+" "+viewport[2]+"x"+viewport[3]);
         final float[] winCoord = new float[3];
-        final PMVMatrix pmv = renderer.getMatrix();
 
         if( pmv.gluProject(objPos[0], objPos[1], objPos[2], viewport, 0, winCoord, 0) ) {
             // System.err.printf("UIShape::objToWinCoord.0: shape %d: obj [%f, %f, %f] -> win [%f, %f, %f]%n", getName(), objPos[0], objPos[1], objPos[2], winCoord[0], winCoord[1], winCoord[2]);
@@ -422,23 +441,43 @@ public abstract class Shape {
     }
 
     /**
-     * Map given gl-window-coordinates to object coordinates relative to this shape and its z-coordinate.
+     * Map given object coordinate relative to this shape to window coordinates using a local {@link PMVMatrix}.
      * <p>
-     * The {@link RegionRenderer#getMatrix()} has to be setup properly for this object,
-     * i.e. reshape for {@link GLMatrixFunc#GL_PROJECTION} and {@link #setTransform(PMVMatrix)} for {@link GLMatrixFunc#GL_MODELVIEW}.
+     * The {@link Scene} has be {@link Scene#reshape(com.jogamp.opengl.GLAutoDrawable, int, int, int, int) reshape(..)}ed once.
      * </p>
-     * @param renderer source of viewport and {@link PMVMatrix}
+     * @param scene {@link Scene} source of viewport and local {@link PMVMatrix} {@link Scene#setupMatrix(PMVMatrix) setupMatrix(..)}.
+     * @param objPos float[3] object position relative to this shape's center
+     * @param glWinPos int[2] target window position of objPos relative to this shape
+     * @return true for successful gluProject(..) operation, otherwise false
+     * @see #objToWinCoord(PMVMatrix, int[], float[], int[])
+     */
+    public boolean objToWinCoord(final Scene scene, final float[/*3*/] objPos, final int[/*2*/] glWinPos) {
+        final PMVMatrix pmv = new PMVMatrix();
+        scene.setupMatrix(pmv);
+        setTransform(pmv);
+        return this.objToWinCoord(pmv, scene.getViewport(), objPos, glWinPos);
+    }
+
+    /**
+     * Map given gl-window-coordinates to object coordinates relative to this shape and its z-coordinate
+     * reusing a given setup {@link PMVMatrix}.
+     * <p>
+     * The given {@link PMVMatrix} has to be setup properly for this object,
+     * i.e. its {@link GLMatrixFunc#GL_PROJECTION} and {@link GLMatrixFunc#GL_MODELVIEW} for the surrounding scene
+     * including this shape's {@link #setTransform(PMVMatrix)}.
+     * </p>
+     * @param pmv well formed {@link PMVMatrix}, e.g. could have been setup via {@link Scene#setupMatrix(PMVMatrix) setupMatrix(..)} and {@link #setTransform(PMVMatrix)}.
+     * @param viewport the int[4] viewport
      * @param glWinX in GL window coordinates, origin bottom-left
      * @param glWinY in GL window coordinates, origin bottom-left
      * @param objPos float[3] target object position of glWinX/glWinY relative to this shape
-     * @return @return true for successful gluProject(..) and gluUnProject(..) operations, otherwise false
+     * @return true for successful gluProject(..) and gluUnProject(..) operations, otherwise false
+     * @see #winToObjCoord(Scene, int, int, float[])
      */
-    public boolean winToObjCoord(final RegionRenderer renderer, final int glWinX, final int glWinY, final float[/*3*/] objPos) {
+    public boolean winToObjCoord(final PMVMatrix pmv, final int[/*4*/] viewport, final int glWinX, final int glWinY, final float[/*3*/] objPos) {
         boolean res = false;
         final float[] ctr = getBounds().getCenter();
-        final int[] viewport = renderer.getViewport(new int[4]);
         final float[] tmp = new float[3];
-        final PMVMatrix pmv = renderer.getMatrix();
 
         if( pmv.gluProject(ctr[0], ctr[1], ctr[2], viewport, 0, tmp, 0) ) {
             // System.err.printf("UIShape::winToObjCoord.0: shape %d: obj [%f, %f, %f] -> win [%f, %f, %f]%n", getName(), ctr[0], ctr[1], ctr[2], tmp[0], tmp[1], tmp[2]);
@@ -450,6 +489,26 @@ public abstract class Shape {
         return res;
     }
 
+    /**
+     * Map given gl-window-coordinates to object coordinates relative to this shape and its z-coordinate
+     * using a local {@link PMVMatrix}.
+     * <p>
+     * The {@link Scene} has be {@link Scene#reshape(com.jogamp.opengl.GLAutoDrawable, int, int, int, int) reshape(..)}ed once.
+     * </p>
+     * @param scene {@link Scene} source of viewport and local {@link PMVMatrix} {@link Scene#setupMatrix(PMVMatrix) setupMatrix(..)}.
+     * @param glWinX in GL window coordinates, origin bottom-left
+     * @param glWinY in GL window coordinates, origin bottom-left
+     * @param objPos float[3] target object position of glWinX/glWinY relative to this shape
+     * @return @return true for successful gluProject(..) and gluUnProject(..) operations, otherwise false
+     * @see #winToObjCoord(PMVMatrix, int[], int, int, float[])
+     */
+    public boolean winToObjCoord(final Scene scene, final int glWinX, final int glWinY, final float[/*3*/] objPos) {
+        final PMVMatrix pmv = new PMVMatrix();
+        scene.setupMatrix(pmv);
+        setTransform(pmv);
+        return this.winToObjCoord(pmv, scene.getViewport(), glWinX, glWinY, objPos);
+    }
+
     public float[] getColor() {
         return rgbaColor;
     }
@@ -826,31 +885,23 @@ public abstract class Shape {
      * @param glWinX x-position in OpenGL model space
      * @param glWinY y-position in OpenGL model space
      */
-    /* pp */ final void dispatchGestureEvent(final RegionRenderer renderer, final GestureEvent e, final int glWinX, final int glWinY, final float[] objPos) {
+    /* pp */ final void dispatchGestureEvent(final Scene scene, final GestureEvent e, final int glWinX, final int glWinY, final float[] objPos) {
         if( resizable && e instanceof PinchToZoomGesture.ZoomEvent ) {
             final PinchToZoomGesture.ZoomEvent ze = (PinchToZoomGesture.ZoomEvent) e;
             final float pixels = ze.getDelta() * ze.getScale(); //
             final float[] objPos2 = { 0f, 0f, 0f };
             final int winX2 = glWinX + Math.round(pixels);
-            final boolean ok;
-            {
-                final PMVMatrix pmv = renderer.getMatrix();
-                pmv.glMatrixMode(GLMatrixFunc.GL_MODELVIEW);
-                pmv.glPushMatrix();
-                setTransform(pmv);
-                ok = winToObjCoord(null, winX2, glWinY, objPos2);
-                pmv.glPopMatrix();
-            }
+            final boolean ok = winToObjCoord(scene, winX2, glWinY, objPos2);
             final float dx = objPos2[0];
             final float dy = objPos2[1];
             final float sx = scale[0] + ( dx/box.getWidth() ); // bottom-right
             final float sy = scale[1] + ( dy/box.getHeight() );
             if( DEBUG ) {
-                System.err.printf("DragZoom: resize %b, obj %4d/%4d, %.3f/%.3f/%.3f %.3f/%.3f/%.3f + %.3f/%.3f -> %.3f/%.3f%n",
-                        inResize, glWinX, glWinY, objPos[0], objPos[1], objPos[2], position[0], position[1], position[2],
+                System.err.printf("DragZoom: resize %b, ok %b, obj %4d/%4d, %.3f/%.3f/%.3f %.3f/%.3f/%.3f + %.3f/%.3f -> %.3f/%.3f%n",
+                        inResize, ok, glWinX, glWinY, objPos[0], objPos[1], objPos[2], position[0], position[1], position[2],
                         dx, dy, sx, sy);
             }
-            if( resize_sxy_min <= sx && sx <= resize_sxy_max && resize_sxy_min <= sy && sy <= resize_sxy_max ) {
+            if( ok && resize_sxy_min <= sx && sx <= resize_sxy_max && resize_sxy_min <= sy && sy <= resize_sxy_max ) {
                 if( DEBUG ) {
                     System.err.printf("PinchZoom: pixels %f, obj %4d/%4d, %.3f/%.3f/%.3f %.3f/%.3f/%.3f + %.3f/%.3f -> %.3f/%.3f%n",
                             pixels, glWinX, glWinY, objPos[0], objPos[1], objPos[2], position[0], position[1], position[2],
diff --git a/src/graphui/classes/com/jogamp/graph/ui/gl/shapes/GLButton.java b/src/graphui/classes/com/jogamp/graph/ui/gl/shapes/GLButton.java
index 7fc2e6f7e..77ac99861 100644
--- a/src/graphui/classes/com/jogamp/graph/ui/gl/shapes/GLButton.java
+++ b/src/graphui/classes/com/jogamp/graph/ui/gl/shapes/GLButton.java
@@ -94,7 +94,7 @@ public class GLButton extends TexSeqButton {
     @Override
     public void drawShape(final GL2ES2 gl, final RegionRenderer renderer, final int[] sampleCount) {
         final int[/*2*/] surfaceSize = new int[2];
-        final boolean got_sz = getSurfaceSize(renderer, surfaceSize) && 0 < surfaceSize[0] && 0 < surfaceSize[1];
+        final boolean got_sz = getSurfaceSize(renderer.getMatrix(), renderer.getViewport(), surfaceSize) && 0 < surfaceSize[0] && 0 < surfaceSize[1];
 
         if( null == fboGLAD ) {
             final ImageSequence imgSeq = (ImageSequence)texSeq;
-- 
cgit v1.2.3