diff options
-rw-r--r-- | src/graphui/classes/com/jogamp/graph/ui/gl/Scene.java | 457 | ||||
-rw-r--r-- | src/graphui/classes/com/jogamp/graph/ui/gl/Shape.java | 124 |
2 files changed, 301 insertions, 280 deletions
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 9c9334182..7e98df635 100644 --- a/src/graphui/classes/com/jogamp/graph/ui/gl/Scene.java +++ b/src/graphui/classes/com/jogamp/graph/ui/gl/Scene.java @@ -27,6 +27,7 @@ */ package com.jogamp.graph.ui.gl; +import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; @@ -37,8 +38,10 @@ import com.jogamp.opengl.GL2ES2; import com.jogamp.opengl.GLAutoDrawable; import com.jogamp.opengl.GLCapabilitiesImmutable; import com.jogamp.opengl.GLEventListener; +import com.jogamp.opengl.GLException; import com.jogamp.opengl.GLRunnable; import com.jogamp.opengl.fixedfunc.GLMatrixFunc; +import com.jogamp.common.nio.Buffers; import com.jogamp.graph.curve.Region; import com.jogamp.graph.curve.opengl.RegionRenderer; import com.jogamp.graph.curve.opengl.RenderState; @@ -75,6 +78,9 @@ import com.jogamp.opengl.util.PMVMatrix; * - {@link GLEventListener#display(GLAutoDrawable)} * - {@link GLEventListener#dispose(GLAutoDrawable)} * </p> + * <p> + * {@link #setPMVMatrixSetup(PMVMatrixSetup)} maybe used to provide a custom {@link PMVMatrix} setup. + * </p> * @see Shape */ public final class Scene implements GLEventListener { @@ -87,12 +93,11 @@ public final class Scene implements GLEventListener { /** Default projection z-far value is 7000. */ public static final float DEFAULT_ZFAR = 7000.0f; + @SuppressWarnings("unused") private static final boolean DEBUG = false; private final ArrayList<Shape> shapes = new ArrayList<Shape>(); - private final float sceneDist, zNear, zFar; - private final float projAngle = DEFAULT_ANGLE; private float[] clearColor = null; private int clearMask; @@ -100,10 +105,8 @@ public final class Scene implements GLEventListener { private final int[] sampleCount = new int[1]; - /** Describing the bounding box in shape's object model-coordinates of the near-plane parallel at its scene-distance, pre {@link #translate(PMVMatrix)} to origin. */ - private final AABBox planeBoxCtr = new AABBox(0f, 0f, 0f, 0f, 0f, 0f); /** Describing the bounding box in shape's object model-coordinates of the near-plane parallel at its scene-distance, post {@link #translate(PMVMatrix)} */ - private final AABBox planBoxBL = new AABBox(0f, 0f, 0f, 0f, 0f, 0f); + private final AABBox planeBox = new AABBox(0f, 0f, 0f, 0f, 0f, 0f); private volatile Shape activeShape = null; @@ -119,60 +122,24 @@ public final class Scene implements GLEventListener { /** * Create a new scene with an internally created RegionRenderer - * and using default values {@link #DEFAULT_SCENE_DIST}, {@link #DEFAULT_ZNEAR} and {@link #DEFAULT_ZFAR}. + * and using default values {@link #DEFAULT_SCENE_DIST}, {@link #DEFAULT_ANGLE}, {@link #DEFAULT_ZNEAR} and {@link #DEFAULT_ZFAR}. */ public Scene() { - this(createRenderer(), DEFAULT_SCENE_DIST, DEFAULT_ZNEAR, DEFAULT_ZFAR); - } - - /** - * Create a new scene with given projection values and an internally created RegionRenderer. - * @param sceneDist scene distance on z-axis to projection, consider using {@link #DEFAULT_SCENE_DIST}. - * @param zNear projection z-near value, consider using {@link #DEFAULT_ZNEAR} - * @param zFar projection z-far value, consider using {@link #DEFAULT_ZFAR} - */ - public Scene(final float sceneDist, final float zNear, final float zFar) { - this(createRenderer(), sceneDist, zNear, zFar); + this(createRenderer()); } /** * Create a new scene taking ownership of the given RegionRenderer - * and using default values {@link #DEFAULT_SCENE_DIST}, {@link #DEFAULT_ZNEAR} and {@link #DEFAULT_ZFAR}. + * and using default values {@link #DEFAULT_SCENE_DIST}, {@link #DEFAULT_ANGLE}, {@link #DEFAULT_ZNEAR} and {@link #DEFAULT_ZFAR}. */ public Scene(final RegionRenderer renderer) { - this(renderer, DEFAULT_SCENE_DIST, DEFAULT_ZNEAR, DEFAULT_ZFAR); - } - - /** - * Create a new scene with given projection values and taking ownership of the given RegionRenderer. - * @param renderer RegionRenderer to use and own - * @param sceneDist scene distance on z-axis to projection, consider using {@link #DEFAULT_SCENE_DIST}. - * @param zNear projection z-near value, consider using {@link #DEFAULT_ZNEAR} - * @param zFar projection z-far value, consider using {@link #DEFAULT_ZFAR} - */ - public Scene(final RegionRenderer renderer, final float sceneDist, final float zNear, final float zFar) { if( null == renderer ) { throw new IllegalArgumentException("Null RegionRenderer"); } this.renderer = renderer; - this.sceneDist = sceneDist; - this.zFar = zFar; - this.zNear = zNear; this.sampleCount[0] = 4; } - /** Return z-axis distance of scene to projection, see {@link #DEFAULT_SCENE_DIST}. */ - public float getProjSceneDist() { return sceneDist; } - - /** Return projection angle in degrees, see {@link #DEFAULT_ANGLE}. */ - public float getProjAngle() { return projAngle; } - - /** Return projection z-near value, see {@link #DEFAULT_ZNEAR}. */ - public float getProjZNear() { return zNear; } - - /** Return projection z-far value, see {@link #DEFAULT_ZFAR}. */ - public float getProjZFar() { return zFar; } - /** Returns the associated RegionRenderer */ public RegionRenderer getRenderer() { return renderer; } @@ -352,8 +319,24 @@ public final class Scene implements GLEventListener { } else { renderer.enable(gl, false); } + synchronized ( syncDisplayedOnce ) { + displayedOnce = true; + syncDisplayedOnce.notifyAll(); + } + } + + private volatile boolean displayedOnce = false; + private final Object syncDisplayedOnce = new Object(); - renderer.enable(gl, false); + /** Blocks until first {@link #display(GLAutoDrawable)} has completed after construction or {@link #dispose(GLAutoDrawable). */ + public void waitUntilDisplayed() { + synchronized( syncDisplayedOnce ) { + while( !displayedOnce ) { + try { + syncDisplayedOnce.wait(); + } catch (final InterruptedException e) { } + } + } } /** @@ -367,18 +350,21 @@ public final class Scene implements GLEventListener { * </p> * @param glWinX window X coordinate, bottom-left origin * @param glWinY window Y coordinate, bottom-left origin + * @param pmv a new {@link PMVMatrix} which will {@link Scene.PMVMatrixSetup#set(PMVMatrix, int, int, int, int) be setup}, + * {@link Shape#setTransform(PMVMatrix) shape-transformed} and can be reused by the caller and runnable. * @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) { - shape[0] = pickShapeImpl(glWinX, glWinY, objPos); + public Shape pickShape(final int glWinX, final int glWinY, final PMVMatrix pmv, final float[] objPos, final Shape[] shape, final Runnable runnable) { + shape[0] = pickShapeImpl(glWinX, glWinY, pmv, objPos); if( null != shape[0] ) { runnable.run(); } + return shape[0]; } @SuppressWarnings({ "unchecked", "rawtypes" }) - private Shape pickShapeImpl(final int glWinX, final int glWinY, final float[] objPos) { + private Shape pickShapeImpl(final int glWinX, final int glWinY, final PMVMatrix pmv, final float[] objPos) { final float winZ0 = 0f; final float winZ1 = 0.3f; /** @@ -386,7 +372,6 @@ public final 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 = new PMVMatrix(); setupMatrix(pmv); final Ray ray = new Ray(); @@ -401,7 +386,6 @@ public final class Scene implements GLEventListener { pmv.glPushMatrix(); uiShape.setTransform(pmv); final boolean ok = pmv.gluUnProjectRay(glWinX, glWinY, winZ0, winZ1, getViewport(), 0, ray); - pmv.glPopMatrix(); if( ok ) { final AABBox sbox = uiShape.getBounds(); if( sbox.intersectsRay(ray) ) { @@ -413,6 +397,7 @@ public final class Scene implements GLEventListener { return uiShape; } } + pmv.glPopMatrix(); // we leave the stack open if picked above, allowing the modelview shape transform to be reused } } return null; @@ -501,11 +486,13 @@ public final class Scene implements GLEventListener { * @param shape * @param glWinX in GL window coordinates, origin bottom-left * @param glWinY in GL window coordinates, origin bottom-left + * @param pmv a new {@link PMVMatrix} which will {@link Scene.PMVMatrixSetup#set(PMVMatrix, int, int, int, int) be setup}, + * {@link Shape#setTransform(PMVMatrix) shape-transformed} and can be reused by the caller and runnable. * @param objPos resulting object position * @param runnable action */ - public void winToObjCoord(final Shape shape, final int glWinX, final int glWinY, final float[] objPos, final Runnable runnable) { - if( null != shape && shape.winToObjCoord(this, glWinX, glWinY, objPos) ) { + public void winToObjCoord(final Shape shape, final int glWinX, final int glWinY, final PMVMatrix pmv, final float[] objPos, final Runnable runnable) { + if( null != shape && shape.winToObjCoord(pmvMatrixSetup, renderer.getViewport(), glWinX, glWinY, pmv, objPos) ) { runnable.run(); } } @@ -523,6 +510,10 @@ public final class Scene implements GLEventListener { @Override public void dispose(final GLAutoDrawable drawable) { System.err.println("SceneUIController: dispose"); + synchronized ( syncDisplayedOnce ) { + displayedOnce = false; + syncDisplayedOnce.notifyAll(); + } if( drawable instanceof GLWindow ) { final GLWindow glw = (GLWindow) drawable; detachInputListenerFrom(glw); @@ -537,119 +528,102 @@ public final class Scene implements GLEventListener { } /** - * - * @param pmv - * @param view - * @param zNear - * @param zFar - * @param orthoX - * @param orthoY - * @param orthoDist - * @param winZ - * @param objPos float[3] storage for object coord result + * Interface providing {@link #set(PMVMatrix, int, int, int, int) a method} to + * setup {@link PMVMatrix}'s {@link GLMatrixFunc#GL_PROJECTION} and {@link GLMatrixFunc#GL_MODELVIEW}. + * <p> + * At the end of operations, the {@link GLMatrixFunc#GL_MODELVIEW} matrix has to be selected. + * </p> + * <p> + * Implementation is being called by {@link Scene#setupMatrix(PMVMatrix, int, int, int, int)} + * and hence {@link Scene#reshape(GLAutoDrawable, int, int, int, int)}. + * </p> + * <p> + * Custom implementations can be set via {@link Scene#setPMVMatrixSetup(PMVMatrixSetup)}. + * </p> + * <p> + * The default implementation is described below + * </p> + * <p> + * {@link GLMatrixFunc#GL_PROJECTION} is setup using perspective {@link Scene#DEFAULT_ANGLE} with {@link Scene#DEFAULT_ZNEAR} and {@link Scene#DEFAULT_ZFAR}. + * </p> + * <p> + * Further {@link GLMatrixFunc#GL_MODELVIEW} is translated to given {@link Scene#DEFAULT_SCENE_DIST}. + * </p> */ - public static void winToObjCoord(final PMVMatrix pmv, final int[] view, - final float zNear, final float zFar, - final float orthoX, final float orthoY, final float orthoDist, - final float[] winZ, final float[] objPos) { - winZ[0] = FloatUtil.getOrthoWinZ(orthoDist, zNear, zFar); - pmv.gluUnProject(orthoX, orthoY, winZ[0], view, 0, objPos, 0); + public static interface PMVMatrixSetup { + /** + * Setup {@link PMVMatrix}'s {@link GLMatrixFunc#GL_PROJECTION} and {@link GLMatrixFunc#GL_MODELVIEW}. + * <p> + * See {@link PMVMatrixSetup} for details. + * </p> + * <p> + * At the end of operations, the {@link GLMatrixFunc#GL_MODELVIEW} matrix is selected. + * </p> + * @param pmv the {@link PMVMatrix} to setup + * @param x lower left corner of the viewport rectangle + * @param y lower left corner of the viewport rectangle + * @param width width of the viewport rectangle + * @param height height of the viewport rectangle + */ + void set(PMVMatrix pmv, final int x, final int y, final int width, final int height); + + /** + * Optional method to set the {@link Scene#getBounds()} {@link AABBox}, maybe a {@code nop} if not desired. + * <p> + * Will be called by {@link Scene#reshape(GLAutoDrawable, int, int, int, int)} after {@link #set(PMVMatrix, int, int, int, int)}. + * </p> + * @param x TODO + * @param y TODO + */ + void setPlaneBox(final AABBox planeBox, final PMVMatrix pmv, int x, int y, final int width, final int height); } - /** - * Map given window surface-size to object coordinates relative to this scene and {@link #getProjSceneDist() and projection settings. - * @param width surface width in pixel - * @param height surface height in pixel - * @param objSceneSize float[2] storage for object surface size result - */ - public void surfaceToObjSize(final int width, final int height, final float[/*2*/] objSceneSize) { - final int[] viewport = { 0, 0, width, height }; + /** Return the default or {@link #setPMVMatrixSetup(PMVMatrixSetup)} {@link PMVMatrixSetup}. */ + public final PMVMatrixSetup getPMVMatrixSetup() { return pmvMatrixSetup; } - final PMVMatrix pmv = new PMVMatrix(); - setupMatrix(pmv, width, height); - { - final float orthoDist = -sceneDist; - final float[] obj00Coord = new float[3]; - final float[] obj11Coord = new float[3]; - final float[] winZ = new float[1]; - - winToObjCoord(pmv, viewport, zNear, zFar, 0f, 0f, orthoDist, winZ, obj00Coord); - winToObjCoord(pmv, viewport, zNear, zFar, width, height, orthoDist, winZ, obj11Coord); - objSceneSize[0] = obj11Coord[0] - obj00Coord[0]; - objSceneSize[1] = obj11Coord[1] - obj00Coord[1]; - } - } + /** Set a custom {@link PMVMatrixSetup}. */ + public final void setPMVMatrixSetup(final PMVMatrixSetup setup) { pmvMatrixSetup = setup; } /** - * Reshape scene {@link #setupMatrix(PMVMatrix, int, int)}. - * <p> - * {@link GLMatrixFunc#GL_PROJECTION} is setup using perspective {@link #getProjAngle()} with {@link #getProjZNear()} and {@link #getProjZFar()}. - * </p> + * Reshape scene using {@link #setupMatrix(PMVMatrix, int, int, int, int)} using {@link PMVMatrixSetup}. * <p> - * {@link GLMatrixFunc#GL_MODELVIEW} is translated to given {@link #getProjSceneDist()} - * and and origin 0/0 becomes the bottom-left corner. + * {@inheritDoc} * </p> - * @see #setupMatrix(PMVMatrix, int, int) + * @see PMVMatrixSetup + * @see #setPMVMatrixSetup(PMVMatrixSetup) + * @see #setupMatrix(PMVMatrix, int, int, int, int) * @see #getBounds() * @see #getBoundsCenter() */ - @SuppressWarnings("unused") @Override public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) { renderer.reshapeNotify(x, y, width, height); - final PMVMatrix pmv = renderer.getMatrix(); - setupMatrix0(pmv, width, height); - pmv.glTranslatef(0f, 0f, sceneDist); - { - final float orthoDist = -sceneDist; - final float[] obj00Coord = new float[3]; - final float[] obj11Coord = new float[3]; - final float[] winZ = new float[1]; - - winToObjCoord(pmv, getViewport(), zNear, zFar, 0f, 0f, orthoDist, winZ, obj00Coord); - winToObjCoord(pmv, getViewport(), zNear, zFar, width, height, orthoDist, winZ, obj11Coord); - - pmv.glTranslatef(obj00Coord[0], obj00Coord[1], 0f); // bottom-left corder origin 0/0 - - planeBoxCtr.setSize( obj00Coord[0], // lx - obj00Coord[1], // ly - obj00Coord[2], // lz - obj11Coord[0], // hx - obj11Coord[1], // hy - obj11Coord[2] );// hz - - planBoxBL.setSize( 0, // lx - 0, // ly - 0, // lz - planeBoxCtr.getWidth(), // hx - planeBoxCtr.getHeight(),// hy - planeBoxCtr.getDepth());// hz - - if( true || DEBUG ) { - System.err.printf("Reshape: zNear %f, zFar %f, sceneDist %f%n", zNear, zFar, sceneDist); - System.err.printf("Reshape: Frustum: %s%n", pmv.glGetFrustum()); - System.err.printf("Reshape: mapped.00: [%f, %f, %f], winZ %f -> [%f, %f, %f]%n", 0f, 0f, orthoDist, winZ[0], obj00Coord[0], obj00Coord[1], obj00Coord[2]); - System.err.printf("Reshape: mapped.11: [%f, %f, %f], winZ %f -> [%f, %f, %f]%n", (float)width, (float)height, orthoDist, winZ[0], obj11Coord[0], obj11Coord[1], obj11Coord[2]); - System.err.printf("Reshape: scenePlaneBox: %s%n", planeBoxCtr); - } - } + setupMatrix(renderer.getMatrix(), x, y, width, height); + pmvMatrixSetup.setPlaneBox(planeBox, renderer.getMatrix(), x, y, width, height); + } - if( false ) { - final float[] sceneScale = new float[3]; - final float[] scenePlaneOrigin = new float[3]; - scenePlaneOrigin[0] = planeBoxCtr.getMinX() * sceneDist; - scenePlaneOrigin[1] = planeBoxCtr.getMinY() * sceneDist; - scenePlaneOrigin[2] = planeBoxCtr.getMinZ() * sceneDist; - sceneScale[0] = ( planeBoxCtr.getWidth() * sceneDist ) / width; - sceneScale[1] = ( planeBoxCtr.getHeight() * sceneDist ) / height; - sceneScale[2] = 1f; - System.err.printf("Scene Origin [%f, %f, %f]%n", scenePlaneOrigin[0], scenePlaneOrigin[1], scenePlaneOrigin[2]); - System.err.printf("Scene Scale %f * [%f x %f] / [%d x %d] = [%f, %f, %f]%n", - sceneDist, planeBoxCtr.getWidth(), planeBoxCtr.getHeight(), - width, height, - sceneScale[0], sceneScale[1], sceneScale[2]); - } + /** + * Setup {@link PMVMatrix} {@link GLMatrixFunc#GL_PROJECTION} and {@link GLMatrixFunc#GL_MODELVIEW} + * by calling {@link #getPMVMatrixSetup()}'s {@link PMVMatrixSetup#set(PMVMatrix, int, int, int, int)}. + * @param pmv the {@link PMVMatrix} to setup + * @param x lower left corner of the viewport rectangle + * @param y lower left corner of the viewport rectangle + * @param width width of the viewport rectangle + * @param height height of the viewport rectangle + */ + public void setupMatrix(final PMVMatrix pmv, final int x, final int y, final int width, final int height) { + pmvMatrixSetup.set(pmv, x, y, width, height); + } + + /** + * Setup {@link PMVMatrix} {@link GLMatrixFunc#GL_PROJECTION} and {@link GLMatrixFunc#GL_MODELVIEW} + * using implicit {@link #getViewport()} surface dimension by calling {@link #getPMVMatrixSetup()}'s {@link PMVMatrixSetup#set(PMVMatrix, int, int, int, int)}. + * @param pmv the {@link PMVMatrix} to setup + */ + public void setupMatrix(final PMVMatrix pmv) { + final int[] viewport = renderer.getViewport(); + setupMatrix(pmv, viewport[0], viewport[1], viewport[2], viewport[3]); } /** Copies the current int[4] viewport in given target and returns it for chaining. It is set after initial {@link #reshape(GLAutoDrawable, int, int, int, int)}. */ @@ -666,94 +640,79 @@ public final class Scene implements GLEventListener { /** 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. */ - private void translate(final PMVMatrix pmv) { - pmv.glTranslatef(planeBoxCtr.getMinX(), planeBoxCtr.getMinY(), sceneDist); - } - /** - * 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. + * Describing the scene's object model-dimensions of the plane at scene-distance covering the visible viewport rectangle. * <p> - * {@link GLMatrixFunc#GL_PROJECTION} is setup using perspective {@link #getProjAngle()} with {@link #getProjZNear()} and {@link #getProjZFar()}. + * The value is evaluated at {@link #reshape(GLAutoDrawable, int, int, int, int)} via {@link } * </p> * <p> - * {@link GLMatrixFunc#GL_MODELVIEW} is translated to given {@link #getProjSceneDist()} - * and and origin 0/0 becomes the bottom-left corner. + * {@link AABBox#getWidth()} and {@link AABBox#getHeight()} define scene's dimension covered by surface size. * </p> * <p> - * At the end of operations, the {@link GLMatrixFunc#GL_MODELVIEW} matrix is selected. + * {@link AABBox} is setup via {@link #getPMVMatrixSetup()}'s {@link PMVMatrixSetup#setPlaneBox(AABBox, PMVMatrix, int, int, int, int)}. * </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(); - pmv.gluPerspective(projAngle, ratio, zNear, zFar); - - pmv.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); - pmv.glLoadIdentity(); - } + public AABBox getBounds() { return planeBox; } /** - * 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> - * {@link GLMatrixFunc#GL_PROJECTION} is setup using perspective {@link #getProjAngle()} with {@link #getProjZNear()} and {@link #getProjZFar()}. - * </p> - * <p> - * {@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 + * + * @param pmv + * @param viewport + * @param zNear + * @param zFar + * @param winX + * @param winY + * @param objOrthoZ + * @param objPos float[3] storage for object coord result + * @param winZ */ - public void setupMatrix(final PMVMatrix pmv) { - setupMatrix(pmv, getWidth(), getHeight()); + public static void winToPlaneCoord(final PMVMatrix pmv, final int[] viewport, + final float zNear, final float zFar, + final float winX, final float winY, final float objOrthoZ, + final float[] objPos) { + final float winZ = FloatUtil.getOrthoWinZ(objOrthoZ, zNear, zFar); + pmv.gluUnProject(winX, winY, winZ, viewport, 0, objPos, 0); } /** - * Describing the scene's object model-dimensions of the near-plane parallel at its scene-distance {@link #getProjSceneDist()} - * having the origin 0/0 on the bottom-left corner. - * <p> - * The value is evaluated at {@link #reshape(GLAutoDrawable, int, int, int, int)}. - * </p> - * <p> - * {@link AABBox#getWidth()} and {@link AABBox#getHeight()} define scene's dimension covered by surface size. - * </p> + * Map given window surface-size to object coordinates relative to this scene using + * the give projection parameters. + * @param viewport + * @param zNear + * @param zFar + * @param objOrthoDist + * @param objSceneSize float[2] storage for object surface size result */ - public AABBox getBounds() { return planBoxBL; } + public void surfaceToPlaneSize(final int[] viewport, final float zNear, final float zFar, final float objOrthoDist, final float[/*2*/] objSceneSize) { + final PMVMatrix pmv = new PMVMatrix(); + setupMatrix(pmv, viewport[0], viewport[1], viewport[2], viewport[3]); + { + final float[] obj00Coord = new float[3]; + final float[] obj11Coord = new float[3]; + + winToPlaneCoord(pmv, viewport, DEFAULT_ZNEAR, DEFAULT_ZFAR, viewport[0], viewport[1], objOrthoDist, obj00Coord); + winToPlaneCoord(pmv, viewport, DEFAULT_ZNEAR, DEFAULT_ZFAR, viewport[2], viewport[3], objOrthoDist, obj11Coord); + objSceneSize[0] = obj11Coord[0] - obj00Coord[0]; + objSceneSize[1] = obj11Coord[1] - obj00Coord[1]; + } + } /** - * Describing the scene's object model-dimensions of the near-plane parallel at its scene-distance {@link #getProjSceneDist()} - * having the origin 0/0 in the center of the screen. - * <p> - * The value is evaluated at {@link #reshape(GLAutoDrawable, int, int, int, int)} before translating to the bottom-left origin 0/0, - * i.e. its minimum values are negative of half dimension. - * </p> - * <p> - * {@link AABBox#getWidth()} and {@link AABBox#getHeight()} define scene's dimension covered by surface size. - * </p> + * Map given window surface-size to object coordinates relative to this scene using + * the default {@link PMVMatrixSetup}. + * @param viewport + * @param objSceneSize float[2] storage for object surface size result */ - public AABBox getBoundsCenter() { return planeBoxCtr; } + public void surfaceToPlaneSize(final int[] viewport, final float[/*2*/] objSceneSize) { + surfaceToPlaneSize(viewport, DEFAULT_ZNEAR, DEFAULT_ZFAR, -DEFAULT_SCENE_DIST, objSceneSize); + } public final Shape getActiveShape() { return activeShape; } - public void release() { - setActiveShape(null); + public void releaseActiveShape() { + activeShape = null; } private void setActiveShape(final Shape shape) { activeShape = shape; @@ -770,10 +729,11 @@ public final class Scene implements GLEventListener { // flip to GL window coordinates final int glWinX = e.getX(); final int glWinY = getHeight() - e.getY() - 1; + final PMVMatrix pmv = new PMVMatrix(); final float[] objPos = new float[3]; final Shape shape = activeShape; - winToObjCoord(shape, glWinX, glWinY, objPos, () -> { - shape.dispatchGestureEvent(Scene.this, gh, glWinX, glWinY, objPos); + winToObjCoord(shape, glWinX, glWinY, pmv, objPos, () -> { + shape.dispatchGestureEvent(gh, glWinX, glWinY, pmv, renderer.getViewport(), objPos); }); } } @@ -788,7 +748,7 @@ public final class Scene implements GLEventListener { */ final void dispatchMouseEvent(final MouseEvent e, final int glWinX, final int glWinY) { if( null == activeShape ) { - dispatchMouseEventPickShape(e, glWinX, glWinY, true); + dispatchMouseEventPickShape(e, glWinX, glWinY); } else { dispatchMouseEventForShape(activeShape, e, glWinX, glWinY); } @@ -798,17 +758,18 @@ public final class Scene implements GLEventListener { * @param e original Newt {@link MouseEvent} * @param glWinX in GL window coordinates, origin bottom-left * @param glWinY in GL window coordinates, origin bottom-left - * @param setActive */ - final void dispatchMouseEventPickShape(final MouseEvent e, final int glWinX, final int glWinY, final boolean setActive) { + final void dispatchMouseEventPickShape(final MouseEvent e, final int glWinX, final int glWinY) { + final PMVMatrix pmv = new PMVMatrix(); final float[] objPos = new float[3]; final Shape[] shape = { null }; - pickShape(glWinX, glWinY, objPos, shape, () -> { - if( setActive ) { - setActiveShape(shape[0]); - } + if( null == pickShape(glWinX, glWinY, pmv, objPos, shape, () -> { + setActiveShape(shape[0]); shape[0].dispatchMouseEvent(e, glWinX, glWinY, objPos); - } ); + } ) ) + { + releaseActiveShape(); + } } /** * Dispatch event to shape @@ -818,8 +779,9 @@ public final class Scene implements GLEventListener { * @param glWinY in GL window coordinates, origin bottom-left */ final void dispatchMouseEventForShape(final Shape shape, final MouseEvent e, final int glWinX, final int glWinY) { + final PMVMatrix pmv = new PMVMatrix(); final float[] objPos = new float[3]; - winToObjCoord(shape, glWinX, glWinY, objPos, () -> { shape.dispatchMouseEvent(e, glWinX, glWinY, objPos); }); + winToObjCoord(shape, glWinX, glWinY, pmv, objPos, () -> { shape.dispatchMouseEvent(e, glWinX, glWinY, objPos); }); } private class SBCMouseListener implements MouseListener { @@ -850,7 +812,7 @@ public final class Scene implements GLEventListener { dispatchMouseEvent(e, glWinX, glWinY); if( 1 == e.getPointerCount() ) { // Release active shape: last pointer has been lifted! - release(); + releaseActiveShape(); clear(); } } @@ -861,9 +823,9 @@ public final class Scene implements GLEventListener { final int glWinX = e.getX(); final int glWinY = getHeight() - e.getY() - 1; // activeId should have been released by mouseRelease() already! - dispatchMouseEventPickShape(e, glWinX, glWinY, false); + dispatchMouseEventPickShape(e, glWinX, glWinY); // Release active shape: last pointer has been lifted! - release(); + releaseActiveShape(); clear(); } @@ -887,7 +849,7 @@ public final class Scene implements GLEventListener { // flip to GL window coordinates final int glWinX = lx; final int glWinY = getHeight() - ly - 1; - dispatchMouseEventPickShape(e, glWinX, glWinY, true); + dispatchMouseEvent(e, glWinX, glWinY); } @Override @@ -897,12 +859,16 @@ public final class Scene implements GLEventListener { ly = e.getY(); lId = e.getPointerId(0); } + final int glWinX = lx; + final int glWinY = getHeight() - ly - 1; + // dispatchMouseEvent(e, glWinX, glWinY); + dispatchMouseEventPickShape(e, glWinX, glWinY); } @Override public void mouseEntered(final MouseEvent e) { } @Override public void mouseExited(final MouseEvent e) { - release(); + releaseActiveShape(); clear(); } } @@ -948,4 +914,35 @@ public final class Scene implements GLEventListener { return String.format("%03.1f/%03.1f fps, %.1f ms/f", lfps, tfps, td); } + private final PMVMatrixSetup defaultPMVMatrixSetup = new PMVMatrixSetup() { + @Override + public void set(final PMVMatrix pmv, final int x, final int y, final int width, final int height) { + final float ratio = (float)width/(float)height; + pmv.glMatrixMode(GLMatrixFunc.GL_PROJECTION); + pmv.glLoadIdentity(); + pmv.gluPerspective(DEFAULT_ANGLE, ratio, DEFAULT_ZNEAR, DEFAULT_ZFAR); + pmv.glTranslatef(0f, 0f, DEFAULT_SCENE_DIST); + + pmv.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); + pmv.glLoadIdentity(); + } + + @Override + public void setPlaneBox(final AABBox planeBox, final PMVMatrix pmv, final int x, final int y, final int width, final int height) { + final float orthoDist = -DEFAULT_SCENE_DIST; + final float[] obj00Coord = new float[3]; + final float[] obj11Coord = new float[3]; + + winToPlaneCoord(pmv, getViewport(), DEFAULT_ZNEAR, DEFAULT_ZFAR, x, y, orthoDist, obj00Coord); + winToPlaneCoord(pmv, getViewport(), DEFAULT_ZNEAR, DEFAULT_ZFAR, width, height, orthoDist, obj11Coord); + + planeBox.setSize( obj00Coord[0], // lx + obj00Coord[1], // ly + obj00Coord[2], // lz + obj11Coord[0], // hx + obj11Coord[1], // hy + obj11Coord[2] );// hz } + } + }; + private PMVMatrixSetup pmvMatrixSetup = defaultPMVMatrixSetup; } 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 140ac8cc8..56ba928d7 100644 --- a/src/graphui/classes/com/jogamp/graph/ui/gl/Shape.java +++ b/src/graphui/classes/com/jogamp/graph/ui/gl/Shape.java @@ -37,7 +37,6 @@ import com.jogamp.graph.curve.OutlineShape; import com.jogamp.graph.curve.Region; import com.jogamp.graph.curve.opengl.GLRegion; import com.jogamp.graph.curve.opengl.RegionRenderer; -import com.jogamp.graph.font.Font; import com.jogamp.graph.geom.Vertex; import com.jogamp.graph.geom.Vertex.Factory; import com.jogamp.graph.geom.plane.AffineTransform; @@ -47,7 +46,6 @@ import com.jogamp.newt.event.MouseAdapter; import com.jogamp.newt.event.NEWTEvent; import com.jogamp.newt.event.PinchToZoomGesture; import com.jogamp.newt.event.MouseEvent; -import com.jogamp.newt.event.MouseEvent.PointerClass; import com.jogamp.newt.event.MouseListener; import com.jogamp.opengl.math.FloatUtil; import com.jogamp.opengl.math.Quaternion; @@ -77,7 +75,7 @@ public abstract class Shape { protected static final int DIRTY_SHAPE = 1 << 0 ; protected static final int DIRTY_STATE = 1 << 1 ; - private final Factory<? extends Vertex> vertexFactory; + protected final Factory<? extends Vertex> vertexFactory; private final int renderModes; protected final AABBox box; @@ -176,13 +174,13 @@ public abstract class Shape { markShapeDirty(); } - public void setPosition(final float tx, final float ty, final float tz) { + public final void moveTo(final float tx, final float ty, final float tz) { position[0] = tx; position[1] = ty; position[2] = tz; // System.err.println("UIShape.setTranslate: "+tx+"/"+ty+"/"+tz+": "+toString()); } - public void move(final float tx, final float ty, final float tz) { + public final void move(final float tx, final float ty, final float tz) { position[0] += tx; position[1] += ty; position[2] += tz; @@ -249,10 +247,27 @@ public abstract class Shape { */ public final AABBox getBounds() { return box; } + /** + * Returns the unscaled bounding {@link AABBox} for this shape. + * + * This variant differs from {@link #getBounds()} as it + * returns a valid {@link AABBox} even before {@link #draw(GL2ES2, RegionRenderer, int[]) draw(..)} + * and having an OpenGL instance available. + * + * @see #getBounds() + */ + public final AABBox getBounds(final GLProfile glp) { + if( null == region ) { + // initial creation of region, producing a valid box + validateImpl(glp, null); + } + return box; + } + public final int getRenderModes() { return renderModes; } - public GLRegion getRegion(final GL2ES2 gl, final RegionRenderer renderer) { - validate(gl, renderer); + public GLRegion getRegion(final GL2ES2 gl) { + validate(gl); return region; } @@ -270,7 +285,7 @@ public abstract class Shape { * @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) { + public void draw(final GL2ES2 gl, final RegionRenderer renderer, final int[] sampleCount) { final float r, g, b, a; final boolean isPressed = isPressed(), isToggleOn = isToggleOn(); final boolean modBaseColor = !Region.hasColorChannel( renderModes ) && !Region.hasColorTexture( renderModes ); @@ -324,7 +339,7 @@ public abstract class Shape { } } renderer.getRenderState().setColorStatic(r, g, b, a); - getRegion(gl, renderer).draw(gl, renderer, sampleCount); + getRegion(gl).draw(gl, renderer, sampleCount); } protected GLRegion createGLRegion(final GLProfile glp) { @@ -335,24 +350,21 @@ public abstract class Shape { * Validates the shape's underlying {@link GLRegion}. * * @param gl - * @param renderer */ - public final void validate(final GL2ES2 gl, final RegionRenderer renderer) { + public final void validate(final GL2ES2 gl) { + validateImpl(gl.getGLProfile(), gl); + } + private final void validateImpl(final GLProfile glp, final GL2ES2 gl) { if( isShapeDirty() || null == region ) { box.reset(); if( null == region ) { - region = createGLRegion(gl.getGLProfile()); - } else { - region.clear(gl); - } - addShapeToRegion(gl, renderer); - if( DRAW_DEBUG_BOX ) { + region = createGLRegion(glp); + } else if( null != gl ) { region.clear(gl); - final OutlineShape shape = new OutlineShape(renderer.getRenderState().getVertexFactory()); - shape.setSharpness(shapesSharpness); - shape.setIsQuadraticNurbs(); - region.addOutlineShape(shape, null, rgbaColor); + } else { + throw new IllegalArgumentException("GL is null on non-initial validation"); } + addShapeToRegion(); region.setQuality(regionQuality); dirty &= ~(DIRTY_SHAPE|DIRTY_STATE); } else if( isStateDirty() ) { @@ -402,7 +414,7 @@ public abstract class Shape { * @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[]) + * @see #getSurfaceSize(com.jogamp.graph.ui.gl.Scene.PMVMatrixSetup, int[], PMVMatrix, int[]) */ public boolean getSurfaceSize(final PMVMatrix pmv, final int[/*4*/] viewport, final int[/*2*/] surfaceSize) { boolean res = false; @@ -426,22 +438,26 @@ public abstract class Shape { } /** - * Retrieve window surface size of this shape using a local {@link PMVMatrix}. + * Retrieve surface (view) size of this shape. * <p> - * The {@link Scene} has be {@link Scene#reshape(com.jogamp.opengl.GLAutoDrawable, int, int, int, int) reshape(..)}ed once. + * The given {@link PMVMatrix} will be {@link Scene.PMVMatrixSetup#set(PMVMatrix, int, int, int, int) setup} properly for this shape + * including this shape's {@link #setTransform(PMVMatrix)}. * </p> - * @param scene {@link Scene} source of viewport and local {@link PMVMatrix} {@link Scene#setupMatrix(PMVMatrix) setupMatrix(..)}. + * @param pmvMatrixSetup {@link Scene.PMVMatrixSetup} to {@link Scene.PMVMatrixSetup#set(PMVMatrix, int, int, int, int) setup} given {@link PMVMatrix} {@code pmv}. + * @param viewport used viewport for {@link PMVMatrix#gluProject(float, float, float, int[], int, float[], int)} + * @param pmv a new {@link PMVMatrix} which will {@link Scene.PMVMatrixSetup#set(PMVMatrix, int, int, int, int) be setup}, + * {@link #setTransform(PMVMatrix) shape-transformed} and can be reused by the caller. * @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); + public boolean getSurfaceSize(final Scene.PMVMatrixSetup pmvMatrixSetup, final int[/*4*/] viewport, final PMVMatrix pmv, final int[/*2*/] surfaceSize) { + pmvMatrixSetup.set(pmv, viewport[0], viewport[1], viewport[2], viewport[3]); setTransform(pmv); - return getSurfaceSize(pmv, scene.getViewport(), surfaceSize); + return getSurfaceSize(pmv, viewport, surfaceSize); } + /** * Map given object coordinate relative to this shape to window coordinates. * <p> @@ -454,7 +470,7 @@ public abstract class Shape { * @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[]) + * @see #objToWinCoord(com.jogamp.graph.ui.gl.Scene.PMVMatrixSetup, int[], float[], PMVMatrix, int[]) */ public boolean objToWinCoord(final PMVMatrix pmv, final int[/*4*/] viewport, final float[/*3*/] objPos, final int[/*2*/] glWinPos) { boolean res = false; @@ -474,19 +490,22 @@ public abstract class Shape { /** * Map given object coordinate relative to this shape to window coordinates. * <p> - * The {@link Scene} has be {@link Scene#reshape(com.jogamp.opengl.GLAutoDrawable, int, int, int, int) reshape(..)}ed once. + * The given {@link PMVMatrix} will be {@link Scene.PMVMatrixSetup#set(PMVMatrix, int, int, int, int) setup} properly for this shape + * including this shape's {@link #setTransform(PMVMatrix)}. * </p> - * @param scene {@link Scene} source of viewport and local {@link PMVMatrix} {@link Scene#setupMatrix(PMVMatrix) setupMatrix(..)}. + * @param pmvMatrixSetup {@link Scene.PMVMatrixSetup} to {@link Scene.PMVMatrixSetup#set(PMVMatrix, int, int, int, int) setup} given {@link PMVMatrix} {@code pmv}. + * @param viewport used viewport for {@link PMVMatrix#gluProject(float, float, float, int[], int, float[], int)} * @param objPos float[3] object position relative to this shape's center + * @param pmv a new {@link PMVMatrix} which will {@link Scene.PMVMatrixSetup#set(PMVMatrix, int, int, int, int) be setup}, + * {@link #setTransform(PMVMatrix) shape-transformed} and can be reused by the caller. * @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); + public boolean objToWinCoord(final Scene.PMVMatrixSetup pmvMatrixSetup, final int[/*4*/] viewport, final float[/*3*/] objPos, final PMVMatrix pmv, final int[/*2*/] glWinPos) { + pmvMatrixSetup.set(pmv, viewport[0], viewport[1], viewport[2], viewport[3]); setTransform(pmv); - return this.objToWinCoord(pmv, scene.getViewport(), objPos, glWinPos); + return this.objToWinCoord(pmv, viewport, objPos, glWinPos); } /** @@ -502,7 +521,7 @@ public abstract class Shape { * @param glWinY in GL window coordinates, origin bottom-left * @param objPos float[3] target object position of glWinX/glWinY relative to this shape * @return true for successful gluProject(..) and gluUnProject(..) operations, otherwise false - * @see #winToObjCoord(Scene, int, int, float[]) + * @see #winToObjCoord(com.jogamp.graph.ui.gl.Scene.PMVMatrixSetup, int[], int, int, PMVMatrix, float[]) */ public boolean winToObjCoord(final PMVMatrix pmv, final int[/*4*/] viewport, final int glWinX, final int glWinY, final float[/*3*/] objPos) { boolean res = false; @@ -520,23 +539,26 @@ public abstract class Shape { } /** - * Map given gl-window-coordinates to object coordinates relative to this shape and its z-coordinate - * using a local {@link PMVMatrix}. + * Map given gl-window-coordinates to object coordinates relative to this shape and its z-coordinate. * <p> - * The {@link Scene} has be {@link Scene#reshape(com.jogamp.opengl.GLAutoDrawable, int, int, int, int) reshape(..)}ed once. + * The given {@link PMVMatrix} will be {@link Scene.PMVMatrixSetup#set(PMVMatrix, int, int, int, int) setup} properly for this shape + * including this shape's {@link #setTransform(PMVMatrix)}. * </p> + * @param pmvMatrixSetup {@link Scene.PMVMatrixSetup} to {@link Scene.PMVMatrixSetup#set(PMVMatrix, int, int, int, int) setup} given {@link PMVMatrix} {@code pmv}. + * @param viewport used viewport for {@link PMVMatrix#gluUnProject(float, float, float, int[], int, float[], int)} * @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 pmv a new {@link PMVMatrix} which will {@link Scene.PMVMatrixSetup#set(PMVMatrix, int, int, int, int) be setup}, + * {@link #setTransform(PMVMatrix) shape-transformed} and can be reused by the caller. * @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(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); + public boolean winToObjCoord(final Scene.PMVMatrixSetup pmvMatrixSetup, final int[/*4*/] viewport, final int glWinX, final int glWinY, final PMVMatrix pmv, final float[/*3*/] objPos) { + pmvMatrixSetup.set(pmv, viewport[0], viewport[1], viewport[2], viewport[3]); setTransform(pmv); - return this.winToObjCoord(pmv, scene.getViewport(), glWinX, glWinY, objPos); + return this.winToObjCoord(pmv, viewport, glWinX, glWinY, objPos); } public float[] getColor() { @@ -912,18 +934,20 @@ public abstract class Shape { } /** - * @param renderer TODO * @param e original Newt {@link GestureEvent} * @param glWinX x-position in OpenGL model space * @param glWinY y-position in OpenGL model space + * @param pmv well formed PMVMatrix for this shape + * @param viewport the viewport + * @param objPos object position of mouse event relative to this shape */ - /* pp */ final void dispatchGestureEvent(final Scene scene, final GestureEvent e, final int glWinX, final int glWinY, final float[] objPos) { + /* pp */ final void dispatchGestureEvent(final GestureEvent e, final int glWinX, final int glWinY, final PMVMatrix pmv, final int[] viewport, 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 = winToObjCoord(scene, winX2, glWinY, objPos2); + final boolean ok = winToObjCoord(pmv, viewport, winX2, glWinY, objPos2); final float dx = objPos2[0]; final float dy = objPos2[1]; final float sx = scale[0] + ( dx/box.getWidth() ); // bottom-right @@ -933,7 +957,7 @@ public abstract class Shape { inResize, ok, glWinX, glWinY, objPos[0], objPos[1], objPos[2], position[0], position[1], position[2], dx, dy, sx, sy); } - if( ok && resize_sxy_min <= sx && sx <= resize_sxy_max && resize_sxy_min <= sy && sy <= resize_sxy_max ) { + if( ok && resize_sxy_min <= sx && resize_sxy_min <= sy ) { // avoid scale flip 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], @@ -958,7 +982,7 @@ public abstract class Shape { protected abstract void clearImpl(GL2ES2 gl, RegionRenderer renderer); protected abstract void destroyImpl(GL2ES2 gl, RegionRenderer renderer); - protected abstract void addShapeToRegion(GL2ES2 gl, RegionRenderer renderer); + protected abstract void addShapeToRegion(); // // |