diff options
author | Sven Gothel <[email protected]> | 2023-02-16 02:01:51 +0100 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2023-02-16 02:01:51 +0100 |
commit | 6558c7b744525d1db5fe1f0a227958e4f0025622 (patch) | |
tree | 1669c0df19a9bc50d52773c8db2579dc68fe2533 /src | |
parent | b5c604bbdfb8278feaa5e5f5f8c047ab48c4d514 (diff) |
Graph UI: Move methods SceneUIController to UIShape public providing win<->obj coord transformation, enhance documentation a little,
Have win<->obj coord transformation in UIShape public:
- Move SceneUIController.transformShape() -> UIShape.setTransform()
- Move SceneUIController.windowToShapeCoordsImpl() -> UIShape.winToObjCoord()
- Add UIShape.objToWinCoord()
- Add UIShape.getSurfaceSize()
Diffstat (limited to 'src')
4 files changed, 444 insertions, 172 deletions
diff --git a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/CrossHair.java b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/CrossHair.java index 209ce3e57..0c8595c70 100644 --- a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/CrossHair.java +++ b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/CrossHair.java @@ -35,7 +35,7 @@ import com.jogamp.graph.geom.Vertex; import com.jogamp.graph.geom.Vertex.Factory; /** - * GPU based resolution independent Button impl + * GPU based resolution independent Crosshair */ public class CrossHair extends UIShape { private float width, height, lineWidth; @@ -70,11 +70,12 @@ public class CrossHair extends UIShape { protected void addShapeToRegion(final GL2ES2 gl, final RegionRenderer renderer) { final OutlineShape shape = new OutlineShape(renderer.getRenderState().getVertexFactory()); + final float lwh = lineWidth/2f; + final float tw = getWidth(); final float th = getHeight(); final float twh = tw/2f; final float thh = th/2f; - final float lwh = lineWidth/2f; final float ctrX = 0f, ctrY = 0f; final float ctrZ = 0f; @@ -86,8 +87,6 @@ public class CrossHair extends UIShape { shape.lineTo(ctrX-lwh, ctrY+thh, ctrZ); shape.closePath(); - // ctrZ -= 0.05f; - // middle horizontal (CCW!) shape.moveTo(ctrX-twh, ctrY-lwh, ctrZ); shape.lineTo(ctrX+twh, ctrY-lwh, ctrZ); diff --git a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/SceneUIController.java b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/SceneUIController.java index 98ddc1db2..9de206cac 100644 --- a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/SceneUIController.java +++ b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/SceneUIController.java @@ -20,9 +20,7 @@ import com.jogamp.newt.event.PinchToZoomGesture; import com.jogamp.newt.event.GestureHandler.GestureEvent; import com.jogamp.newt.opengl.GLWindow; import com.jogamp.opengl.math.FloatUtil; -import com.jogamp.opengl.math.Quaternion; import com.jogamp.opengl.math.Ray; -import com.jogamp.opengl.math.VectorUtil; import com.jogamp.opengl.math.geom.AABBox; import com.jogamp.opengl.util.PMVMatrix; @@ -130,35 +128,6 @@ public class SceneUIController implements GLEventListener{ cDrawable = drawable; } - private void transformShape(final PMVMatrix pmv, final UIShape uiShape) { - final float[] uiTranslate = uiShape.getTranslate(); - pmv.glTranslatef(uiTranslate[0], uiTranslate[1], uiTranslate[2]); - // final float dz = 100f; - - final Quaternion quat = uiShape.getRotation(); - final boolean rotate = !quat.isIdentity(); - final float[] uiScale = uiShape.getScale(); - final boolean scale = !VectorUtil.isVec3Equal(uiScale, 0, VectorUtil.VEC3_ONE, 0, FloatUtil.EPSILON); - if( rotate || scale ) { - final float[] rotOrigin = uiShape.getRotationOrigin(); - final boolean pivot = !VectorUtil.isVec3Zero(rotOrigin, 0, FloatUtil.EPSILON); - // pmv.glTranslatef(0f, 0f, dz); - if( pivot ) { - pmv.glTranslatef(rotOrigin[0], rotOrigin[1], rotOrigin[2]); - } - if( scale ) { - pmv.glScalef(uiScale[0], uiScale[1], uiScale[2]); - } - if( rotate ) { - pmv.glRotate(quat); - } - if( pivot ) { - pmv.glTranslatef(-rotOrigin[0], -rotOrigin[1], -rotOrigin[2]); - } - // pmv.glTranslatef(0f, 0f, -dz); - } - } - private static Comparator<UIShape> shapeZAscComparator = new Comparator<UIShape>() { @Override public int compare(final UIShape s1, final UIShape s2) { @@ -198,7 +167,7 @@ public class SceneUIController implements GLEventListener{ if( uiShape.isEnabled() ) { uiShape.validate(gl, renderer); pmv.glPushMatrix(); - transformShape(pmv, uiShape); + uiShape.setTransform(pmv); uiShape.drawShape(gl, renderer, sampleCount); pmv.glPopMatrix(); } @@ -243,7 +212,7 @@ public class SceneUIController implements GLEventListener{ if( uiShape.isEnabled() ) { pmv.glPushMatrix(); - transformShape(pmv, uiShape); + uiShape.setTransform(pmv); final boolean ok = pmv.gluUnProjectRay(glWinX, glWinY, winZ0, winZ1, viewport, 0, ray); pmv.glPopMatrix(); if( ok ) { @@ -265,6 +234,14 @@ public class SceneUIController implements GLEventListener{ private final float[] dpyTmp2V3 = new float[3]; private final float[] dpyTmp3V3 = new float[3]; + /** + * Calling {@link UIShape#winToObjCoord(RegionRenderer, int, int, float[])}, retrieving its object position. + * @param activeShape + * @param glWinX in GL window coordinates, origin bottom-left + * @param glWinY in GL window coordinates, origin bottom-left + * @param objPos resulting object position + * @param runnable action + */ public void windowToShapeCoords(final UIShape activeShape, final int glWinX, final int glWinY, final float[] objPos, final Runnable runnable) { if( null == cDrawable || null == activeShape ) { return; @@ -272,30 +249,12 @@ public class SceneUIController implements GLEventListener{ cDrawable.invoke(false, new GLRunnable() { @Override public boolean run(final GLAutoDrawable drawable) { - if( windowToShapeCoordsImpl(activeShape, glWinX, glWinY, objPos) ) { + if( activeShape.winToObjCoord(renderer, glWinX, glWinY, objPos) ) { runnable.run(); } return true; } } ); } - private boolean windowToShapeCoordsImpl(final UIShape activeShape, final int glWinX, final int glWinY, final float[] objPos) { - final PMVMatrix pmv = renderer.getMatrix(); - pmv.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); - - pmv.glPushMatrix(); - transformShape(pmv, activeShape); - boolean res = false; - final float[] ctr = activeShape.getBounds().getCenter(); - if( pmv.gluProject(ctr[0], ctr[1], ctr[2], viewport, 0, dpyTmp1V3, 0) ) { - // System.err.printf("winToShapeCoords.0: shape %d: obj [%f, %f, %f] -> win [%f, %f, %f]%n", shapeId, ctr[0], ctr[1], ctr[2], dpyTmp1V3[0], dpyTmp1V3[1], dpyTmp1V3[2]); - if( pmv.gluUnProject(glWinX, glWinY, dpyTmp1V3[2], viewport, 0, objPos, 0) ) { - // System.err.printf("winToShapeCoords.1: shape %d: win [%d, %d, %f] -> obj [%f, %f, %f]%n", shapeId, glWinX, glWinY, dpyTmp1V3[2], objPos[0], objPos[1], objPos[2]); - res = true; - } - } - pmv.glPopMatrix(); - return res; - } @Override public void dispose(final GLAutoDrawable drawable) { @@ -397,6 +356,7 @@ public class SceneUIController implements GLEventListener{ final float[] objPos = new float[3]; final UIShape shape = activeShape; windowToShapeCoords(shape, glWinX, glWinY, objPos, new Runnable() { + @Override public void run() { shape.dispatchGestureEvent(gh, glWinX, glWinY, objPos); } } ); @@ -405,6 +365,12 @@ public class SceneUIController implements GLEventListener{ } } + /** + * Dispatch mouse event, either directly sending to activeShape or picking one + * @param e original Newt {@link MouseEvent} + * @param glWinX in GL window coordinates, origin bottom-left + * @param glWinY in GL window coordinates, origin bottom-left + */ final void dispatchMouseEvent(final MouseEvent e, final int glWinX, final int glWinY) { if( null == activeShape ) { dispatchMouseEventPickShape(e, glWinX, glWinY, true); @@ -412,20 +378,36 @@ public class SceneUIController implements GLEventListener{ dispatchMouseEventForShape(activeShape, e, glWinX, glWinY); } } + /** + * Pick the shape using the event coordinates + * @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 float[] objPos = new float[3]; final UIShape[] shape = { null }; pickShape(glWinX, glWinY, objPos, shape, new Runnable() { - public void run() { + @Override + public void run() { if( setActive ) { setActiveShape(shape[0]); } shape[0].dispatchMouseEvent(e, glWinX, glWinY, objPos); } } ); } + /** + * Dispatch event to shape + * @param shape target active shape of event + * @param e original Newt {@link MouseEvent} + * @param glWinX in GL window coordinates, origin bottom-left + * @param glWinY in GL window coordinates, origin bottom-left + */ final void dispatchMouseEventForShape(final UIShape shape, final MouseEvent e, final int glWinX, final int glWinY) { final float[] objPos = new float[3]; windowToShapeCoords(shape, glWinX, glWinY, objPos, new Runnable() { + @Override public void run() { shape.dispatchMouseEvent(e, glWinX, glWinY, objPos); } } ); @@ -445,7 +427,7 @@ public class SceneUIController implements GLEventListener{ ly = e.getY(); lId = e.getPointerId(0); } - // flip to GL window coordinates + // flip to GL window coordinates, origin bottom-left final int glWinX = e.getX(); final int glWinY = viewport[3] - e.getY() - 1; dispatchMouseEvent(e, glWinX, glWinY); @@ -453,7 +435,7 @@ public class SceneUIController implements GLEventListener{ @Override public void mouseReleased(final MouseEvent e) { - // flip to GL window coordinates + // flip to GL window coordinates, origin bottom-left final int glWinX = e.getX(); final int glWinY = viewport[3] - e.getY() - 1; dispatchMouseEvent(e, glWinX, glWinY); @@ -484,7 +466,7 @@ public class SceneUIController implements GLEventListener{ ly = e.getY(); // dragged .. delegate to active shape! - // flip to GL window coordinates + // flip to GL window coordinates, origin bottom-left final int glWinX = lx; final int glWinY = viewport[3] - ly - 1; dispatchMouseEventForShape(activeShape, e, glWinX, glWinY); diff --git a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UIShape.java b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UIShape.java index 4976c78e8..f8c04d4bf 100644 --- a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UIShape.java +++ b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UIShape.java @@ -31,7 +31,7 @@ import java.util.ArrayList; import com.jogamp.nativewindow.NativeWindowException; import com.jogamp.opengl.GL2ES2; - +import com.jogamp.opengl.fixedfunc.GLMatrixFunc; import com.jogamp.graph.curve.OutlineShape; import com.jogamp.graph.curve.Region; import com.jogamp.graph.curve.opengl.GLRegion; @@ -44,11 +44,14 @@ import com.jogamp.newt.event.MouseAdapter; import com.jogamp.newt.event.NEWTEvent; import com.jogamp.newt.event.MouseEvent; import com.jogamp.newt.event.MouseListener; +import com.jogamp.opengl.math.FloatUtil; import com.jogamp.opengl.math.Quaternion; +import com.jogamp.opengl.math.VectorUtil; import com.jogamp.opengl.math.geom.AABBox; +import com.jogamp.opengl.util.PMVMatrix; public abstract class UIShape { - public static final boolean DRAW_DEBUG_BOX = true; + public static final boolean DRAW_DEBUG_BOX = false; protected static final int DIRTY_SHAPE = 1 << 0 ; protected static final int DIRTY_STATE = 1 << 1 ; @@ -280,7 +283,7 @@ public abstract class UIShape { region.clear(gl); } addShapeToRegion(gl, renderer); - if( false && DRAW_DEBUG_BOX ) { + if( DRAW_DEBUG_BOX ) { region.clear(gl); final OutlineShape shape = new OutlineShape(renderer.getRenderState().getVertexFactory()); shape.setSharpness(shapesSharpness); @@ -295,6 +298,124 @@ public abstract class UIShape { } } + public void setTransform(final PMVMatrix pmv) { + final float[] uiTranslate = getTranslate(); + pmv.glTranslatef(uiTranslate[0], uiTranslate[1], uiTranslate[2]); + + final Quaternion quat = getRotation(); + final boolean rotate = !quat.isIdentity(); + final float[] uiScale = getScale(); + final boolean scale = !VectorUtil.isVec3Equal(uiScale, 0, VectorUtil.VEC3_ONE, 0, FloatUtil.EPSILON); + if( rotate || scale ) { + final float[] rotOrigin = getRotationOrigin(); + final boolean pivot = !VectorUtil.isVec3Zero(rotOrigin, 0, FloatUtil.EPSILON); + if( pivot ) { + pmv.glTranslatef(rotOrigin[0], rotOrigin[1], rotOrigin[2]); + } + if( scale ) { + pmv.glScalef(uiScale[0], uiScale[1], uiScale[2]); + } + if( rotate ) { + pmv.glRotate(quat); + } + if( pivot ) { + pmv.glTranslatef(-rotOrigin[0], -rotOrigin[1], -rotOrigin[2]); + } + } + } + + /** + * Retrieve window surface size of this shape + * @param renderer source of viewport and PMVMatrix + * @param surfaceSize target surface size + * @return true for successful gluProject(..) operation, otherwise false + */ + public boolean getSurfaceSize(final RegionRenderer renderer, final int[/*2*/] surfaceSize) { + final PMVMatrix pmv = renderer.getMatrix(); + pmv.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); + + pmv.glPushMatrix(); + setTransform(pmv); + 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(); + + 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]); + if( pmv.gluProject(low[0], low[1], low[2], viewport, 0, winCoordLow, 0) ) { + // System.err.printf("UIShape::surfaceSize.L: shape %d: obj [%f, %f, %f] -> win [%f, %f, %f]%n", getName(), low[0], low[1], low[2], winCoordLow[0], winCoordLow[1], winCoordLow[2]); + surfaceSize[0] = (int)(winCoordHigh[0] - winCoordLow[0]); + surfaceSize[1] = (int)(winCoordHigh[1] - winCoordLow[1]); + // System.err.printf("UIShape::surfaceSize.S: shape %d: %f x %f -> %d x %d%n", getName(), winCoordHigh[0] - winCoordLow[0], winCoordHigh[1] - winCoordLow[1], surfaceSize[0], surfaceSize[1]); + res = true; + } + } + pmv.glPopMatrix(); + return res; + } + + /** + * Map given object coordinate relative to this shape to window coordinates + * @param renderer source of viewport and PMVMatrix + * @param objPos object position relative to this shape's center + * @param glWinPos target window position of objPos relative to this shape + * @return true for successful gluProject(..) operation, otherwise false + */ + public boolean objToWinCoord(final RegionRenderer renderer, final float[/*3*/] objPos, final int[/*2*/] glWinPos) { + final PMVMatrix pmv = renderer.getMatrix(); + pmv.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); + + pmv.glPushMatrix(); + setTransform(pmv); + 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]; + + 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]); + glWinPos[0] = (int)(winCoord[0]); + glWinPos[1] = (int)(winCoord[1]); + // System.err.printf("UIShape::objToWinCoord.X: shape %d: %f / %f -> %d / %d%n", getName(), winCoord[0], winCoord[1], glWinPos[0], glWinPos[1]); + res = true; + } + pmv.glPopMatrix(); + return res; + } + + /** + * Map given gl-window-coordinates to object coordinates relative to this shape and its z-coordinate. + * @param renderer source of viewport and PMVMatrix + * @param glWinX in GL window coordinates, origin bottom-left + * @param glWinY in GL window coordinates, origin bottom-left + * @param objPos target object position of glWinX/glWinY relative to this shape + * @return @return true for successful gluProject(..) and gluUnProject(..) operations, otherwise false + */ + public boolean winToObjCoord(final RegionRenderer renderer, final int glWinX, final int glWinY, final float[/*3*/] objPos) { + final PMVMatrix pmv = renderer.getMatrix(); + pmv.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); + + pmv.glPushMatrix(); + setTransform(pmv); + boolean res = false; + final float[] ctr = getBounds().getCenter(); + final int[] viewport = renderer.getViewport(new int[4]); + final float[] tmp = new float[3]; + 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]); + if( pmv.gluUnProject(glWinX, glWinY, tmp[2], viewport, 0, objPos, 0) ) { + // System.err.printf("UIShape::winToObjCoord.1: shape %d: win [%d, %d, %f] -> obj [%f, %f, %f]%n", getName(), glWinX, glWinY, tmp[2], objPos[0], objPos[1], objPos[2]); + res = true; + } + } + pmv.glPopMatrix(); + return res; + } + public float[] getColor() { return rgbaColor; } @@ -420,15 +541,22 @@ public abstract class UIShape { * The latter fields are also normalized to lower-left zero origin, allowing easier usage. */ public static class PointerEventInfo { - /** The intended {@link UIShape} instance for this event */ + /** The associated {@link UIShape} for this event */ public final UIShape shape; - /** The relative pointer position inside the intended {@link UIShape}. */ + /** The relative object coordinate of glWinX/glWinY to the associated {@link UIShape}. */ public final float[] objPos; - /** window x-position in OpenGL model space */ + /** X-coordinate in GL window coordinates, origin bottom-left */ public final int glWinX; - /** window y-position in OpenGL model space */ + /** Y-coordinate in GL window coordinates, origin bottom-left */ public final int glWinY; + /** + * Ctor + * @param glWinX in GL window coordinates, origin bottom-left + * @param glWinY in GL window coordinates, origin bottom-left + * @param shape associated shape + * @param objPos relative object coordinate of glWinX/glWinY to the associated shape. + */ PointerEventInfo(final int glWinX, final int glWinY, final UIShape shape, final float[] objPos) { this.glWinX = glWinX; this.glWinY = glWinY; @@ -455,10 +583,11 @@ public abstract class UIShape { } /** - * + * Dispatch given NEWT mouse event to this shape * @param e original Newt {@link MouseEvent} - * @param glX x-position in OpenGL model space - * @param glY y-position in OpenGL model space + * @param glWinX in GL window coordinates, origin bottom-left + * @param glWinY in GL window coordinates, origin bottom-left + * @param objPos object position of mouse event within this shape */ public final void dispatchMouseEvent(final MouseEvent e, final int glWinX, final int glWinY, final float[] objPos) { e.setAttachment(new PointerEventInfo(glWinX, glWinY, this, objPos)); @@ -524,19 +653,26 @@ public abstract class UIShape { // protected OutlineShape createDebugOutline(final OutlineShape shape, final AABBox box) { - final float tw = box.getWidth(); - final float th = box.getHeight(); + final float d = 0.025f; + final float tw = box.getWidth() + d*2f; + final float th = box.getHeight() + d*2f; - final float minX = box.getMinX(); - final float minY = box.getMinY(); - final float z = box.getMinZ() + 0.025f; + final float minX = box.getMinX() - d; + final float minY = box.getMinY() - d; + final float z = 0; // box.getMinZ() + 0.025f; // CCW! - shape.addVertex(minX, minY, z, true); - shape.addVertex(minX+tw, minY, z, true); - shape.addVertex(minX+tw, minY + th, z, true); - shape.addVertex(minX, minY + th, z, true); - shape.closeLastOutline(true); + shape.moveTo(minX, minY, z); + shape.lineTo(minX+tw, minY, z); + shape.lineTo(minX+tw, minY + th, z); + shape.lineTo(minX, minY + th, z); + shape.closePath(); + + // shape.addVertex(minX, minY, z, true); + // shape.addVertex(minX+tw, minY, z, true); + // shape.addVertex(minX+tw, minY + th, z, true); + // shape.addVertex(minX, minY + th, z, true); + // shape.closeLastOutline(true); return shape; } diff --git a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UIShapeDemo01.java b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UIShapeDemo01.java index f18491a2a..b9d249e19 100644 --- a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UIShapeDemo01.java +++ b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UIShapeDemo01.java @@ -1,5 +1,5 @@ /** - * Copyright 2010 JogAmp Community. All rights reserved. + * Copyright 2010-2023 JogAmp Community. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: @@ -37,27 +37,40 @@ import com.jogamp.opengl.GL; import com.jogamp.opengl.GL2ES2; import com.jogamp.opengl.GLAnimatorControl; import com.jogamp.opengl.GLAutoDrawable; +import com.jogamp.opengl.GLCapabilities; import com.jogamp.opengl.GLEventListener; import com.jogamp.opengl.GLException; import com.jogamp.opengl.GLPipelineFactory; +import com.jogamp.opengl.GLProfile; import com.jogamp.opengl.GLRunnable; import com.jogamp.opengl.fixedfunc.GLMatrixFunc; +import com.jogamp.opengl.math.FloatUtil; +import com.jogamp.opengl.math.geom.AABBox; import com.jogamp.opengl.test.junit.graph.demos.MSAATool; +import com.jogamp.common.util.InterruptSource; import com.jogamp.graph.curve.Region; import com.jogamp.graph.curve.opengl.RegionRenderer; import com.jogamp.graph.curve.opengl.RenderState; +import com.jogamp.graph.curve.opengl.TextRegionUtil; import com.jogamp.graph.font.Font; import com.jogamp.graph.font.FontFactory; +import com.jogamp.graph.font.FontSet; import com.jogamp.graph.geom.SVertex; +import com.jogamp.newt.Window; +import com.jogamp.newt.event.KeyAdapter; import com.jogamp.newt.event.KeyEvent; import com.jogamp.newt.event.KeyListener; import com.jogamp.newt.event.MouseEvent; import com.jogamp.newt.event.MouseListener; +import com.jogamp.newt.event.WindowAdapter; +import com.jogamp.newt.event.WindowEvent; import com.jogamp.newt.opengl.GLWindow; +import com.jogamp.opengl.util.Animator; import com.jogamp.opengl.util.GLReadBufferUtil; import com.jogamp.opengl.util.PMVMatrix; /** + * Basic UIShape and Type Rendering demo. * * Action Keys: * - 1/2: zoom in/out @@ -67,14 +80,77 @@ import com.jogamp.opengl.util.PMVMatrix; * - v: toggle v-sync * - s: screenshot */ -public class UIListener01 implements GLEventListener { +public class UIShapeDemo01 implements GLEventListener { + static final boolean DEBUG = false; + static final boolean TRACE = false; + + public static void main(final String[] args) throws IOException { + Font font = null; + if( 0 != args.length ) { + for(int i=0; i<args.length; i++) { + if(args[i].equals("-font")) { + i++; + font = FontFactory.get(new File(args[i])); + } + } + } + if( null == font ) { + font = FontFactory.get(FontFactory.UBUNTU).get(FontSet.FAMILY_LIGHT, FontSet.STYLE_SERIF); + } + System.err.println("Font: "+font.getFullFamilyName()); + + final GLProfile glp = GLProfile.getGL2ES2(); + final GLCapabilities caps = new GLCapabilities(glp); + caps.setAlphaBits(4); + caps.setSampleBuffers(true); + caps.setNumSamples(4); + System.out.println("Requested: " + caps); + + final GLWindow window = GLWindow.create(caps); + // window.setPosition(10, 10); + window.setSize(800, 400); + window.setTitle(UIShapeDemo01.class.getSimpleName()+": "+window.getSurfaceWidth()+" x "+window.getSurfaceHeight()); + final RenderState rs = RenderState.createRenderState(SVertex.factory()); + final UIShapeDemo01 uiGLListener = new UIShapeDemo01(font, Region.COLORCHANNEL_RENDERING_BIT, rs, DEBUG, TRACE); + uiGLListener.attachInputListenerTo(window); + window.addGLEventListener(uiGLListener); + window.setVisible(true); + + final Animator animator = new Animator(); + // animator.setUpdateFPSFrames(60, System.err); + animator.add(window); + + window.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(final KeyEvent arg0) { + if(arg0.getKeyCode() == KeyEvent.VK_F4) { + new InterruptSource.Thread() { + @Override + public void run() { + window.destroy(); + } }.start(); + } + } + }); + window.addWindowListener(new WindowAdapter() { + @Override + public void windowDestroyed(final WindowEvent e) { + animator.stop(); + } + }); + + animator.start(); + } + + private final Font font; private final GLReadBufferUtil screenshot; private final int renderModes; private final RegionRenderer rRenderer; private final boolean debug; private final boolean trace; - protected LabelButton button; + private final LabelButton button; + private final CrossHair crossHair; private KeyAction keyAction; private MouseAction mouseAction; @@ -83,52 +159,39 @@ public class UIListener01 implements GLEventListener { private final float[] position = new float[] {0,0,0}; - private float xTran = -10; - private float yTran = 10; - private float ang = 0f; - private float zoom = -70f; + private static final float xTran = 0f; + private static final float yTran = 0f; + private static final float zTran = -1/5f; + private static final float zNear = 0.1f; + private static final float zFar = 7000.0f; boolean ignoreInput = false; - public UIListener01(final int renderModes, final RenderState rs, final boolean debug, final boolean trace) { + public UIShapeDemo01(final Font font, final int renderModes, final RenderState rs, final boolean debug, final boolean trace) { + this.font = font; this.renderModes = renderModes; this.rRenderer = RegionRenderer.create(rs, RegionRenderer.defaultBlendEnable, RegionRenderer.defaultBlendDisable); this.debug = debug; this.trace = trace; this.screenshot = new GLReadBufferUtil(false, false); - setMatrix(-4, -2, 0f, -10); - try { - final Font font = FontFactory.get(FontFactory.UBUNTU).getDefault(); - button = new LabelButton(SVertex.factory(), 0, font, "Click me!", 4f, 2f); - button.translate(2,1,0); - /** Button defaults ! + button = new LabelButton(SVertex.factory(), renderModes, font, "Click me!", 1/8f, 1/16f); + button.setLabelColor(0.0f,0.0f,0.0f); + /** Button defaults ! button.setLabelColor(1.0f,1.0f,1.0f); button.setButtonColor(0.6f,0.6f,0.6f); button.setCorner(1.0f); button.setSpacing(2.0f); - */ - System.err.println(button); - } catch (final IOException ex) { - System.err.println("Caught: "+ex.getMessage()); - ex.printStackTrace(); - } + */ + System.err.println(button); + crossHair = new CrossHair(SVertex.factory(), renderModes, 1/20f, 1/20f, 1/1000f); + crossHair.setColor(0f,0f,1f,1f); + crossHair.setEnabled(true); } public final RegionRenderer getRegionRenderer() { return rRenderer; } - public final float getZoom() { return zoom; } - public final float getXTran() { return xTran; } - public final float getYTran() { return yTran; } - public final float getAngle() { return ang; } public final float[] getPosition() { return position; } - public void setMatrix(final float xtrans, final float ytrans, final float angle, final int zoom) { - this.xTran = xtrans; - this.yTran = ytrans; - this.ang = angle; - this.zoom = zoom; - } - @Override public void init(final GLAutoDrawable drawable) { autoDrawable = drawable; @@ -153,8 +216,24 @@ public class UIListener01 implements GLEventListener { final GL2ES2 gl = drawable.getGL().getGL2ES2(); gl.glViewport(xstart, ystart, width, height); - rRenderer.reshapePerspective(45.0f, width, height, 0.1f, 7000.0f); - dumpMatrix(); + rRenderer.reshapePerspective(45.0f, width, height, zNear, zFar); + // rRenderer.reshapeOrtho(width, height, zNear, zFar); + + lastWidth = width; + lastHeight = height; + if( drawable instanceof Window ) { + ((Window)drawable).setTitle(UIShapeDemo01.class.getSimpleName()+": "+drawable.getSurfaceWidth()+" x "+drawable.getSurfaceHeight()); + } + } + float lastWidth = 0f, lastHeight = 0f; + + final int[] sampleCount = { 4 }; + + private void drawShape(final GL2ES2 gl, final PMVMatrix pmv, final RegionRenderer renderer, final UIShape shape) { + pmv.glPushMatrix(); + shape.setTransform(pmv); + shape.drawShape(gl, renderer, sampleCount); + pmv.glPopMatrix(); } @Override @@ -164,49 +243,70 @@ public class UIListener01 implements GLEventListener { gl.glClearColor(1.0f, 1.0f, 1.0f, 1.0f); gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); - final int[] sampleCount = { 4 }; - final float[] translate = button.getTranslate(); - - final RegionRenderer regionRenderer = getRegionRenderer(); - final PMVMatrix pmv = regionRenderer.getMatrix(); + final RegionRenderer renderer = getRegionRenderer(); + final PMVMatrix pmv = renderer.getMatrix(); pmv.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); pmv.glLoadIdentity(); - pmv.glTranslatef(getXTran(), getYTran(), getZoom()); - pmv.glRotatef(getAngle(), 0, 1, 0); - pmv.glTranslatef(translate[0], translate[1], 0); - button.drawShape(gl, regionRenderer, sampleCount); + pmv.glTranslatef(xTran, yTran, zTran); + renderer.enable(gl, true); + drawShape(gl, pmv, renderer, button); + drawShape(gl, pmv, renderer, crossHair); + { + final String text = "Hello Origin."; + final float full_width_o; + { + final float orthoDist = -zTran; // assume orthogonal plane at -zTran + float glWinX = 0; + float glWinY = 0; + final float winZ = FloatUtil.getOrthoWinZ(orthoDist, zNear, zFar); + final float[] objCoord0 = new float[3]; + final float[] objCoord1 = new float[3]; + if( pmv.gluUnProject(glWinX, glWinY, winZ, renderer.getViewport(), 0, objCoord0, 0) ) { + if( once ) { + System.err.printf("winToObjCoord: win [%f, %f, %f] -> obj [%f, %f, %f]%n", glWinX, glWinY, winZ, objCoord0[0], objCoord0[1], objCoord0[2]); + } + } + glWinX = drawable.getSurfaceWidth(); + glWinY = drawable.getSurfaceHeight(); + if( pmv.gluUnProject(glWinX, glWinY, winZ, renderer.getViewport(), 0, objCoord1, 0) ) { + if( once ) { + System.err.printf("winToObjCoord: win [%f, %f, %f] -> obj [%f, %f, %f]%n", glWinX, glWinY, winZ, objCoord1[0], objCoord1[1], objCoord1[2]); + } + } + full_width_o = objCoord1[0] - objCoord0[0]; + } + final AABBox txt_box_em = font.getPointsBounds(null, text); + final float full_width_s = full_width_o / txt_box_em.getWidth(); + final float txt_scale = full_width_s/2f; + pmv.glPushMatrix(); + pmv.glScalef(txt_scale, txt_scale, 1f); + pmv.glTranslatef(-txt_box_em.getWidth(), 0f, 0f); + final AABBox txt_box_r = TextRegionUtil.drawString3D(gl, renderModes, renderer, font, text, new float[] { 0, 0, 0, 1 }, sampleCount); + if( once ) { + final AABBox txt_box_em2 = font.getPointsBounds2(null, text); + System.err.println("XXX: full_width: "+full_width_o+" / "+txt_box_em.getWidth()+" -> "+full_width_s); + System.err.println("XXX: txt_box_em "+txt_box_em); + System.err.println("XXX: txt_box_e2 "+txt_box_em2); + System.err.println("XXX: txt_box_rg "+txt_box_r); + once = false; + } + pmv.glPopMatrix(); + } + renderer.enable(gl, false); } + static boolean once = true; @Override public void dispose(final GLAutoDrawable drawable) { final GL2ES2 gl = drawable.getGL().getGL2ES2(); button.destroy(gl, getRegionRenderer()); + crossHair.destroy(gl, getRegionRenderer()); autoDrawable = null; screenshot.dispose(gl); rRenderer.destroy(gl); } - public void zoom(final int v){ - zoom += v; - dumpMatrix(); - } - - public void move(final float x, final float y){ - xTran += x; - yTran += y; - dumpMatrix(); - } - public void rotate(final float delta){ - ang += delta; - ang %= 360.0f; - dumpMatrix(); - } - - void dumpMatrix() { - System.err.println("Matrix: " + xTran + "/" + yTran + " x"+zoom + " @"+ang); - } - /** Attach the input listener to the window */ public void attachInputListenerTo(final GLWindow window) { if ( null == keyAction ) { @@ -234,7 +334,7 @@ public class UIListener01 implements GLEventListener { public void printScreen(final GLAutoDrawable drawable, final String dir, final String tech, final String objName, final boolean exportAlpha) throws GLException, IOException { final StringWriter sw = new StringWriter(); final PrintWriter pw = new PrintWriter(sw); - pw.printf("-%03dx%03d-Z%04d-T%04d-%s", drawable.getSurfaceWidth(), drawable.getSurfaceHeight(), (int)Math.abs(zoom), 0, objName); + pw.printf("-%03dx%03d-Z%04d-T%04d-%s", drawable.getSurfaceWidth(), drawable.getSurfaceHeight(), (int)Math.abs(zTran), 0, objName); final String filename = dir + tech + sw +".png"; if(screenshot.readPixels(drawable.getGL(), false)) { @@ -268,14 +368,70 @@ public class UIListener01 implements GLEventListener { @Override public void mousePressed(final MouseEvent e) { - button.setLabelColor(0.8f,0.8f,0.8f); - button.setColor(0.1f, 0.1f, 0.1f, 1.0f); + autoDrawable.invoke(false, new GLRunnable() { // avoid data-race + @Override + public boolean run(final GLAutoDrawable drawable) { + System.err.println("\n\nMouse: "+e); + + final RegionRenderer renderer = getRegionRenderer(); + final PMVMatrix pmv = renderer.getMatrix(); + pmv.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); + pmv.glLoadIdentity(); + pmv.glTranslatef(xTran, yTran, zTran); + + // flip to GL window coordinates, origin bottom-left + final int[] viewport = renderer.getViewport(new int[4]); + final int glWinX = e.getX(); + final int glWinY = viewport[3] - e.getY() - 1; + + { + final float[] objPos = new float[3]; + System.err.println("\n\nButton: "+button); + button.winToObjCoord(renderer, glWinX, glWinY, objPos); + System.err.println("Button: Click: Win "+glWinX+"/"+glWinY+" -> Obj "+objPos[0]+"/"+objPos[1]+"/"+objPos[1]); + + final int[] surfaceSize = new int[2]; + button.getSurfaceSize(renderer, surfaceSize); + System.err.println("Button: Size: Pixel "+surfaceSize[0]+" x "+surfaceSize[1]); + } + { + final float[] objPosC = crossHair.getBounds().getCenter(); + final int[] objWinPos = new int[2]; + System.err.println("\n\nCrossHair: "+crossHair); + if( crossHair.objToWinCoord(renderer, objPosC, objWinPos) ) { + System.err.println("CrossHair: Obj: Obj "+objPosC[0]+"/"+objPosC[1]+"/"+objPosC[1]+" -> Win "+objWinPos[0]+"/"+objWinPos[1]); + } + + final float[] objPos2 = new float[3]; + crossHair.winToObjCoord(renderer, objWinPos[0], objWinPos[1], objPos2); + System.err.println("CrossHair: Obj: Win "+objWinPos[0]+"/"+objWinPos[1]+" -> Obj "+objPos2[0]+"/"+objPos2[1]+"/"+objPos2[1]); + + final float[] winObjPos = new float[3]; + if( crossHair.winToObjCoord(renderer, glWinX, glWinY, winObjPos) ) { + // final float[] translate = crossHair.getTranslate(); + // final float[] objPosT = new float[] { objPosC[0]+translate[0], objPosC[1]+translate[1], objPosC[2]+translate[2] }; + final float dx = winObjPos[0] - objPosC[0]; + final float dy = winObjPos[1] - objPosC[1]; + // final float dz = winObjPos[2] - objPosT[2]; + if( !FloatUtil.isZero(dx, FloatUtil.EPSILON) || !FloatUtil.isZero(dy, FloatUtil.EPSILON) ) { + System.err.println("CrossHair: Move.1: Win "+glWinX+"/"+glWinY+" -> Obj "+winObjPos[0]+"/"+winObjPos[1]+"/"+winObjPos[1]+" -> diff "+dx+" / "+dy); + crossHair.translate(dx, dy, 0f); + } else { + System.err.println("CrossHair: Move.0: Win "+glWinX+"/"+glWinY+" -> Obj "+winObjPos[0]+"/"+winObjPos[1]+"/"+winObjPos[1]+" -> diff "+dx+" / "+dy); + } + } + + final int[] surfaceSize = new int[2]; + crossHair.getSurfaceSize(renderer, surfaceSize); + System.err.println("CrossHair: Size: Pixel "+surfaceSize[0]+" x "+surfaceSize[1]); + } + return true; + } } ); + } @Override public void mouseReleased(final MouseEvent e) { - button.setLabelColor(1.0f,1.0f,1.0f); - button.setColor(0.6f,0.6f,0.6f, 1.0f); } @Override @@ -306,22 +462,22 @@ public class UIListener01 implements GLEventListener { } if(arg0.getKeyCode() == KeyEvent.VK_1){ - zoom(2); + button.translate(0f, 0f, -zTran/10f); } else if(arg0.getKeyCode() == KeyEvent.VK_2){ - zoom(-2); + button.translate(0f, 0f, zTran/10f); } else if(arg0.getKeyCode() == KeyEvent.VK_UP){ - move(0, 1); + button.translate(0f, button.getHeight()/10f, 0f); } else if(arg0.getKeyCode() == KeyEvent.VK_DOWN){ - move(0, -1); + button.translate(0f, -button.getHeight()/10f, 0f); } else if(arg0.getKeyCode() == KeyEvent.VK_LEFT){ - move(-1, 0); + button.translate(-button.getWidth()/10f, 0f, 0f); } else if(arg0.getKeyCode() == KeyEvent.VK_RIGHT){ - move(1, 0); + button.translate(button.getWidth()/10f, 0f, 0f); } else if(arg0.getKeyCode() == KeyEvent.VK_4){ button.setSpacing(button.getSpacingX()-0.01f, button.getSpacingY()-0.005f); @@ -340,10 +496,10 @@ public class UIListener01 implements GLEventListener { System.err.println("Button Corner: " + button.getCorner()); } else if(arg0.getKeyCode() == KeyEvent.VK_0){ - rotate(1); + // rotate(1); } else if(arg0.getKeyCode() == KeyEvent.VK_9){ - rotate(-1); + // rotate(-1); } else if(arg0.getKeyCode() == KeyEvent.VK_V) { if(null != autoDrawable) { @@ -375,24 +531,23 @@ public class UIListener01 implements GLEventListener { } } else if(arg0.getKeyCode() == KeyEvent.VK_S){ - rotate(-1); - if(null != autoDrawable) { - autoDrawable.invoke(false, new GLRunnable() { - @Override - public boolean run(final GLAutoDrawable drawable) { - try { - final String type = Region.getRenderModeString(renderModes); - printScreen(drawable, "./", "demo-"+type, "snap"+screenshot_num, false); - screenshot_num++; - } catch (final GLException e) { - e.printStackTrace(); - } catch (final IOException e) { - e.printStackTrace(); - } - return true; + if(null != autoDrawable) { + autoDrawable.invoke(false, new GLRunnable() { + @Override + public boolean run(final GLAutoDrawable drawable) { + try { + final String type = Region.getRenderModeString(renderModes); + printScreen(drawable, "./", "demo-"+type, "snap"+screenshot_num, false); + screenshot_num++; + } catch (final GLException e) { + e.printStackTrace(); + } catch (final IOException e) { + e.printStackTrace(); } - }); - } + return true; + } + }); + } } } @Override |