diff options
author | Sven Gothel <[email protected]> | 2014-04-09 09:09:51 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2014-04-09 09:09:51 +0200 |
commit | fe47c613e3e07681a5366d6ec3f071fdc4ade65d (patch) | |
tree | fd980c33b0fde468918227d31174320b8ae6b163 /src | |
parent | ff4e2b1996d2cfab1eb154020106004fb71471fd (diff) |
Bug 801: Region Dirty Update; TextureSequence GLMediaPlayer Fix; Blending Fix ;
- Region Dirty Update
- Split dirty -> ShapeDirty + StateDirty,
where StateDirty forces re-rendering content
w/o geometry update as req. for 2-pass mode.
- Fix TextureSequence (GLMediaPlayer) usage in RegionRenderer / GLRegion*
- handle GL_TEXTURE_EXTERNAL_OES incl. Android ES3 bug
- inject TextureSequence's shader stubs
- shader: Use abstract lookup 'texture2D' -> 'gcuTexture2D'
- flip scaled colorTexBBox if TextureSequence 'tex.getMustFlipVertically()'
- TODO: Handle multiple TextureSequence shader programs!
- Fix Blending: GLRegion* / RegionRenderer / RenderState
- Disable/Enable depth-writing w/ blending
- Region impl. sets proper glBlendFunc*(..),
i.e. 2-pass:
- render2FBO: glClearColor(0f, 0f, 0f, 0f)
glBlendFuncSeparate(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA, GL.GL_ONE, GL.GL_ONE_MINUS_SRC_ALPHA)
- renderFBO: glBlendFunc(GL.GL_ONE, GL.GL_ONE_MINUS_SRC_ALPHA)
- User code shall not set glClearColor(..) for 2-pass anymore
- Graph-UI Demo
- UIShape:
- Add MouseGestureListener, combining MouseListener + GestureListener
- EventDetails -> PointerEventInfo
- PointerEventInfo contains objPos (ray-intersection) and glWin-pos
- Toggle:
- Separate color (on/off) if enabled
- Toggle on click if enabled
- SceneUIController
- Use PinchToZoomGesture and propagete same gesture to UIShape
- Use AABBox.getRayIntersection(..) using 'real' shape coordinates
for 1st picking.
- Use shape PMV for secondary picking (drag, zoom 2-pointer, etc),
see windowToShapeCoords(..)
- Sort shapes according to z-value (render: ascending; picking: descending)
- Only 'drag' if pointerId matches 1st pressed pointer
Diffstat (limited to 'src')
21 files changed, 827 insertions, 377 deletions
diff --git a/src/jogl/classes/com/jogamp/graph/curve/Region.java b/src/jogl/classes/com/jogamp/graph/curve/Region.java index 230fa324d..45ad20f1b 100644 --- a/src/jogl/classes/com/jogamp/graph/curve/Region.java +++ b/src/jogl/classes/com/jogamp/graph/curve/Region.java @@ -105,9 +105,12 @@ public abstract class Region { public static final int DEFAULT_TWO_PASS_TEXTURE_UNIT = 0; + protected static final int DIRTY_SHAPE = 1 << 0 ; + protected static final int DIRTY_STATE = 1 << 1 ; + private final int renderModes; private int quality; - private boolean dirty = true; + private int dirty = DIRTY_SHAPE | DIRTY_STATE; private int numVertices = 0; protected final AABBox box = new AABBox(); protected Frustum frustum = null; @@ -187,7 +190,7 @@ public abstract class Region { public final void setQuality(int q) { quality=q; } protected void clearImpl() { - dirty = true; + dirty = DIRTY_SHAPE | DIRTY_STATE; numVertices = 0; box.reset(); } @@ -356,7 +359,7 @@ public abstract class Region { // int vertsDupCountV = 0, vertsDupCountT = 0; System.err.println("Region.addOutlineShape().X: box "+box); } - setDirty(true); + markShapeDirty(); } public final void addOutlineShapes(final List<OutlineShape> shapes, final AffineTransform transform, final float[] rgbaColor) { @@ -371,32 +374,35 @@ public abstract class Region { } /** - * Check if this region is dirty. A region is marked dirty when new - * Vertices, Triangles, and or Lines are added after a call to update(). - * <p> - * A region is also dirty if other render attributes or parameters are changed! - * </p> - * - * @return true if region is Dirty, false otherwise - * - * @see update(GL2ES2) + * Mark this region's shape dirty, i.e. it's + * Vertices, Triangles, and or Lines changed. */ - public final boolean isDirty() { - return dirty; + public final void markShapeDirty() { + dirty |= DIRTY_SHAPE; + } + /** Returns true if this region's shape are dirty, see {@link #markShapeDirty()}. */ + public final boolean isShapeDirty() { + return 0 != ( dirty & DIRTY_SHAPE ) ; } - /** - * See {@link #isDirty()}. + * Mark this region's state dirty, i.e. + * it's render attributes or parameters changed. */ - protected final void setDirty(boolean v) { - dirty = v; + public final void markStateDirty() { + dirty |= DIRTY_STATE; + } + /** Returns true if this region's state is dirty, see {@link #markStateDirty()}. */ + public final boolean isStateDirty() { + return 0 != ( dirty & DIRTY_STATE ) ; } + /** - * See {@link #isDirty()}. + * See {@link #markShapeDirty()} and {@link #markStateDirty()}. */ - public final void markDirty() { - dirty = true; + protected final void clearDirtyBits(int v) { + dirty &= ~v; } + protected final int getDirtyBits() { return dirty; } public String toString() { return "Region["+getRenderModeString(this.renderModes)+", q "+quality+", dirty "+dirty+", vertices "+numVertices+", box "+box+"]"; diff --git a/src/jogl/classes/com/jogamp/graph/curve/opengl/GLRegion.java b/src/jogl/classes/com/jogamp/graph/curve/opengl/GLRegion.java index 3c6045a1f..49c1944b1 100644 --- a/src/jogl/classes/com/jogamp/graph/curve/opengl/GLRegion.java +++ b/src/jogl/classes/com/jogamp/graph/curve/opengl/GLRegion.java @@ -83,7 +83,7 @@ public abstract class GLRegion extends Region { /**
* Updates a graph region by updating the ogl related
- * objects for use in rendering if {@link #isDirty()}.
+ * objects for use in rendering if {@link #isShapeDirty()}.
* <p>Allocates the ogl related data and initializes it the 1st time.<p>
* <p>Called by {@link #draw(GL2ES2, RenderState, int, int, int)}.</p>
*/
@@ -139,11 +139,11 @@ public abstract class GLRegion extends Region { * @see RegionRenderer#enable(GL2ES2, boolean)
*/
public final void draw(final GL2ES2 gl, final RegionRenderer renderer, final int[/*1*/] sampleCount) {
- if(isDirty()) {
+ if( isShapeDirty() ) {
updateImpl(gl);
- setDirty(false);
}
drawImpl(gl, renderer, sampleCount);
+ clearDirtyBits(DIRTY_SHAPE|DIRTY_STATE);
}
protected abstract void drawImpl(final GL2ES2 gl, final RegionRenderer renderer, final int[/*1*/] sampleCount);
diff --git a/src/jogl/classes/com/jogamp/graph/curve/opengl/RegionRenderer.java b/src/jogl/classes/com/jogamp/graph/curve/opengl/RegionRenderer.java index 7337aca36..a0a318a49 100644 --- a/src/jogl/classes/com/jogamp/graph/curve/opengl/RegionRenderer.java +++ b/src/jogl/classes/com/jogamp/graph/curve/opengl/RegionRenderer.java @@ -32,15 +32,19 @@ import java.util.Iterator; import javax.media.opengl.GL; import javax.media.opengl.GL2ES2; +import javax.media.opengl.GLES2; import javax.media.opengl.GLException; import javax.media.opengl.fixedfunc.GLMatrixFunc; import jogamp.graph.curve.opengl.shader.AttributeNames; +import jogamp.graph.curve.opengl.shader.UniformNames; import com.jogamp.opengl.GLExtensions; import com.jogamp.opengl.util.glsl.ShaderCode; import com.jogamp.opengl.util.glsl.ShaderProgram; +import com.jogamp.opengl.util.texture.TextureSequence; import com.jogamp.opengl.util.PMVMatrix; +import com.jogamp.common.os.Platform; import com.jogamp.common.util.IntObjectHashMap; import com.jogamp.graph.curve.Region; @@ -65,10 +69,12 @@ public class RegionRenderer { /** * Default {@link GL#GL_BLEND} <i>enable</i> {@link GLCallback}, - * turning on the {@link GL#GL_BLEND} state and setting up - * {@link GL#glBlendFunc(int, int) glBlendFunc}({@link GL#GL_SRC_ALPHA}, {@link GL#GL_ONE_MINUS_SRC_ALPHA}). + * turning-off depth writing via {@link GL#glDepthMask(boolean)} and turning-on the {@link GL#GL_BLEND} state. * <p> - * Implementation also sets {@link RegionRenderer#getRenderState() RenderState}'s {@link RenderState#BITHINT_BLENDING_ENABLED blending bit-hint}. + * Implementation also sets {@link RegionRenderer#getRenderState() RenderState}'s {@link RenderState#BITHINT_BLENDING_ENABLED blending bit-hint}, + * which will cause {@link GLRegion#draw(GL2ES2, RegionRenderer, int[]) GLRegion's draw-method} + * to set the proper {@link GL#glBlendFuncSeparate(int, int, int, int) blend-function} + * and the clear-color to <i>transparent-black</i> in case of {@link Region#isTwoPass(int) multipass} FBO rendering. * </p> * @see #create(RenderState, GLCallback, GLCallback) * @see #enable(GL2ES2, boolean) @@ -76,16 +82,17 @@ public class RegionRenderer { public static final GLCallback defaultBlendEnable = new GLCallback() { @Override public void run(final GL gl, final RegionRenderer renderer) { + gl.glDepthMask(false); gl.glEnable(GL.GL_BLEND); gl.glBlendEquation(GL.GL_FUNC_ADD); // default - gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA); renderer.rs.setHintMask(RenderState.BITHINT_BLENDING_ENABLED); } }; /** * Default {@link GL#GL_BLEND} <i>disable</i> {@link GLCallback}, - * simply turning off the {@link GL#GL_BLEND} state. + * simply turning-off the {@link GL#GL_BLEND} state + * and turning-on depth writing via {@link GL#glDepthMask(boolean)}. * <p> * Implementation also clears {@link RegionRenderer#getRenderState() RenderState}'s {@link RenderState#BITHINT_BLENDING_ENABLED blending bit-hint}. * </p> @@ -97,6 +104,7 @@ public class RegionRenderer { public void run(final GL gl, final RegionRenderer renderer) { renderer.rs.clearHintMask(RenderState.BITHINT_BLENDING_ENABLED); gl.glDisable(GL.GL_BLEND); + gl.glDepthMask(true); } }; @@ -160,7 +168,7 @@ public class RegionRenderer { * <p>Shall be called by a {@code draw()} method, e.g. {@link RegionRenderer#draw(GL2ES2, Region, int)}</p> * * @param gl referencing the current GLContext to which the ShaderState is bound to - * @param renderModes TODO + * @param renderModes * @throws GLException if initialization failed */ public final void init(final GL2ES2 gl, final int renderModes) throws GLException { @@ -270,6 +278,8 @@ public class RegionRenderer { private static String USE_COLOR_CHANNEL = "#define USE_COLOR_CHANNEL 1\n"; private static String USE_COLOR_TEXTURE = "#define USE_COLOR_TEXTURE 1\n"; private static String DEF_SAMPLE_COUNT = "#define SAMPLE_COUNT "; + private static String MAIN_BEGIN = "void main (void)\n{\n"; + private static final String gcuTexture2D = "gcuTexture2D"; private String getVersionedShaderName() { return "curverenderer01"; @@ -367,14 +377,16 @@ public class RegionRenderer { * @param pass1 * @param quality * @param sampleCount + * @param colorTexSeq * @return true if a new shader program is being used and hence external uniform-data and -location, * as well as the attribute-location must be updated, otherwise false. */ public final boolean useShaderProgram(final GL2ES2 gl, final int renderModes, - final boolean pass1, final int quality, final int sampleCount) { + final boolean pass1, final int quality, final int sampleCount, final TextureSequence colorTexSeq) { final ShaderModeSelector1 sel1 = pass1 ? ShaderModeSelector1.selectPass1(renderModes) : ShaderModeSelector1.selectPass2(renderModes, quality, sampleCount); final boolean isTwoPass = Region.isTwoPass( renderModes ); + final boolean isPass1ColorTexSeq = pass1 && null != colorTexSeq; final int shaderKey = sel1.ordinal() | ( HIGH_MASK & renderModes ) | ( isTwoPass ? TWO_PASS_BIT : 0 ); /** @@ -404,15 +416,40 @@ public class RegionRenderer { } final ShaderCode rsVp = ShaderCode.create(gl, GL2ES2.GL_VERTEX_SHADER, AttributeNames.class, SHADER_SRC_SUB, SHADER_BIN_SUB, vertexShaderName, true); final ShaderCode rsFp = ShaderCode.create(gl, GL2ES2.GL_FRAGMENT_SHADER, AttributeNames.class, SHADER_SRC_SUB, SHADER_BIN_SUB, versionedBaseName+"-segment-head", true); - int posVp = rsVp.defaultShaderCustomization(gl, true, true); + + int posVp = 0; + int posFp = 0; + + if( isPass1ColorTexSeq && GLES2.GL_TEXTURE_EXTERNAL_OES == colorTexSeq.getTextureTarget() ) { + if( !gl.isExtensionAvailable(GLExtensions.OES_EGL_image_external) ) { + throw new GLException(GLExtensions.OES_EGL_image_external+" requested but not available"); + } + } + boolean supressGLSLVersionES30 = false; + if( isPass1ColorTexSeq && GLES2.GL_TEXTURE_EXTERNAL_OES == colorTexSeq.getTextureTarget() ) { + if( Platform.OSType.ANDROID == Platform.getOSType() && gl.isGLES3() ) { + // Bug on Nexus 10, ES3 - Android 4.3, where + // GL_OES_EGL_image_external extension directive leads to a failure _with_ '#version 300 es' ! + // P0003: Extension 'GL_OES_EGL_image_external' not supported + supressGLSLVersionES30 = true; + } + } + posVp = rsVp.defaultShaderCustomization(gl, !supressGLSLVersionES30, true); // rsFp.defaultShaderCustomization(gl, true, true); - int posFp = rsFp.addGLSLVersion(gl); - if( gl.isGLES2() && ! gl.isGLES3() ) { + posFp = supressGLSLVersionES30 ? 0 : rsFp.addGLSLVersion(gl); + if( isPass1ColorTexSeq ) { + posFp = rsFp.insertShaderSource(0, posFp, colorTexSeq.getRequiredExtensionsShaderStub()); + } + if( pass1 && supressGLSLVersionES30 || ( gl.isGLES2() && !gl.isGLES3() ) ) { posFp = rsFp.insertShaderSource(0, posFp, ShaderCode.createExtensionDirective(GLExtensions.OES_standard_derivatives, ShaderCode.ENABLE)); } - final String rsFpDefPrecision = getFragmentShaderPrecision(gl); - if( null != rsFpDefPrecision ) { - rsFp.insertShaderSource(0, posFp, rsFpDefPrecision); + if( false ) { + final String rsFpDefPrecision = getFragmentShaderPrecision(gl); + if( null != rsFpDefPrecision ) { + posFp = rsFp.insertShaderSource(0, posFp, rsFpDefPrecision); + } + } else { + posFp = rsFp.addDefaultShaderPrecision(gl, posFp); } if( Region.hasColorChannel( renderModes ) ) { posVp = rsVp.insertShaderSource(0, posVp, USE_COLOR_CHANNEL); @@ -426,20 +463,35 @@ public class RegionRenderer { posFp = rsFp.insertShaderSource(0, posFp, DEF_SAMPLE_COUNT+sel1.sampleCount+"\n"); } + final String texLookupFuncName; + if( isPass1ColorTexSeq ) { + posFp = rsFp.insertShaderSource(0, posFp, "uniform "+colorTexSeq.getTextureSampler2DType()+" "+UniformNames.gcu_ColorTexUnit+";\n"); + texLookupFuncName = colorTexSeq.getTextureLookupFunctionName(gcuTexture2D); + posFp = rsFp.insertShaderSource(0, posFp, colorTexSeq.getTextureLookupFragmentShaderImpl()); + } else { + texLookupFuncName = null; + } + + posFp = rsFp.insertShaderSource(0, -1, MAIN_BEGIN); + final String passS = pass1 ? "-pass1-" : "-pass2-"; final String shaderSegment = versionedBaseName+passS+sel1.tech+sel1.sub+".glsl"; if(DEBUG) { System.err.printf("RegionRendererImpl01.useShaderProgram.1: segment %s%n", shaderSegment); } try { - posFp = rsFp.insertShaderSource(0, -1, AttributeNames.class, shaderSegment); + posFp = rsFp.insertShaderSource(0, posFp, AttributeNames.class, shaderSegment); } catch (IOException ioe) { throw new RuntimeException("Failed to read: "+shaderSegment, ioe); } if( 0 > posFp ) { throw new RuntimeException("Failed to read: "+shaderSegment); } - posFp = rsFp.insertShaderSource(0, -1, "}\n"); + posFp = rsFp.insertShaderSource(0, posFp, "}\n"); + + if( isPass1ColorTexSeq ) { + rsFp.replaceInShaderSource(gcuTexture2D, texLookupFuncName); + } sp = new ShaderProgram(); sp.add(rsVp); @@ -461,5 +513,4 @@ public class RegionRenderer { } return true; } - }
\ No newline at end of file diff --git a/src/jogl/classes/com/jogamp/graph/curve/opengl/RenderState.java b/src/jogl/classes/com/jogamp/graph/curve/opengl/RenderState.java index 818526cd7..7eb34a62d 100644 --- a/src/jogl/classes/com/jogamp/graph/curve/opengl/RenderState.java +++ b/src/jogl/classes/com/jogamp/graph/curve/opengl/RenderState.java @@ -53,9 +53,9 @@ public class RenderState { * Shall be set via {@link #setHintMask(int)} and cleared via {@link #clearHintMask(int)}. * </p> * <p> - * Due to alpha blending and multipass rendering, e.g. {@link Region#VBAA_RENDERING_BIT}, - * the clear-color shall be set to the {@link #getColorStaticUniform() foreground color} and <i>zero alpha</i>, - * otherwise blending will amplify the scene's clear-color. + * If set, {@link GLRegion#draw(GL2ES2, RegionRenderer, int[]) GLRegion's draw-method} + * will set the proper {@link GL#glBlendFuncSeparate(int, int, int, int) blend-function} + * and the clear-color to <i>transparent-black</i> in case of {@link Region#isTwoPass(int) multipass} FBO rendering. * </p> * <p> * Shall be called by custom code, e.g. via {@link RegionRenderer}'s diff --git a/src/jogl/classes/jogamp/graph/curve/opengl/VBORegion2PMSAAES2.java b/src/jogl/classes/jogamp/graph/curve/opengl/VBORegion2PMSAAES2.java index 33527381d..b52308e05 100644 --- a/src/jogl/classes/jogamp/graph/curve/opengl/VBORegion2PMSAAES2.java +++ b/src/jogl/classes/jogamp/graph/curve/opengl/VBORegion2PMSAAES2.java @@ -46,6 +46,7 @@ import com.jogamp.opengl.math.geom.AABBox; import com.jogamp.opengl.util.GLArrayDataServer; import com.jogamp.opengl.util.glsl.ShaderProgram; import com.jogamp.opengl.util.texture.Texture; +import com.jogamp.opengl.util.texture.TextureCoords; import com.jogamp.opengl.util.texture.TextureSequence; public class VBORegion2PMSAAES2 extends GLRegion { @@ -96,7 +97,7 @@ public class VBORegion2PMSAAES2 extends GLRegion { */ public void useShaderProgram(final GL2ES2 gl, final RegionRenderer renderer, final int renderModes, final boolean pass1, final int quality, final int sampleCount) { final RenderState rs = renderer.getRenderState(); - final boolean updateLocGlobal = renderer.useShaderProgram(gl, renderModes, pass1, quality, sampleCount); + final boolean updateLocGlobal = renderer.useShaderProgram(gl, renderModes, pass1, quality, sampleCount, colorTexSeq); final ShaderProgram sp = renderer.getRenderState().getShaderProgram(); final boolean updateLocLocal; if( pass1 ) { @@ -250,11 +251,22 @@ public class VBORegion2PMSAAES2 extends GLRegion { gca_ColorsAttr.seal(gl, true); gca_ColorsAttr.enableBuffer(gl, false); } - if( null != gcu_ColorTexUnit ) { - colorTexBBox[0] = box.getMinX(); - colorTexBBox[1] = box.getMinY(); - colorTexBBox[2] = box.getMaxX(); - colorTexBBox[3] = box.getMaxY(); + if( null != gcu_ColorTexUnit && colorTexSeq.isTextureAvailable() ) { + final TextureSequence.TextureFrame frame = colorTexSeq.getLastTexture(); + final Texture tex = frame.getTexture(); + final TextureCoords tc = tex.getImageTexCoords(); + final float tcSx = 1f / ( tc.right() - tc.left() ); + colorTexBBox[0] = box.getMinX() * tcSx; + colorTexBBox[2] = box.getMaxX() * tcSx; + if( tex.getMustFlipVertically() ) { + final float tcSy = 1f / ( tc.bottom() - tc.top() ); + colorTexBBox[1] = box.getMaxY() * tcSy; + colorTexBBox[3] = box.getMinY() * tcSy; + } else { + final float tcSy = 1f / ( tc.top() - tc.bottom() ); + colorTexBBox[1] = box.getMinY() * tcSy; + colorTexBBox[3] = box.getMaxY() * tcSy; + } } gca_FboVerticesAttr.seal(gl, false); { @@ -364,9 +376,8 @@ public class VBORegion2PMSAAES2 extends GLRegion { System.err.printf("XXX.Scale %d * [%f x %f]: %d x %d%n", sampleCount[0], winWidth, winHeight, targetFboWidth, targetFboHeight); } - if( hasDelta || fboDirty || null == fbo || ( fbo != null && fbo.getNumSamples() != sampleCount[0] ) ) { + if( hasDelta || fboDirty || isShapeDirty() || null == fbo || ( fbo != null && fbo.getNumSamples() != sampleCount[0] ) ) { // FIXME: rescale - final float minX = box.getMinX()-diffObjBorderWidth; final float minY = box.getMinY()-diffObjBorderHeight; final float maxX = box.getMaxX()+diffObjBorderWidth+diffObjWidth; @@ -384,10 +395,10 @@ public class VBORegion2PMSAAES2 extends GLRegion { FloatUtil.makeOrthof(pmvMatrix02, 0, true, minX, maxX, minY, maxY, -1, 1); useShaderProgram(gl, renderer, getRenderModes(), true, getQuality(), sampleCount[0]); renderRegion2FBO(gl, rs, targetFboWidth, targetFboHeight, vpWidth, vpHeight, sampleCount); - } else { - gca_FboTexCoordsAttr.setVBOWritten(false); + } else if( isStateDirty() ) { + useShaderProgram(gl, renderer, getRenderModes(), true, getQuality(), sampleCount[0]); + renderRegion2FBO(gl, rs, targetFboWidth, targetFboHeight, vpWidth, vpHeight, sampleCount); } - // System.out.println("Scale: " + matrix.glGetMatrixf().get(1+4*3) +" " + matrix.glGetMatrixf().get(2+4*3)); useShaderProgram(gl, renderer, getRenderModes(), false, getQuality(), sampleCount[0]); renderFBO(gl, rs, vpWidth, vpHeight, sampleCount[0]); } @@ -396,6 +407,11 @@ public class VBORegion2PMSAAES2 extends GLRegion { private void renderFBO(final GL2ES2 gl, final RenderState rs, final int width, final int height, final int sampleCount) { gl.glViewport(0, 0, width, height); + if( rs.isHintMaskSet(RenderState.BITHINT_BLENDING_ENABLED) ) { + // RGB is already multiplied w/ alpha via renderRegion2FBO(..) + gl.glBlendFunc(GL.GL_ONE, GL.GL_ONE_MINUS_SRC_ALPHA); + } + gl.glActiveTexture(GL.GL_TEXTURE0 + gcu_FboTexUnit.intValue()); fbo.use(gl, fbo.getSamplingSink()); @@ -419,6 +435,8 @@ public class VBORegion2PMSAAES2 extends GLRegion { throw new IllegalArgumentException("fboSize must be greater than 0: "+targetFboWidth+"x"+targetFboHeight); } + final boolean blendingEnabled = rs.isHintMaskSet(RenderState.BITHINT_BLENDING_ENABLED); + if(null == fbo) { fboWidth = targetFboWidth; fboHeight = targetFboHeight; @@ -433,7 +451,10 @@ public class VBORegion2PMSAAES2 extends GLRegion { // FIXME: shall not use bilinear (GL_LINEAR), due to MSAA ??? // ssink.attachTexture2D(gl, 0, true, GL2ES2.GL_LINEAR, GL2ES2.GL_LINEAR, GL2ES2.GL_CLAMP_TO_EDGE, GL2ES2.GL_CLAMP_TO_EDGE); ssink.attachTexture2D(gl, 0, true, GL2ES2.GL_NEAREST, GL2ES2.GL_NEAREST, GL2ES2.GL_CLAMP_TO_EDGE, GL2ES2.GL_CLAMP_TO_EDGE); - ssink.attachRenderbuffer(gl, Attachment.Type.DEPTH, 24); + if( !blendingEnabled ) { + // no depth-buffer w/ blending + ssink.attachRenderbuffer(gl, Attachment.Type.DEPTH, 24); + } } fbo.setSamplingSink(ssink); fbo.resetSamplingSink(gl); // validate @@ -453,9 +474,20 @@ public class VBORegion2PMSAAES2 extends GLRegion { //render texture gl.glViewport(0, 0, fboWidth, fboHeight); + if( blendingEnabled ) { + gl.glClearColor(0f, 0f, 0f, 0.0f); + gl.glClear(GL2ES2.GL_COLOR_BUFFER_BIT); // no depth-buffer w/ blending + // For already pre-multiplied alpha values, use: + // gl.glBlendFunc(GL.GL_ONE, GL.GL_ONE_MINUS_SRC_ALPHA); + + // Multiply RGB w/ Alpha, preserve alpha for renderFBO(..) + gl.glBlendFuncSeparate(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA, GL.GL_ONE, GL.GL_ONE_MINUS_SRC_ALPHA); + } else { + gl.glClear(GL2ES2.GL_COLOR_BUFFER_BIT | GL2ES2.GL_DEPTH_BUFFER_BIT); + } - gl.glClear(GL2ES2.GL_COLOR_BUFFER_BIT | GL2ES2.GL_DEPTH_BUFFER_BIT); renderRegion(gl); + fbo.unbind(gl); fboDirty = false; } @@ -468,7 +500,7 @@ public class VBORegion2PMSAAES2 extends GLRegion { gca_ColorsAttr.enableBuffer(gl, true); } indicesBuffer.bindBuffer(gl, true); // keeps VBO binding - if( null != gcu_ColorTexUnit ) { + if( null != gcu_ColorTexUnit && colorTexSeq.isTextureAvailable() ) { final TextureSequence.TextureFrame frame = colorTexSeq.getNextTexture(gl); gl.glActiveTexture(GL.GL_TEXTURE0 + colorTexSeq.getTextureUnit()); final Texture tex = frame.getTexture(); @@ -479,7 +511,7 @@ public class VBORegion2PMSAAES2 extends GLRegion { gcu_ColorTexUnit.setData(colorTexUnit); gl.glUniform(gcu_ColorTexUnit); } - gl.glUniform(gcu_ColorTexBBox); // FIXME: Only if changed! + gl.glUniform(gcu_ColorTexBBox); // Always update, since program maybe used by multiple regions gl.glDrawElements(GL2ES2.GL_TRIANGLES, indicesBuffer.getElementCount() * indicesBuffer.getComponentCount(), GL2ES2.GL_UNSIGNED_SHORT, 0); tex.disable(gl); // nop on core } else { diff --git a/src/jogl/classes/jogamp/graph/curve/opengl/VBORegion2PVBAAES2.java b/src/jogl/classes/jogamp/graph/curve/opengl/VBORegion2PVBAAES2.java index c623eddb7..cea292210 100644 --- a/src/jogl/classes/jogamp/graph/curve/opengl/VBORegion2PVBAAES2.java +++ b/src/jogl/classes/jogamp/graph/curve/opengl/VBORegion2PVBAAES2.java @@ -49,6 +49,7 @@ import com.jogamp.opengl.math.geom.AABBox; import com.jogamp.opengl.util.GLArrayDataServer; import com.jogamp.opengl.util.glsl.ShaderProgram; import com.jogamp.opengl.util.texture.Texture; +import com.jogamp.opengl.util.texture.TextureCoords; import com.jogamp.opengl.util.texture.TextureSequence; public class VBORegion2PVBAAES2 extends GLRegion { @@ -131,7 +132,7 @@ public class VBORegion2PVBAAES2 extends GLRegion { */ public void useShaderProgram(final GL2ES2 gl, final RegionRenderer renderer, final int renderModes, final boolean pass1, final int quality, final int sampleCount) { final RenderState rs = renderer.getRenderState(); - final boolean updateLocGlobal = renderer.useShaderProgram(gl, renderModes, pass1, quality, sampleCount); + final boolean updateLocGlobal = renderer.useShaderProgram(gl, renderModes, pass1, quality, sampleCount, colorTexSeq); final ShaderProgram sp = renderer.getRenderState().getShaderProgram(); final boolean updateLocLocal; if( pass1 ) { @@ -295,11 +296,22 @@ public class VBORegion2PVBAAES2 extends GLRegion { gca_ColorsAttr.seal(gl, true); gca_ColorsAttr.enableBuffer(gl, false); } - if( null != gcu_ColorTexUnit ) { - colorTexBBox[0] = box.getMinX(); - colorTexBBox[1] = box.getMinY(); - colorTexBBox[2] = box.getMaxX(); - colorTexBBox[3] = box.getMaxY(); + if( null != gcu_ColorTexUnit && colorTexSeq.isTextureAvailable() ) { + final TextureSequence.TextureFrame frame = colorTexSeq.getLastTexture(); + final Texture tex = frame.getTexture(); + final TextureCoords tc = tex.getImageTexCoords(); + final float tcSx = 1f / ( tc.right() - tc.left() ); + colorTexBBox[0] = box.getMinX() * tcSx; + colorTexBBox[2] = box.getMaxX() * tcSx; + if( tex.getMustFlipVertically() ) { + final float tcSy = 1f / ( tc.bottom() - tc.top() ); + colorTexBBox[1] = box.getMaxY() * tcSy; + colorTexBBox[3] = box.getMinY() * tcSy; + } else { + final float tcSy = 1f / ( tc.top() - tc.bottom() ); + colorTexBBox[1] = box.getMinY() * tcSy; + colorTexBBox[3] = box.getMaxY() * tcSy; + } } gca_FboVerticesAttr.seal(gl, false); { @@ -419,7 +431,7 @@ public class VBORegion2PVBAAES2 extends GLRegion { System.err.printf("XXX.Scale %d * [%f x %f]: %d x %d%n", sampleCount[0], winWidth, winHeight, targetFboWidth, targetFboHeight); } - if( hasDelta || fboDirty || null == fbo ) { + if( hasDelta || fboDirty || isShapeDirty() || null == fbo ) { final int maxLength = Math.max(targetFboWidth, targetFboHeight); if( maxLength > maxTexSize[0] ) { if( targetFboWidth > targetFboHeight ) { @@ -503,6 +515,9 @@ public class VBORegion2PVBAAES2 extends GLRegion { FloatUtil.makeOrthof(pmvMatrix02, 0, true, minX, maxX, minY, maxY, -1, 1); useShaderProgram(gl, renderer, getRenderModes(), true, getQuality(), sampleCount[0]); renderRegion2FBO(gl, rs, targetFboWidth, targetFboHeight, newFboWidth, newFboHeight, vpWidth, vpHeight, sampleCount[0]); + } else if( isStateDirty() ) { + useShaderProgram(gl, renderer, getRenderModes(), true, getQuality(), sampleCount[0]); + renderRegion2FBO(gl, rs, targetFboWidth, targetFboHeight, fboWidth, fboHeight, vpWidth, vpHeight, sampleCount[0]); } useShaderProgram(gl, renderer, getRenderModes(), false, getQuality(), sampleCount[0]); renderFBO(gl, rs, targetFboWidth, targetFboHeight, vpWidth, vpHeight, sampleCount[0]); @@ -513,6 +528,11 @@ public class VBORegion2PVBAAES2 extends GLRegion { final int vpWidth, final int vpHeight, final int sampleCount) { gl.glViewport(0, 0, vpWidth, vpHeight); + if( rs.isHintMaskSet(RenderState.BITHINT_BLENDING_ENABLED) ) { + // RGB is already multiplied w/ alpha via renderRegion2FBO(..) + gl.glBlendFunc(GL.GL_ONE, GL.GL_ONE_MINUS_SRC_ALPHA); + } + gl.glUniform(gcu_FboTexSize); gl.glActiveTexture(GL.GL_TEXTURE0 + gcu_FboTexUnit.intValue()); @@ -539,6 +559,8 @@ public class VBORegion2PVBAAES2 extends GLRegion { throw new IllegalArgumentException("fboSize must be greater than 0: "+targetFboWidth+"x"+targetFboHeight); } + final boolean blendingEnabled = rs.isHintMaskSet(RenderState.BITHINT_BLENDING_ENABLED); + if(null == fbo) { fboWidth = newFboWidth; fboHeight = newFboHeight; @@ -553,7 +575,10 @@ public class VBORegion2PVBAAES2 extends GLRegion { // FIXME: FXAA requires bilinear filtering! // texA = fbo.attachTexture2D(gl, 0, true, GL2ES2.GL_LINEAR, GL2ES2.GL_LINEAR, GL2ES2.GL_CLAMP_TO_EDGE, GL2ES2.GL_CLAMP_TO_EDGE); texA = fbo.attachTexture2D(gl, 0, true, GL2ES2.GL_NEAREST, GL2ES2.GL_NEAREST, GL2ES2.GL_CLAMP_TO_EDGE, GL2ES2.GL_CLAMP_TO_EDGE); - fbo.attachRenderbuffer(gl, Attachment.Type.DEPTH, 24); + if( !blendingEnabled ) { + // no depth-buffer w/ blending + fbo.attachRenderbuffer(gl, Attachment.Type.DEPTH, 24); + } if( DEBUG_FBO_1 ) { System.err.printf("XXX.createFBO: %dx%d%n%s%n", fboWidth, fboHeight, fbo.toString()); } @@ -576,7 +601,17 @@ public class VBORegion2PVBAAES2 extends GLRegion { //render texture gl.glViewport(0, 0, fboWidth, fboHeight); - gl.glClear(GL2ES2.GL_COLOR_BUFFER_BIT | GL2ES2.GL_DEPTH_BUFFER_BIT); + if( blendingEnabled ) { + gl.glClearColor(0f, 0f, 0f, 0.0f); + gl.glClear(GL2ES2.GL_COLOR_BUFFER_BIT); // no depth-buffer w/ blending + // For already pre-multiplied alpha values, use: + // gl.glBlendFunc(GL.GL_ONE, GL.GL_ONE_MINUS_SRC_ALPHA); + + // Multiply RGB w/ Alpha, preserve alpha for renderFBO(..) + gl.glBlendFuncSeparate(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA, GL.GL_ONE, GL.GL_ONE_MINUS_SRC_ALPHA); + } else { + gl.glClear(GL2ES2.GL_COLOR_BUFFER_BIT | GL2ES2.GL_DEPTH_BUFFER_BIT); + } renderRegion(gl); @@ -593,7 +628,7 @@ public class VBORegion2PVBAAES2 extends GLRegion { gca_ColorsAttr.enableBuffer(gl, true); } indicesBuffer.bindBuffer(gl, true); // keeps VBO binding - if( null != gcu_ColorTexUnit ) { + if( null != gcu_ColorTexUnit && colorTexSeq.isTextureAvailable() ) { final TextureSequence.TextureFrame frame = colorTexSeq.getNextTexture(gl); gl.glActiveTexture(GL.GL_TEXTURE0 + colorTexSeq.getTextureUnit()); final Texture tex = frame.getTexture(); @@ -604,7 +639,7 @@ public class VBORegion2PVBAAES2 extends GLRegion { gcu_ColorTexUnit.setData(colorTexUnit); gl.glUniform(gcu_ColorTexUnit); } - gl.glUniform(gcu_ColorTexBBox); // FIXME: Only if changed! + gl.glUniform(gcu_ColorTexBBox); // Always update, since program maybe used by multiple regions gl.glDrawElements(GL2ES2.GL_TRIANGLES, indicesBuffer.getElementCount() * indicesBuffer.getComponentCount(), GL2ES2.GL_UNSIGNED_SHORT, 0); tex.disable(gl); // nop on core } else { diff --git a/src/jogl/classes/jogamp/graph/curve/opengl/VBORegionSPES2.java b/src/jogl/classes/jogamp/graph/curve/opengl/VBORegionSPES2.java index 0ec139a0f..24d53c1a2 100644 --- a/src/jogl/classes/jogamp/graph/curve/opengl/VBORegionSPES2.java +++ b/src/jogl/classes/jogamp/graph/curve/opengl/VBORegionSPES2.java @@ -43,6 +43,7 @@ import com.jogamp.graph.curve.opengl.RenderState; import com.jogamp.opengl.util.GLArrayDataServer; import com.jogamp.opengl.util.glsl.ShaderProgram; import com.jogamp.opengl.util.texture.Texture; +import com.jogamp.opengl.util.texture.TextureCoords; import com.jogamp.opengl.util.texture.TextureSequence; public class VBORegionSPES2 extends GLRegion { @@ -149,11 +150,23 @@ public class VBORegionSPES2 extends GLRegion { gca_ColorsAttr.seal(gl, true); gca_ColorsAttr.enableBuffer(gl, false); } - if( null != gcu_ColorTexUnit ) { - colorTexBBox[0] = box.getMinX(); - colorTexBBox[1] = box.getMinY(); - colorTexBBox[2] = box.getMaxX(); - colorTexBBox[3] = box.getMaxY(); + if( null != gcu_ColorTexUnit && colorTexSeq.isTextureAvailable() ) { + final TextureSequence.TextureFrame frame = colorTexSeq.getLastTexture(); + final Texture tex = frame.getTexture(); + final TextureCoords tc = tex.getImageTexCoords(); + final float tcSx = 1f / ( tc.right() - tc.left() ); + colorTexBBox[0] = box.getMinX() * tcSx; + colorTexBBox[2] = box.getMaxX() * tcSx; + final float tcSy; + if( tex.getMustFlipVertically() ) { + tcSy = 1f / ( tc.bottom() - tc.top() ); + colorTexBBox[1] = box.getMaxY() * tcSy; + colorTexBBox[3] = box.getMinY() * tcSy; + } else { + tcSy = 1f / ( tc.top() - tc.bottom() ); + colorTexBBox[1] = box.getMinY() * tcSy; + colorTexBBox[3] = box.getMaxY() * tcSy; + } } indicesBuffer.seal(gl, true); indicesBuffer.enableBuffer(gl, false); @@ -164,6 +177,7 @@ public class VBORegionSPES2 extends GLRegion { } } + private static final boolean throwOnError = false; // FIXME /** * <p> * Since multiple {@link Region}s may share one @@ -177,7 +191,7 @@ public class VBORegionSPES2 extends GLRegion { */ public void useShaderProgram(final GL2ES2 gl, final RegionRenderer renderer, final int renderModes, final int quality) { final RenderState rs = renderer.getRenderState(); - final boolean updateLocGlobal = renderer.useShaderProgram(gl, renderModes, true, quality, 0); + final boolean updateLocGlobal = renderer.useShaderProgram(gl, renderModes, true, quality, 0, colorTexSeq); final ShaderProgram sp = renderer.getRenderState().getShaderProgram(); final boolean updateLocLocal = !sp.equals(spPass1); spPass1 = sp; @@ -185,16 +199,16 @@ public class VBORegionSPES2 extends GLRegion { System.err.println("XXX changedSP.p1 updateLocation loc "+updateLocLocal+" / glob "+updateLocGlobal); } if( updateLocLocal ) { - rs.updateAttributeLoc(gl, true, gca_VerticesAttr, true); - rs.updateAttributeLoc(gl, true, gca_CurveParamsAttr, true); + rs.updateAttributeLoc(gl, true, gca_VerticesAttr, throwOnError); + rs.updateAttributeLoc(gl, true, gca_CurveParamsAttr, throwOnError); if( null != gca_ColorsAttr ) { - rs.updateAttributeLoc(gl, true, gca_ColorsAttr, true); + rs.updateAttributeLoc(gl, true, gca_ColorsAttr, throwOnError); } } - rsLocal.update(gl, rs, updateLocLocal, renderModes, true, true); + rsLocal.update(gl, rs, updateLocLocal, renderModes, true, throwOnError); if( null != gcu_ColorTexUnit ) { - rs.updateUniformLoc(gl, updateLocLocal, gcu_ColorTexUnit, true); - rs.updateUniformLoc(gl, updateLocLocal, gcu_ColorTexBBox, true); + rs.updateUniformLoc(gl, updateLocLocal, gcu_ColorTexUnit, throwOnError); + rs.updateUniformLoc(gl, updateLocLocal, gcu_ColorTexBBox, throwOnError); } } @@ -216,7 +230,12 @@ public class VBORegionSPES2 extends GLRegion { gca_ColorsAttr.enableBuffer(gl, true); } indicesBuffer.bindBuffer(gl, true); // keeps VBO binding - if( null != gcu_ColorTexUnit ) { + + if( renderer.getRenderState().isHintMaskSet(RenderState.BITHINT_BLENDING_ENABLED) ) { + gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA); + } + + if( null != gcu_ColorTexUnit && colorTexSeq.isTextureAvailable() ) { final TextureSequence.TextureFrame frame = colorTexSeq.getNextTexture(gl); gl.glActiveTexture(GL.GL_TEXTURE0 + colorTexSeq.getTextureUnit()); final Texture tex = frame.getTexture(); diff --git a/src/jogl/classes/jogamp/graph/curve/opengl/shader/curverenderer01-pass1-curve_simple.glsl b/src/jogl/classes/jogamp/graph/curve/opengl/shader/curverenderer01-pass1-curve_simple.glsl index 83b312a0b..447242438 100644 --- a/src/jogl/classes/jogamp/graph/curve/opengl/shader/curverenderer01-pass1-curve_simple.glsl +++ b/src/jogl/classes/jogamp/graph/curve/opengl/shader/curverenderer01-pass1-curve_simple.glsl @@ -2,9 +2,9 @@ if( gcv_CurveParam.x == 0.0 && gcv_CurveParam.y == 0.0 ) { // pass-1: Lines #if defined(USE_COLOR_TEXTURE) && defined(USE_COLOR_CHANNEL) - mgl_FragColor = texture2D(gcu_ColorTexUnit, gcv_ColorTexCoord.st) * gcv_Color * gcu_ColorStatic; + mgl_FragColor = gcuTexture2D(gcu_ColorTexUnit, gcv_ColorTexCoord.st) * gcv_Color * gcu_ColorStatic; #elif defined(USE_COLOR_TEXTURE) - mgl_FragColor = texture2D(gcu_ColorTexUnit, gcv_ColorTexCoord.st) * gcu_ColorStatic; + mgl_FragColor = gcuTexture2D(gcu_ColorTexUnit, gcv_ColorTexCoord.st) * gcu_ColorStatic; #elif defined(USE_COLOR_CHANNEL) mgl_FragColor = gcv_Color * gcu_ColorStatic; #else @@ -22,10 +22,10 @@ float a = clamp(0.5 - ( position/length(f) ) * sign(gcv_CurveParam.y), 0.0, 1.0); #if defined(USE_COLOR_TEXTURE) && defined(USE_COLOR_CHANNEL) - vec4 t = texture2D(gcu_ColorTexUnit, gcv_ColorTexCoord.st); + vec4 t = gcuTexture2D(gcu_ColorTexUnit, gcv_ColorTexCoord.st); mgl_FragColor = vec4(t.rgb * gcv_Color.rgb * gcu_ColorStatic.rgb, t.a * gcv_Color.a * gcu_ColorStatic.a * a); #elif defined(USE_COLOR_TEXTURE) - vec4 t = texture2D(gcu_ColorTexUnit, gcv_ColorTexCoord.st); + vec4 t = gcuTexture2D(gcu_ColorTexUnit, gcv_ColorTexCoord.st); mgl_FragColor = vec4(t.rgb * gcu_ColorStatic.rgb, t.a * gcu_ColorStatic.a * a); #elif defined(USE_COLOR_CHANNEL) mgl_FragColor = vec4(gcv_Color.rgb * gcu_ColorStatic.rgb, gcv_Color.a * gcu_ColorStatic.a * a); diff --git a/src/jogl/classes/jogamp/graph/curve/opengl/shader/curverenderer01-pass1-curve_simple.orig.glsl b/src/jogl/classes/jogamp/graph/curve/opengl/shader/curverenderer01-pass1-curve_simple.orig.glsl index 92f0fc07b..fa2608365 100644 --- a/src/jogl/classes/jogamp/graph/curve/opengl/shader/curverenderer01-pass1-curve_simple.orig.glsl +++ b/src/jogl/classes/jogamp/graph/curve/opengl/shader/curverenderer01-pass1-curve_simple.orig.glsl @@ -3,7 +3,11 @@ if( gcv_CurveParam.x == 0.0 && gcv_CurveParam.y == 0.0 ) { // pass-1: Lines -#ifdef USE_COLOR_CHANNEL +#if defined(USE_COLOR_TEXTURE) && defined(USE_COLOR_CHANNEL) + mgl_FragColor = gcuTexture2D(gcu_ColorTexUnit, gcv_ColorTexCoord.st) * gcv_Color * gcu_ColorStatic; +#elif defined(USE_COLOR_TEXTURE) + mgl_FragColor = gcuTexture2D(gcu_ColorTexUnit, gcv_ColorTexCoord.st) * gcu_ColorStatic; +#elif defined(USE_COLOR_CHANNEL) mgl_FragColor = gcv_Color * gcu_ColorStatic; #else mgl_FragColor = gcu_ColorStatic; @@ -25,7 +29,13 @@ float position = rtex.y - (rtex.x * (1.0 - rtex.x)); float a = clamp(0.5 - ( position/length(f) ) * sign(gcv_CurveParam.y), 0.0, 1.0); -#ifdef USE_COLOR_CHANNEL +#if defined(USE_COLOR_TEXTURE) && defined(USE_COLOR_CHANNEL) + vec4 t = gcuTexture2D(gcu_ColorTexUnit, gcv_ColorTexCoord.st); + mgl_FragColor = vec4(t.rgb * gcv_Color.rgb * gcu_ColorStatic.rgb, t.a * gcv_Color.a * gcu_ColorStatic.a * a); +#elif defined(USE_COLOR_TEXTURE) + vec4 t = gcuTexture2D(gcu_ColorTexUnit, gcv_ColorTexCoord.st); + mgl_FragColor = vec4(t.rgb * gcu_ColorStatic.rgb, t.a * gcu_ColorStatic.a * a); +#elif defined(USE_COLOR_CHANNEL) mgl_FragColor = vec4(gcv_Color.rgb * gcu_ColorStatic.rgb, gcv_Color.a * gcu_ColorStatic.a * a); #else mgl_FragColor = vec4(gcu_ColorStatic.rgb, gcu_ColorStatic.a * a); diff --git a/src/jogl/classes/jogamp/graph/curve/opengl/shader/curverenderer01-segment-head.fp b/src/jogl/classes/jogamp/graph/curve/opengl/shader/curverenderer01-segment-head.fp index 74a1dea4e..26e2bcf24 100644 --- a/src/jogl/classes/jogamp/graph/curve/opengl/shader/curverenderer01-segment-head.fp +++ b/src/jogl/classes/jogamp/graph/curve/opengl/shader/curverenderer01-segment-head.fp @@ -17,6 +17,3 @@ #define GetSample(texUnit, texCoord, psize, cx, cy, offX, offY) texture2D(texUnit, texCoord + psize * vec2(cx+offX, cy+offY)) -void main (void) -{ - diff --git a/src/jogl/classes/jogamp/graph/curve/opengl/shader/uniforms.glsl b/src/jogl/classes/jogamp/graph/curve/opengl/shader/uniforms.glsl index ae7fa8490..cd014b732 100644 --- a/src/jogl/classes/jogamp/graph/curve/opengl/shader/uniforms.glsl +++ b/src/jogl/classes/jogamp/graph/curve/opengl/shader/uniforms.glsl @@ -7,7 +7,6 @@ uniform vec4 gcu_ColorStatic; uniform float gcu_Weight; #ifdef USE_COLOR_TEXTURE - uniform sampler2D gcu_ColorTexUnit; uniform vec4 gcu_ColorTexBBox; #endif diff --git a/src/test/com/jogamp/opengl/test/junit/graph/demos/GPUTextRendererListenerBase01.java b/src/test/com/jogamp/opengl/test/junit/graph/demos/GPUTextRendererListenerBase01.java index 5704f3e7a..ad12e11e1 100644 --- a/src/test/com/jogamp/opengl/test/junit/graph/demos/GPUTextRendererListenerBase01.java +++ b/src/test/com/jogamp/opengl/test/junit/graph/demos/GPUTextRendererListenerBase01.java @@ -202,7 +202,7 @@ public abstract class GPUTextRendererListenerBase01 extends GPURendererListenerB final int height = drawable.getHeight(); GL2ES2 gl = drawable.getGL().getGL2ES2(); - gl.glClearColor(1.0f, 1.0f, 1.0f, 0.0f); + gl.glClearColor(1.0f, 1.0f, 1.0f, 1.0f); gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); // final float zDistance0 = 500f; @@ -217,9 +217,6 @@ public abstract class GPUTextRendererListenerBase01 extends GPURendererListenerB pmv.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); pmv.glLoadIdentity(); rs.setColorStatic(0.1f, 0.1f, 0.1f, 1.0f); - if( renderer.getRenderState().isHintMaskSet(RenderState.BITHINT_BLENDING_ENABLED) ) { - gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - } final float pixelSizeFName = font.getPixelSize(fontSizeFName, dpiH); final float pixelSizeHead = font.getPixelSize(fontSizeHead, dpiH); final float pixelSizeBottom = font.getPixelSize(fontSizeBottom, dpiH); @@ -281,22 +278,21 @@ public abstract class GPUTextRendererListenerBase01 extends GPURendererListenerB pmv.glTranslatef(getXTran(), getYTran(), getZTran()); pmv.glRotatef(getAngle(), 0, 1, 0); rs.setColorStatic(0.9f, 0.0f, 0.0f, 1.0f); - if( renderer.getRenderState().isHintMaskSet(RenderState.BITHINT_BLENDING_ENABLED) ) { - gl.glClearColor(1.0f, 0.0f, 0.0f, 0.0f); - } if( bottomTextUseFrustum ) { regionBottom.setFrustum(pmv.glGetFrustum()); } if(!userInput) { if( bottomTextUseFrustum ) { - TextRegionUtil.drawString3D(gl, regionBottom, renderer, font, nearPlaneS * pixelSizeBottom, text2, null, getSampleCount(), null, null); + TextRegionUtil.drawString3D(gl, regionBottom, renderer, font, nearPlaneS * pixelSizeBottom, text2, null, getSampleCount(), + textRegionUtil.tempT1, textRegionUtil.tempT2); } else { textRegionUtil.drawString3D(gl, renderer, font, nearPlaneS * pixelSizeBottom, text2, null, getSampleCount()); } } else { if( bottomTextUseFrustum ) { - TextRegionUtil.drawString3D(gl, regionBottom, renderer, font, nearPlaneS * pixelSizeBottom, userString.toString(), null, getSampleCount(), null, null); + TextRegionUtil.drawString3D(gl, regionBottom, renderer, font, nearPlaneS * pixelSizeBottom, userString.toString(), null, getSampleCount(), + textRegionUtil.tempT1, textRegionUtil.tempT2); } else { textRegionUtil.drawString3D(gl, renderer, font, nearPlaneS * pixelSizeBottom, userString.toString(), null, getSampleCount()); } diff --git a/src/test/com/jogamp/opengl/test/junit/graph/demos/GPUUISceneGLListener0A.java b/src/test/com/jogamp/opengl/test/junit/graph/demos/GPUUISceneGLListener0A.java index f9f9d58a0..211e4c387 100644 --- a/src/test/com/jogamp/opengl/test/junit/graph/demos/GPUUISceneGLListener0A.java +++ b/src/test/com/jogamp/opengl/test/junit/graph/demos/GPUUISceneGLListener0A.java @@ -2,6 +2,8 @@ package com.jogamp.opengl.test.junit.graph.demos; import java.io.File; import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; @@ -22,8 +24,10 @@ import com.jogamp.graph.geom.SVertex; import com.jogamp.newt.MonitorDevice; import com.jogamp.newt.Window; import com.jogamp.newt.event.InputEvent; -import com.jogamp.newt.event.MouseAdapter; import com.jogamp.newt.event.MouseEvent; +import com.jogamp.newt.event.PinchToZoomGesture; +import com.jogamp.newt.event.GestureHandler.GestureEvent; +import com.jogamp.newt.event.MouseEvent.PointerClass; import com.jogamp.newt.opengl.GLWindow; import com.jogamp.opengl.math.FloatUtil; import com.jogamp.opengl.math.VectorUtil; @@ -34,8 +38,12 @@ import com.jogamp.opengl.test.junit.graph.demos.ui.RoundButton; import com.jogamp.opengl.test.junit.graph.demos.ui.SceneUIController; import com.jogamp.opengl.test.junit.graph.demos.ui.TextureButton; import com.jogamp.opengl.test.junit.graph.demos.ui.UIShape; -import com.jogamp.opengl.test.junit.jogl.demos.TextureSequenceDemo01; import com.jogamp.opengl.util.GLReadBufferUtil; +import com.jogamp.opengl.util.av.GLMediaPlayer; +import com.jogamp.opengl.util.av.GLMediaPlayerFactory; +import com.jogamp.opengl.util.av.GLMediaPlayer.GLMediaEventListener; +import com.jogamp.opengl.util.av.GLMediaPlayer.StreamException; +import com.jogamp.opengl.util.texture.TextureSequence.TextureFrame; public class GPUUISceneGLListener0A implements GLEventListener { @@ -168,7 +176,7 @@ public class GPUUISceneGLListener0A implements GLEventListener { LabelButton button = new LabelButton(SVertex.factory(), renderModes, font, "Next Text", buttonXSize, buttonYSize, labelZOffset); button.translate(xstart,ystart-diffY*buttons.size(), 0f); - button.addMouseListener(new MouseAdapter() { + button.addMouseListener(new UIShape.MouseGestureAdapter() { @Override public void mouseClicked(MouseEvent e) { if( null != labels[currentText] ) { @@ -186,8 +194,8 @@ public class GPUUISceneGLListener0A implements GLEventListener { button.setName(100); // FIXME: DEBUG tag button.translate(xstart,ystart - diffY*buttons.size(), 0f); button.setToggleable(true); - button.setToggleState(fpsLabel.isEnabled()); - button.addMouseListener(new MouseAdapter() { + button.setToggle(fpsLabel.isEnabled()); + button.addMouseListener(new UIShape.MouseGestureAdapter() { @Override public void mouseClicked(MouseEvent e) { final GLAnimatorControl a = cDrawable.getAnimator(); @@ -202,8 +210,8 @@ public class GPUUISceneGLListener0A implements GLEventListener { button = new LabelButton(SVertex.factory(), renderModes, font, "v-sync", buttonXSize, buttonYSize, labelZOffset); button.translate(xstart,ystart - diffY*buttons.size(), 0f); button.setToggleable(true); - button.setToggleState(gl.getSwapInterval()>0); - button.addMouseListener(new MouseAdapter() { + button.setToggle(gl.getSwapInterval()>0); + button.addMouseListener(new UIShape.MouseGestureAdapter() { @Override public void mouseClicked(MouseEvent e) { cDrawable.invoke(false, new GLRunnable() { @@ -224,13 +232,13 @@ public class GPUUISceneGLListener0A implements GLEventListener { button = new LabelButton(SVertex.factory(), renderModes, font, "< tilt >", buttonXSize, buttonYSize, labelZOffset); button.translate(xstart,ystart - diffY*buttons.size(), 0f); - button.addMouseListener(new MouseAdapter() { + button.addMouseListener(new UIShape.MouseGestureAdapter() { @Override public void mouseClicked(MouseEvent e) { final Object attachment = e.getAttachment(); - if( attachment instanceof UIShape.EventDetails ) { - final UIShape.EventDetails shapeEvent = (UIShape.EventDetails)attachment; - if( shapeEvent.rotPosition[0] < shapeEvent.rotBounds.getCenter()[0] ) { + if( attachment instanceof UIShape.PointerEventInfo ) { + final UIShape.PointerEventInfo shapeEvent = (UIShape.PointerEventInfo)attachment; + if( shapeEvent.objPos[0] < shapeEvent.shape.getBounds().getCenter()[0] ) { rotateButtons(new float[] { 0f, -5f, 0f}); // left-half pressed } else { rotateButtons(new float[] { 0f, 5f, 0f}); // right-half pressed @@ -246,14 +254,14 @@ public class GPUUISceneGLListener0A implements GLEventListener { if( pass2Mode ) { // second column to the left button = new LabelButton(SVertex.factory(), renderModes, font, "< samples >", buttonXSize, buttonYSize, labelZOffset); button.translate(xstart,ystart - diffY*buttons.size(), 0f); - button.addMouseListener(new MouseAdapter() { + button.addMouseListener(new UIShape.MouseGestureAdapter() { @Override public void mouseClicked(MouseEvent e) { final Object attachment = e.getAttachment(); - if( attachment instanceof UIShape.EventDetails ) { - final UIShape.EventDetails shapeEvent = (UIShape.EventDetails)attachment; + if( attachment instanceof UIShape.PointerEventInfo ) { + final UIShape.PointerEventInfo shapeEvent = (UIShape.PointerEventInfo)attachment; int sampleCount = sceneUIController.getSampleCount(); - if( shapeEvent.rotPosition[0] < shapeEvent.rotBounds.getCenter()[0] ) { + if( shapeEvent.objPos[0] < shapeEvent.shape.getBounds().getCenter()[0] ) { // left-half pressed if( sampleCount > 0 ) { sampleCount-=1; @@ -272,15 +280,15 @@ public class GPUUISceneGLListener0A implements GLEventListener { button = new LabelButton(SVertex.factory(), renderModes, font, "< quality >", buttonXSize, buttonYSize, labelZOffset); button.translate(xstart,ystart - diffY*buttons.size(), 0f); - button.addMouseListener(new MouseAdapter() { + button.addMouseListener(new UIShape.MouseGestureAdapter() { @Override public void mouseClicked(MouseEvent e) { final Object attachment = e.getAttachment(); - if( attachment instanceof UIShape.EventDetails ) { - final UIShape.EventDetails shapeEvent = (UIShape.EventDetails)attachment; + if( attachment instanceof UIShape.PointerEventInfo ) { + final UIShape.PointerEventInfo shapeEvent = (UIShape.PointerEventInfo)attachment; int quality = shapeEvent.shape.getQuality(); - if( shapeEvent.rotPosition[0] < shapeEvent.rotBounds.getCenter()[0] ) { + if( shapeEvent.objPos[0] < shapeEvent.shape.getBounds().getCenter()[0] ) { // left-half pressed if( quality > 0 ) { quality--; @@ -300,9 +308,10 @@ public class GPUUISceneGLListener0A implements GLEventListener { button = new LabelButton(SVertex.factory(), renderModes, font, "Quit", buttonXSize, buttonYSize, labelZOffset); button.translate(xstart,ystart - diffY*buttons.size(), 0f); - button.setColor(0.8f, 0.0f, 0.0f, 1.0f); - button.setSelectedColorMod(0.8f, 0.8f, 0.8f, 1.0f); - button.addMouseListener(new MouseAdapter() { + button.setColor(0.7f, 0.0f, 0.0f, 1.0f); + button.setLabelColor(1.2f, 1.2f, 1.2f); + button.setPressedColorMod(1.1f, 0.0f, 0.0f, 1.0f); + button.addMouseListener(new UIShape.MouseGestureAdapter() { @Override public void mouseClicked(MouseEvent e) { new Thread() { @@ -325,7 +334,7 @@ public class GPUUISceneGLListener0A implements GLEventListener { int k = 0; // row button = new LabelButton(SVertex.factory(), renderModes, font, "y flip", buttonXSize, buttonYSize, labelZOffset); button.translate(xstart - diffX*j,ystart - diffY*k, 0f); - button.addMouseListener(new MouseAdapter() { + button.addMouseListener(new UIShape.MouseGestureAdapter() { @Override public void mouseClicked(MouseEvent e) { rotateButtons(new float[] { 0f, 180f, 0f}); @@ -336,7 +345,7 @@ public class GPUUISceneGLListener0A implements GLEventListener { k++; button = new LabelButton(SVertex.factory(), renderModes, font, "x flip", buttonXSize, buttonYSize, labelZOffset); button.translate(xstart - diffX*j,ystart - diffY*k, 0f); - button.addMouseListener(new MouseAdapter() { + button.addMouseListener(new UIShape.MouseGestureAdapter() { @Override public void mouseClicked(MouseEvent e) { rotateButtons(new float[] { 180f, 0f, 0f}); @@ -347,18 +356,18 @@ public class GPUUISceneGLListener0A implements GLEventListener { button = new LabelButton(SVertex.factory(), renderModes, font, "+", buttonXSize, buttonYSize, labelZOffset); button.translate(xstart - diffX*j,ystart - diffY*k, 0f); - button.addMouseListener(new MouseAdapter() { + button.addMouseListener(new UIShape.MouseGestureAdapter() { @Override public void mouseClicked(MouseEvent e) { final Object attachment = e.getAttachment(); - if( attachment instanceof UIShape.EventDetails ) { - final UIShape.EventDetails shapeEvent = (UIShape.EventDetails)attachment; + if( attachment instanceof UIShape.PointerEventInfo ) { + final UIShape.PointerEventInfo shapeEvent = (UIShape.PointerEventInfo)attachment; // rel position to center - final float dx = shapeEvent.rotPosition[0] - shapeEvent.rotBounds.getCenter()[0] ; - final float dy = shapeEvent.rotPosition[1] - shapeEvent.rotBounds.getCenter()[1] ; + final float dx = shapeEvent.objPos[0] - shapeEvent.shape.getBounds().getCenter()[0] ; + final float dy = shapeEvent.objPos[1] - shapeEvent.shape.getBounds().getCenter()[1] ; // per-cent position to center (remove dependency on dimension) - final float awdx = Math.abs(dx)/shapeEvent.rotBounds.getWidth(); - final float awdy = Math.abs(dy)/shapeEvent.rotBounds.getHeight(); + final float awdx = Math.abs(dx)/shapeEvent.shape.getBounds().getWidth(); + final float awdy = Math.abs(dy)/shapeEvent.shape.getBounds().getHeight(); float tx = 0, ty = 0; if ( awdx > awdy ) { tx = dx < 0 ? -5 : 5; @@ -374,14 +383,14 @@ public class GPUUISceneGLListener0A implements GLEventListener { button = new LabelButton(SVertex.factory(), renderModes, font, "< space >", buttonXSize, buttonYSize, labelZOffset); button.translate(xstart - diffX*j,ystart - diffY*k, 0f); - button.addMouseListener(new MouseAdapter() { + button.addMouseListener(new UIShape.MouseGestureAdapter() { @Override public void mouseClicked(MouseEvent e) { final Object attachment = e.getAttachment(); - if( attachment instanceof UIShape.EventDetails ) { - final UIShape.EventDetails shapeEvent = (UIShape.EventDetails)attachment; + if( attachment instanceof UIShape.PointerEventInfo ) { + final UIShape.PointerEventInfo shapeEvent = (UIShape.PointerEventInfo)attachment; final float dx, dy; - if( shapeEvent.rotPosition[0] < shapeEvent.rotBounds.getCenter()[0] ) { + if( shapeEvent.objPos[0] < shapeEvent.shape.getBounds().getCenter()[0] ) { dx=-0.01f; dy=-0.005f; } else { dx=0.01f; dy=0.005f; @@ -398,14 +407,14 @@ public class GPUUISceneGLListener0A implements GLEventListener { button = new LabelButton(SVertex.factory(), renderModes, font, "< corner >", buttonXSize, buttonYSize, labelZOffset); button.translate(xstart - diffX*j,ystart - diffY*k, 0f); - button.addMouseListener(new MouseAdapter() { + button.addMouseListener(new UIShape.MouseGestureAdapter() { @Override public void mouseClicked(MouseEvent e) { final Object attachment = e.getAttachment(); - if( attachment instanceof UIShape.EventDetails ) { - final UIShape.EventDetails shapeEvent = (UIShape.EventDetails)attachment; + if( attachment instanceof UIShape.PointerEventInfo ) { + final UIShape.PointerEventInfo shapeEvent = (UIShape.PointerEventInfo)attachment; final float dc; - if( shapeEvent.rotPosition[0] < shapeEvent.rotBounds.getCenter()[0] ) { + if( shapeEvent.objPos[0] < shapeEvent.shape.getBounds().getCenter()[0] ) { dc=-0.1f; } else { dc=0.1f; @@ -423,7 +432,7 @@ public class GPUUISceneGLListener0A implements GLEventListener { button = new LabelButton(SVertex.factory(), renderModes, font, "reset", buttonXSize, buttonYSize, labelZOffset); button.translate(xstart - diffX*j,ystart - diffY*k, 0f); - button.addMouseListener(new MouseAdapter() { + button.addMouseListener(new UIShape.MouseGestureAdapter() { @Override public void mouseClicked(MouseEvent e) { resetButtons(); @@ -434,7 +443,7 @@ public class GPUUISceneGLListener0A implements GLEventListener { button = new LabelButton(SVertex.factory(), renderModes, font, "screenshot", buttonXSize, buttonYSize, labelZOffset); button.translate(xstart - diffX*j,ystart - diffY*k, 0f); - button.addMouseListener(new MouseAdapter() { + button.addMouseListener(new UIShape.MouseGestureAdapter() { @Override public void mouseClicked(MouseEvent e) { cDrawable.invoke(false, new GLRunnable() { @@ -451,15 +460,80 @@ public class GPUUISceneGLListener0A implements GLEventListener { } if(true) { - final TextureSequenceDemo01 texSeq = new TextureSequenceDemo01(true); - gl.glActiveTexture(GL.GL_TEXTURE0 + texSeq.getTextureUnit()); - texSeq.initGLResources(gl); + final GLMediaPlayer mPlayer = GLMediaPlayerFactory.createDefault(); final TextureButton texButton = new TextureButton(renderer.getRenderState().getVertexFactory(), renderModes, - 2f*buttonXSize, 2f*buttonYSize, texSeq); + 2f*buttonXSize, 2f*buttonYSize, mPlayer) { + @Override + protected void destroyImpl(GL2ES2 gl, RegionRenderer renderer) { + mPlayer.destroy(gl); + } + @Override + public void drawShape(GL2ES2 gl, RegionRenderer renderer, int[] sampleCount) { + if( GLMediaPlayer.State.Initialized == mPlayer.getState() ) { + try { + System.err.println("XXX InitGL.pre: "+mPlayer); + mPlayer.initGL(gl); + mPlayer.setAudioVolume( 0f ); + System.err.println("XXX Play.pre: "+mPlayer); + GLMediaPlayer.State r = mPlayer.play(); + System.err.println("XXX Play.post: "+r+", "+mPlayer); + markStateDirty(); + } catch (Exception e) { + e.printStackTrace(); + } + } + super.drawShape(gl, renderer, sampleCount); + markStateDirty(); // keep on going + } }; + texButton.setEnabled(false); // wait until data is avail. (shader) texButton.translate(xstart + diffX*5, ystart - diffY*1, 0f); texButton.setToggleable(true); + texButton.setToggle(false); // toggle == false -> mute audio + texButton.setToggleOffColorMod(0f, 1f, 0f, 1.0f); texButton.addMouseListener(dragZoomRotateListener); + texButton.addMouseListener(new UIShape.MouseGestureAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + mPlayer.setAudioVolume( texButton.isToggleOn() ? 1f : 0f ); + } } ); buttons.add(texButton); + mPlayer.addEventListener(new GLMediaEventListener() { + @Override + public void newFrameAvailable(GLMediaPlayer ts, TextureFrame newFrame, long when) { + // texButton.markStateDirty(); + } + + @Override + public void attributesChanged(final GLMediaPlayer mp, int event_mask, long when) { + System.err.println("MovieCube AttributesChanges: events_mask 0x"+Integer.toHexString(event_mask)+", when "+when); + System.err.println("MovieCube State: "+mp); + if( 0 != ( GLMediaEventListener.EVENT_CHANGE_INIT & event_mask ) ) { + texButton.setEnabled(true); // data and shader is available .. + } + if( 0 != ( GLMediaEventListener.EVENT_CHANGE_SIZE & event_mask ) ) { + // FIXME: mPlayer.resetGLState(); + } + if( 0 != ( GLMediaEventListener.EVENT_CHANGE_EOS & event_mask ) ) { + new Thread() { + public void run() { + // loop for-ever .. + mPlayer.seek(0); + mPlayer.play(); + } }.start(); + } else if( 0 != ( GLMediaEventListener.EVENT_CHANGE_ERR & event_mask ) ) { + final StreamException se = mPlayer.getStreamException(); + if( null != se ) { + se.printStackTrace(); + } + } + } + }); + try { + final URI streamLoc = new URI("http://archive.org/download/BigBuckBunny_328/BigBuckBunny_512kb.mp4"); + mPlayer.initStream(streamLoc, GLMediaPlayer.STREAM_ID_AUTO, GLMediaPlayer.STREAM_ID_AUTO, GLMediaPlayer.TEXTURE_COUNT_DEFAULT); + } catch (URISyntaxException e1) { + e1.printStackTrace(); + } } } @@ -613,7 +687,7 @@ public class GPUUISceneGLListener0A implements GLEventListener { System.err.println("GPUUISceneGLListener0A: dispose (0)"); } - sceneUIController.dispose(drawable); + sceneUIController.dispose(drawable); // disposes all registered UIShapes GL2ES2 gl = drawable.getGL().getGL2ES2(); renderer.destroy(gl); @@ -735,8 +809,8 @@ public class GPUUISceneGLListener0A implements GLEventListener { * We can share this instance w/ all UI elements, * since only mouse action / gesture is complete for a single one (press, drag, released and click). */ - private final MouseAdapter dragZoomRotateListener = new MouseAdapter() { - int dragLastX=-1, dragLastY=-1; + private final UIShape.MouseGestureAdapter dragZoomRotateListener = new UIShape.MouseGestureAdapter() { + float dragFirstX=-1f, dragFirstY=-1f; boolean dragFirst = false; @Override @@ -753,34 +827,23 @@ public class GPUUISceneGLListener0A implements GLEventListener { @Override public void mouseDragged(MouseEvent e) { final Object attachment = e.getAttachment(); - if( attachment instanceof UIShape.EventDetails ) { - final UIShape.EventDetails shapeEvent = (UIShape.EventDetails)attachment; - if(e.getPointerCount()==2) { - // 2 pointers zoom .. - if(dragFirst) { - dragLastX = Math.abs(e.getY(0)-e.getY(1)); - dragFirst=false; - return; - } - int nv = Math.abs(e.getY(0)-e.getY(1)); - int dy = nv - dragLastX; - dragLastX = nv; - - shapeEvent.shape.translate(0f, 0f, 2 * Math.signum(dy)); - } else { + if( attachment instanceof UIShape.PointerEventInfo ) { + final UIShape.PointerEventInfo shapeEvent = (UIShape.PointerEventInfo)attachment; + if( e.getPointerCount() == 1 ) { // 1 pointer drag if(dragFirst) { - dragLastX = e.getX(); - dragLastY = e.getY(); + dragFirstX = shapeEvent.objPos[0]; // e.getX(); + dragFirstY = shapeEvent.objPos[1]; // e.getY(); dragFirst=false; return; } - final int nx = e.getX(); - final int ny = e.getY(); - shapeEvent.shape.translate(nx-dragLastX, -(ny-dragLastY), 0f); - dragLastX = nx; - dragLastY = ny; - float[] tx = shapeEvent.shape.getTranslate(); + final float nx = shapeEvent.objPos[0]; // e.getX(); + final float ny = shapeEvent.objPos[1]; // e.getY(); + final float dx = nx - dragFirstX; + final float dy = ny - dragFirstY; + // final float dy = -(ny - dragLastY); + shapeEvent.shape.translate(dx, dy, 0f); + final float[] tx = shapeEvent.shape.getTranslate(); actionText = String.format("Pos %6.2f / %6.2f / %6.2f", tx[0], tx[1], tx[2]); } } @@ -789,15 +852,37 @@ public class GPUUISceneGLListener0A implements GLEventListener { @Override public void mouseWheelMoved(MouseEvent e) { final Object attachment = e.getAttachment(); - if( attachment instanceof UIShape.EventDetails ) { - final UIShape.EventDetails shapeEvent = (UIShape.EventDetails)attachment; - if( 0 == ( ~InputEvent.BUTTONALL_MASK & e.getModifiers() ) ) { - float tz = 8f*e.getRotation()[1]; // vertical: wheel - System.err.println("tz.4 "+tz); + if( attachment instanceof UIShape.PointerEventInfo ) { + final UIShape.PointerEventInfo shapeEvent = (UIShape.PointerEventInfo)attachment; + final boolean isOnscreen = PointerClass.Onscreen == e.getPointerType(0).getPointerClass(); + if( 0 == ( ~InputEvent.BUTTONALL_MASK & e.getModifiers() ) && !isOnscreen ) { + // offscreen vertical mouse wheel zoom + final float tz = 8f*e.getRotation()[1]; // vertical: wheel + System.err.println("Rotate.Zoom.W: "+tz); + shapeEvent.shape.translate(0f, 0f, tz); + } else if( isOnscreen || e.isControlDown() ) { + final float[] rot = VectorUtil.scaleVec3(e.getRotation(), e.getRotation(), FloatUtil.PI / 180.0f); + if( isOnscreen ) { + System.err.println("XXX: "+e); + // swap axis for onscreen rotation matching natural feel + final float tmp = rot[0]; rot[0] = rot[1]; rot[1] = tmp; + VectorUtil.scaleVec3(rot, rot, 2f); + } + shapeEvent.shape.getRotation().rotateByEuler( rot ); + } + } + } + @Override + public void gestureDetected(GestureEvent e) { + final Object attachment = e.getAttachment(); + if( attachment instanceof UIShape.PointerEventInfo ) { + final UIShape.PointerEventInfo shapeEvent = (UIShape.PointerEventInfo)attachment; + if( e instanceof PinchToZoomGesture.ZoomEvent ) { + final PinchToZoomGesture.ZoomEvent ze = (PinchToZoomGesture.ZoomEvent) e; + final float tz = ze.getDelta() * ze.getScale(); + System.err.println("Rotate.Zoom.G: "+tz); shapeEvent.shape.translate(0f, 0f, tz); - } else if( e.isControlDown() ) { - shapeEvent.shape.getRotation().rotateByEuler( VectorUtil.scaleVec3(e.getRotation(), e.getRotation(), FloatUtil.PI / 180.0f) ); } } - } }; + } }; }
\ No newline at end of file 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 a15e2acdd..1056bb0f9 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 @@ -104,7 +104,7 @@ public class CrossHair extends UIShape { } @Override - public String toString() { - return "CrossHair [" + translate[0]+getWidth()/2f+" / "+translate[1]+getHeight()/2f+" "+getWidth() + "x" + getHeight() + ", "+box+"]"; + public String getSubString() { + return super.getSubString()+", dim "+getWidth() + "x" + getHeight(); } } diff --git a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/Label.java b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/Label.java index 37a82fe0f..175d18135 100644 --- a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/Label.java +++ b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/Label.java @@ -110,7 +110,8 @@ public class Label extends UIShape { } @Override - public String toString(){ - return "Label [" + font.toString() + ", size " + pixelSize + ", " + getText() + "]"; + public String getSubString() { + final int m = Math.min(text.length(), 8); + return super.getSubString()+", psize " + pixelSize + ", '" + text.substring(0, m)+"'"; } } diff --git a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/Label0.java b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/Label0.java index 336ce7526..573dd5604 100644 --- a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/Label0.java +++ b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/Label0.java @@ -99,6 +99,7 @@ public class Label0 { @Override public final String toString(){ - return "Label0 [" + font.toString() + ", " + getText() + "]"; + final int m = Math.min(text.length(), 8); + return "Label0 ['" + text.substring(0, m) + "']"; } } diff --git a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/LabelButton.java b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/LabelButton.java index 64cf794c6..90237f56c 100644 --- a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/LabelButton.java +++ b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/LabelButton.java @@ -56,9 +56,12 @@ public class LabelButton extends RoundButton { final Font labelFont, final String labelText, final float width, final float height, final float labelZOffset) { super(factory, renderModes | Region.COLORCHANNEL_RENDERING_BIT, width, height); - this.label = new Label0(labelFont, labelText, new float[] { 0.9f, 0.9f, 0.9f, 1.0f }); + this.label = new Label0(labelFont, labelText, new float[] { 1.33f, 1.33f, 1.33f, 1.0f }); // 0.75 * 1.33 = 1.0 this.labelZOffset = labelZOffset; - setLabelColor(1.0f, 1.0f, 1.0f); + setColor(0.75f, 0.75f, 0.75f, 1.0f); + setPressedColorMod(0.9f, 0.9f, 0.9f, 0.7f); + setToggleOffColorMod(0.65f, 0.65f, 0.65f, 1.0f); + setToggleOnColorMod(0.85f, 0.85f, 0.85f, 1.0f); } @Override 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 45907ad6a..4581223d7 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 @@ -1,6 +1,8 @@ package com.jogamp.opengl.test.junit.graph.demos.ui; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; import javax.media.opengl.GL; import javax.media.opengl.GL2ES2; @@ -10,8 +12,12 @@ import javax.media.opengl.GLRunnable; import javax.media.opengl.fixedfunc.GLMatrixFunc; import com.jogamp.graph.curve.opengl.RegionRenderer; +import com.jogamp.newt.event.GestureHandler; +import com.jogamp.newt.event.InputEvent; import com.jogamp.newt.event.MouseEvent; import com.jogamp.newt.event.MouseListener; +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; @@ -35,9 +41,12 @@ public class SceneUIController implements GLEventListener{ private final float[] sceneScale = new float[3]; private final float[] scenePlaneOrigin = new float[3]; - private int activeId = -1; + + private volatile UIShape activeShape = null; private SBCMouseListener sbcMouseListener = null; + private SBCGestureListener sbcGestureListener = null; + private PinchToZoomGesture pinchToZoomGesture = null; private GLAutoDrawable cDrawable = null; @@ -61,12 +70,21 @@ public class SceneUIController implements GLEventListener{ if(null == sbcMouseListener) { sbcMouseListener = new SBCMouseListener(); window.addMouseListener(sbcMouseListener); + sbcGestureListener = new SBCGestureListener(); + window.addGestureListener(sbcGestureListener); + pinchToZoomGesture = new PinchToZoomGesture(window.getNativeSurface(), false); + window.addGestureHandler(pinchToZoomGesture); } } public void detachInputListenerFrom(GLWindow window) { if(null != sbcMouseListener) { window.removeMouseListener(sbcMouseListener); + sbcMouseListener = null; + window.removeGestureListener(sbcGestureListener); + sbcGestureListener = null; + window.removeGestureHandler(pinchToZoomGesture); + pinchToZoomGesture = null; } } @@ -76,7 +94,6 @@ public class SceneUIController implements GLEventListener{ public void addShape(UIShape b) { shapes.add(b); } - public void removeShape(UIShape b) { shapes.remove(b); } @@ -109,41 +126,6 @@ public class SceneUIController implements GLEventListener{ cDrawable = drawable; } - public int pickShape(final int glWinX, final int glWinY) { - final float winZ0 = 0f; - final float winZ1 = 0.3f; - /** - final FloatBuffer winZRB = Buffers.newDirectFloatBuffer(1); - 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 Ray ray = new Ray(); - final int shapeCount = shapes.size(); - for(int i=0; i<shapeCount; i++) { - final UIShape uiShape = shapes.get(i); - if( uiShape.isEnabled() ) { - pmv.glPushMatrix(); - transformShape(pmv, uiShape); - - pmv.gluUnProjectRay(glWinX, glWinY, winZ0, winZ1, viewport, 0, ray); - // System.err.printf("Pick: mapped.0: [%d, %d, %f/%f] -> %s%n", winX, winY, winZ0, winZ1, ray); - - pmv.glPopMatrix(); - final AABBox box = shapes.get(i).getBounds(); - final boolean hit = box.intersectsRay(ray); - // System.err.println("Test: "+box+" -> hit "+hit+", shape: "+uiShape); - if( hit ) { - return i; - } - } - } - return -1; - } - private void transformShape(final PMVMatrix pmv, final UIShape uiShape) { final float[] uiTranslate = uiShape.getTranslate(); pmv.glTranslatef(uiTranslate[0], uiTranslate[1], uiTranslate[2]); @@ -173,6 +155,21 @@ public class SceneUIController implements GLEventListener{ } } + private static Comparator<UIShape> shapeZAscComparator = new Comparator<UIShape>() { + @Override + public int compare(final UIShape s1, final UIShape s2) { + final float s1Z = s1.getBounds().getMinZ()+s1.getTranslate()[2]; + final float s2Z = s2.getBounds().getMinZ()+s2.getTranslate()[2]; + if( FloatUtil.isEqual(s1Z, s2Z, FloatUtil.EPSILON) ) { + return 0; + } else if( s1Z < s2Z ){ + return -1; + } else { + return 1; + } + } }; + + @SuppressWarnings({ "unchecked", "rawtypes" }) @Override public void display(GLAutoDrawable drawable) { final GL2ES2 gl = drawable.getGL().getGL2ES2(); @@ -183,9 +180,17 @@ public class SceneUIController implements GLEventListener{ final PMVMatrix pmv = renderer.getMatrix(); pmv.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); - final int shapeCount = shapes.size(); + final Object[] shapesS = shapes.toArray(); + Arrays.sort(shapesS, (Comparator)shapeZAscComparator); + + renderer.enable(gl, true); + + //final int shapeCount = shapes.size(); + final int shapeCount = shapesS.length; for(int i=0; i<shapeCount; i++) { - final UIShape uiShape = shapes.get(i); + // final UIShape uiShape = shapes.get(i); + final UIShape uiShape = (UIShape)shapesS[i]; + // System.err.println("Id "+i+": "+uiShape); if( uiShape.isEnabled() ) { uiShape.validate(gl, renderer); pmv.glPushMatrix(); @@ -194,11 +199,108 @@ public class SceneUIController implements GLEventListener{ pmv.glPopMatrix(); } } + + renderer.enable(gl, false); + } + + public void pickShape(final int glWinX, final int glWinY, final float[] objPos, final UIShape[] shape, final Runnable runnable) { + if( null == cDrawable ) { + return; + } + cDrawable.invoke(false, new GLRunnable() { + @Override + public boolean run(GLAutoDrawable drawable) { + shape[0] = pickShapeImpl(glWinX, glWinY, objPos); + if( null != shape[0] ) { + runnable.run(); + } + return true; + } } ); + } + @SuppressWarnings({ "unchecked", "rawtypes" }) + private UIShape pickShapeImpl(final int glWinX, final int glWinY, final float[] objPos) { + final float winZ0 = 0f; + final float winZ1 = 0.3f; + /** + final FloatBuffer winZRB = Buffers.newDirectFloatBuffer(1); + 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 Ray ray = new Ray(); + + final Object[] shapesS = shapes.toArray(); + Arrays.sort(shapesS, (Comparator)shapeZAscComparator); + + for(int i=shapesS.length-1; i>=0; i--) { + final UIShape uiShape = (UIShape)shapesS[i]; + + if( uiShape.isEnabled() ) { + pmv.glPushMatrix(); + transformShape(pmv, uiShape); + final boolean ok = pmv.gluUnProjectRay(glWinX, glWinY, winZ0, winZ1, viewport, 0, ray); + pmv.glPopMatrix(); + if( ok ) { + final AABBox sbox = uiShape.getBounds(); + if( sbox.intersectsRay(ray) ) { + // System.err.printf("Pick.0: shape %d, [%d, %d, %f/%f] -> %s%n", i, glWinX, glWinY, winZ0, winZ1, ray); + if( null == sbox.getRayIntersection(objPos, ray, FloatUtil.EPSILON, true, dpyTmp1V3, dpyTmp2V3, dpyTmp3V3) ) { + throw new InternalError("Ray "+ray+", box "+sbox); + } + // System.err.printf("Pick.1: shape %d @ [%f, %f, %f], within %s%n", i, objPos[0], objPos[1], objPos[2], uiShape.getBounds()); + return uiShape; + } + } + } + } + return null; + } + private final float[] dpyTmp1V3 = new float[3]; + private final float[] dpyTmp2V3 = new float[3]; + private final float[] dpyTmp3V3 = new float[3]; + + public void windowToShapeCoords(final UIShape activeShape, final int glWinX, final int glWinY, final float[] objPos, final Runnable runnable) { + if( null == cDrawable || null == activeShape ) { + return; + } + cDrawable.invoke(false, new GLRunnable() { + @Override + public boolean run(GLAutoDrawable drawable) { + if( windowToShapeCoordsImpl(activeShape, 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(GLAutoDrawable drawable) { System.err.println("SceneUIController: dispose"); + final GL2ES2 gl = drawable.getGL().getGL2ES2(); + for(int i=0; i<shapes.size(); i++) { + shapes.get(i).destroy(gl, renderer); + } + shapes.clear(); cDrawable = null; } @@ -229,12 +331,11 @@ public class SceneUIController implements GLEventListener{ final float[] obj00Coord = new float[3]; final float[] obj11Coord = new float[3]; final float[] winZ = new float[1]; - final int[] view = new int[] { 0, 0, width, height }; - mapWin2ObjectCoords(pmv, view, zNear, zFar, 0f, 0f, orthoDist, winZ, obj00Coord); + mapWin2ObjectCoords(pmv, viewport, zNear, zFar, 0f, 0f, orthoDist, winZ, obj00Coord); 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]); - mapWin2ObjectCoords(pmv, view, zNear, zFar, width, height, orthoDist, winZ, obj11Coord); + mapWin2ObjectCoords(pmv, viewport, zNear, zFar, width, height, orthoDist, winZ, obj11Coord); 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]); nearPlane1Box.setSize( obj00Coord[0], // lx @@ -261,71 +362,128 @@ public class SceneUIController implements GLEventListener{ pmv.glScalef(sceneScale[0], sceneScale[1], sceneScale[2]); } - public UIShape getActiveUI() { - if( 0 > activeId ) { + public final UIShape getShape(final int id) { + if( 0 > id ) { return null; } - return shapes.get(activeId); + return shapes.get(id); + } + public final UIShape getActiveShape() { + return activeShape; } public void release() { - activeId = -1; + setActiveShape(null); + } + private void setActiveShape(final UIShape shape) { + activeShape = shape; } - private class SBCMouseListener implements MouseListener { - int lx=-1, ly=-1; + private final class SBCGestureListener implements GestureHandler.GestureListener { + @Override + public void gestureDetected(final GestureEvent gh) { + if( null != activeShape ) { + // gesture .. delegate to active shape! + final InputEvent orig = gh.getTrigger(); + if( orig instanceof MouseEvent ) { + final MouseEvent e = (MouseEvent) orig; + // flip to GL window coordinates + final int glWinX = e.getX(); + final int glWinY = viewport[3] - e.getY() - 1; + final float[] objPos = new float[3]; + final UIShape shape = activeShape; + windowToShapeCoords(shape, glWinX, glWinY, objPos, new Runnable() { + public void run() { + shape.dispatchGestureEvent(gh, glWinX, glWinY, objPos); + } } ); + } + } + } + } - void clear() { - lx = -1; ly = -1; + final void dispatchMouseEvent(final MouseEvent e, final int glWinX, final int glWinY) { + if( null == activeShape ) { + dispatchMouseEventPickShape(e, glWinX, glWinY, true); + } else { + dispatchMouseEventForShape(activeShape, e, glWinX, glWinY); } + } + 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() { + if( setActive ) { + setActiveShape(shape[0]); + } + shape[0].dispatchMouseEvent(e, glWinX, glWinY, objPos); + } } ); + } + 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() { + public void run() { + shape.dispatchMouseEvent(e, glWinX, glWinY, objPos); + } } ); + } - @Override - public void mouseClicked(MouseEvent e) { - UIShape uiShape = getActiveUI(); - if(uiShape != null){ - uiShape.dispatchMouseEvent(e, e.getX(), viewport[3]-e.getY()); - } - clear(); - release(); + private class SBCMouseListener implements MouseListener { + int lx=-1, ly=-1, lId=-1; + + void clear() { + lx = -1; ly = -1; lId = -1; } @Override public void mousePressed(final MouseEvent e) { - if(null==cDrawable) { - return; + if( -1 == lId || e.getPointerId(0) == lId ) { + lx = e.getX(); + ly = e.getY(); + lId = e.getPointerId(0); } - // flip to GL window coordinates final int glWinX = e.getX(); - final int glWinY = viewport[3] - e.getY(); - - // Avoid race condition w/ matrix instance, - // even thought we do not require a GL operation! - cDrawable.invoke(true, new GLRunnable() { - @Override - public boolean run(GLAutoDrawable drawable) { - activeId = pickShape(glWinX, glWinY); - final UIShape uiShape = getActiveUI(); - if(uiShape != null) { - uiShape.dispatchMouseEvent(e, glWinX, glWinY); - } - return true; - } } ); + final int glWinY = viewport[3] - e.getY() - 1; + dispatchMouseEvent(e, glWinX, glWinY); } @Override public void mouseReleased(MouseEvent e) { - final UIShape uiShape = getActiveUI(); - if(uiShape != null){ - uiShape.dispatchMouseEvent(e, e.getX(), viewport[3]-e.getY()); + // flip to GL window coordinates + final int glWinX = e.getX(); + final int glWinY = viewport[3] - e.getY() - 1; + dispatchMouseEvent(e, glWinX, glWinY); + if( 1 == e.getPointerCount() ) { + // Release active shape: last pointer has been lifted! + release(); + clear(); } } @Override - public void mouseDragged(MouseEvent e) { - final UIShape uiShape = getActiveUI(); - if(uiShape != null) { - uiShape.dispatchMouseEvent(e, e.getX(), viewport[3]-e.getY()); + public void mouseClicked(final MouseEvent e) { + // flip to GL window coordinates + final int glWinX = e.getX(); + final int glWinY = viewport[3] - e.getY() - 1; + // activeId should have been released by mouseRelease() already! + dispatchMouseEventPickShape(e, glWinX, glWinY, false); + // Release active shape: last pointer has been lifted! + release(); + clear(); + } + + @Override + public void mouseDragged(final MouseEvent e) { + // drag activeShape, if no gesture-activity, only on 1st pointer + if( null != activeShape && !pinchToZoomGesture.isWithinGesture() && e.getPointerId(0) == lId ) { + lx = e.getX(); + ly = e.getY(); + + // dragged .. delegate to active shape! + // flip to GL window coordinates + final int glWinX = lx; + final int glWinY = viewport[3] - ly - 1; + dispatchMouseEventForShape(activeShape, e, glWinX, glWinY); } } @@ -333,24 +491,17 @@ public class SceneUIController implements GLEventListener{ public void mouseWheelMoved(final MouseEvent e) { // flip to GL window coordinates final int glWinX = lx; - final int glWinY = viewport[3] - ly; - - cDrawable.invoke(true, new GLRunnable() { - @Override - public boolean run(GLAutoDrawable drawable) { - activeId = pickShape(glWinX, glWinY); - final UIShape uiShape = getActiveUI(); - if(uiShape != null) { - uiShape.dispatchMouseEvent(e, glWinX, glWinY); - } - return true; - } } ); + final int glWinY = viewport[3] - ly - 1; + dispatchMouseEventPickShape(e, glWinX, glWinY, true); } @Override public void mouseMoved(MouseEvent e) { - lx = e.getX(); - ly = e.getY(); + if( -1 == lId || e.getPointerId(0) == lId ) { + lx = e.getX(); + ly = e.getY(); + lId = e.getPointerId(0); + } } @Override public void mouseEntered(MouseEvent e) { } diff --git a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/TextureButton.java b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/TextureButton.java index eeb5e19f3..5b00d1e3b 100644 --- a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/TextureButton.java +++ b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/TextureButton.java @@ -47,8 +47,10 @@ public class TextureButton extends RoundButton { final float width, final float height, final TextureSequence texSeq) { super(factory, renderModes | Region.COLORTEXTURE_RENDERING_BIT, width, height); this.texSeq = texSeq; - setColor(1.0f, 1.0f, 1.0f, 1.0f); - setSelectedColorMod(0.9f, 0.9f, 0.9f, 0.9f); + setColor(0.8f, 0.8f, 0.8f, 1.0f); + setPressedColorMod(1.1f, 1.1f, 1.1f, 0.7f); + setToggleOffColorMod(0.8f, 0.8f, 0.8f, 1.0f); + setToggleOnColorMod(1.0f, 1.0f, 1.0f, 1.0f); } @Override diff --git a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UIGLListener01.java b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UIGLListener01.java index 90eb7d4f0..d019e696e 100644 --- a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UIGLListener01.java +++ b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UIGLListener01.java @@ -100,7 +100,7 @@ public class UIGLListener01 extends UIListenerBase01 { @Override public void dispose(GLAutoDrawable drawable) { - GL2ES2 gl = drawable.getGL().getGL2ES2(); + final GL2ES2 gl = drawable.getGL().getGL2ES2(); button.destroy(gl, getRegionRenderer()); super.dispose(drawable); } 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 419370af9..2e9d33a78 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 @@ -36,20 +36,21 @@ 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.curve.opengl.RenderState; import com.jogamp.graph.geom.Vertex; import com.jogamp.graph.geom.Vertex.Factory; +import com.jogamp.newt.event.GestureHandler.GestureEvent; +import com.jogamp.newt.event.GestureHandler.GestureListener; +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.Quaternion; -import com.jogamp.opengl.math.VectorUtil; import com.jogamp.opengl.math.geom.AABBox; public abstract class UIShape { public static final boolean DRAW_DEBUG_BOX = false; - protected static final int DIRTY_SHAPE = 1 << 0 ; + protected static final int DIRTY_SHAPE = 1 << 0 ; protected static final int DIRTY_STATE = 1 << 1 ; private final Factory<? extends Vertex> vertexFactory; @@ -67,16 +68,22 @@ public abstract class UIShape { protected int dirty = DIRTY_SHAPE | DIRTY_STATE; protected float shapesSharpness = OutlineShape.DEFAULT_SHARPNESS; - protected final float[] rgbaColor = {0.6f, 0.6f, 0.6f, 1.0f}; - protected final float[] selectedRGBAModulate = {1.4f, 1.4f, 1.4f, 1.0f}; + /** Default base-color w/o color channel, will be modulated w/ pressed- and toggle color */ + protected final float[] rgbaColor = {0.75f, 0.75f, 0.75f, 1.0f}; + /** Default pressed color-factor w/o color channel, modulated base-color. 0.75 * 1.2 = 0.9 */ + protected final float[] pressedRGBAModulate = {1.2f, 1.2f, 1.2f, 0.7f}; + /** Default toggle color-factor w/o color channel, modulated base-color. 0.75 * 1.13 ~ 0.85 */ + protected final float[] toggleOnRGBAModulate = {1.13f, 1.13f, 1.13f, 1.0f}; + /** Default toggle color-factor w/o color channel, modulated base-color. 0.75 * 0.86 ~ 0.65 */ + protected final float[] toggleOffRGBAModulate = {0.86f, 0.86f, 0.86f, 1.0f}; private int name = -1; private boolean down = false; - private boolean toggle =false; + private boolean toggle = false; private boolean toggleable = false; private boolean enabled = true; - private ArrayList<MouseListener> mouseListeners = new ArrayList<MouseListener>(); + private ArrayList<MouseGestureListener> mouseListeners = new ArrayList<MouseGestureListener>(); public UIShape(final Factory<? extends Vertex> factory, final int renderModes) { this.vertexFactory = factory; @@ -199,35 +206,58 @@ public abstract class UIShape { */ public void drawShape(GL2ES2 gl, RegionRenderer renderer, int[] sampleCount) { final float r, g, b, a; - final boolean isSelect; - if( isPressed() || getToggleState() ) { - isSelect = true; - r = rgbaColor[0]*selectedRGBAModulate[0]; - g = rgbaColor[1]*selectedRGBAModulate[1]; - b = rgbaColor[2]*selectedRGBAModulate[2]; - a = rgbaColor[3]*selectedRGBAModulate[3]; - } else { - isSelect = false; - r = rgbaColor[0]; - g = rgbaColor[1]; - b = rgbaColor[2]; - a = rgbaColor[3]; - } - - if( renderer.getRenderState().isHintMaskSet(RenderState.BITHINT_BLENDING_ENABLED) ) { - gl.glClearColor(r, g, b, 0.0f); - } - final RenderState rs = renderer.getRenderState(); - - if( Region.hasColorChannel( renderModes ) || Region.hasColorTexture( renderModes ) ) { - if( isSelect ) { - rs.setColorStatic(selectedRGBAModulate[0], selectedRGBAModulate[1], selectedRGBAModulate[2], selectedRGBAModulate[3]); + final boolean isPressed = isPressed(), isToggleOn = isToggleOn(); + final boolean modBaseColor = !Region.hasColorChannel( renderModes ) && !Region.hasColorTexture( renderModes ); + if( modBaseColor ) { + if( isPressed ) { + r = rgbaColor[0]*pressedRGBAModulate[0]; + g = rgbaColor[1]*pressedRGBAModulate[1]; + b = rgbaColor[2]*pressedRGBAModulate[2]; + a = rgbaColor[3]*pressedRGBAModulate[3]; + } else if( isToggleable() ) { + if( isToggleOn ) { + r = rgbaColor[0]*toggleOnRGBAModulate[0]; + g = rgbaColor[1]*toggleOnRGBAModulate[1]; + b = rgbaColor[2]*toggleOnRGBAModulate[2]; + a = rgbaColor[3]*toggleOnRGBAModulate[3]; + } else { + r = rgbaColor[0]*toggleOffRGBAModulate[0]; + g = rgbaColor[1]*toggleOffRGBAModulate[1]; + b = rgbaColor[2]*toggleOffRGBAModulate[2]; + a = rgbaColor[3]*toggleOffRGBAModulate[3]; + } } else { - rs.setColorStatic(1.0f, 1.0f, 1.0f, 1.0f); + r = rgbaColor[0]; + g = rgbaColor[1]; + b = rgbaColor[2]; + a = rgbaColor[3]; } } else { - rs.setColorStatic(r, g, b, a); + if( isPressed ) { + r = pressedRGBAModulate[0]; + g = pressedRGBAModulate[1]; + b = pressedRGBAModulate[2]; + a = pressedRGBAModulate[3]; + } else if( isToggleable() ) { + if( isToggleOn ) { + r = toggleOnRGBAModulate[0]; + g = toggleOnRGBAModulate[1]; + b = toggleOnRGBAModulate[2]; + a = toggleOnRGBAModulate[3]; + } else { + r = toggleOffRGBAModulate[0]; + g = toggleOffRGBAModulate[1]; + b = toggleOffRGBAModulate[2]; + a = toggleOffRGBAModulate[3]; + } + } else { + r = rgbaColor[0]; + g = rgbaColor[1]; + b = rgbaColor[2]; + a = rgbaColor[3]; + } } + renderer.getRenderState().setColorStatic(r, g, b, a); getRegion(gl, renderer).draw(gl, renderer, sampleCount); } @@ -252,12 +282,10 @@ public abstract class UIShape { region.addOutlineShape(shape, null, rgbaColor); } region.setQuality(regionQuality); - // dirty &= ~(DIRTY_SHAPE | DIRTY_STATE); - dirty = 0; + dirty &= ~(DIRTY_SHAPE|DIRTY_STATE); } else if( isStateDirty() ) { - region.markDirty(); - // dirty &= ~DIRTY_STATE; - dirty = 0; + region.markStateDirty(); + dirty &= ~DIRTY_STATE; } } @@ -286,14 +314,27 @@ public abstract class UIShape { this.rgbaColor[2] = b; this.rgbaColor[3] = a; } - public final void setSelectedColorMod(float r, float g, float b, float a){ - this.selectedRGBAModulate[0] = r; - this.selectedRGBAModulate[1] = g; - this.selectedRGBAModulate[2] = b; - this.selectedRGBAModulate[3] = a; + public final void setPressedColorMod(float r, float g, float b, float a) { + this.pressedRGBAModulate[0] = r; + this.pressedRGBAModulate[1] = g; + this.pressedRGBAModulate[2] = b; + this.pressedRGBAModulate[3] = a; + } + public final void setToggleOnColorMod(float r, float g, float b, float a) { + this.toggleOnRGBAModulate[0] = r; + this.toggleOnRGBAModulate[1] = g; + this.toggleOnRGBAModulate[2] = b; + this.toggleOnRGBAModulate[3] = a; + } + public final void setToggleOffColorMod(float r, float g, float b, float a) { + this.toggleOffRGBAModulate[0] = r; + this.toggleOffRGBAModulate[1] = g; + this.toggleOffRGBAModulate[2] = b; + this.toggleOffRGBAModulate[3] = a; } - public String toString() { + @Override + public final String toString() { return getClass().getSimpleName()+"["+getSubString()+"]"; } @@ -307,12 +348,8 @@ public abstract class UIShape { public void setPressed(final boolean b) { this.down = b; - if( isToggleable() && b ) { - toggle = !toggle; - } markStateDirty(); } - public boolean isPressed() { return this.down; } @@ -320,60 +357,93 @@ public abstract class UIShape { public void setToggleable(final boolean toggleable) { this.toggleable = toggleable; } - public boolean isToggleable() { return toggleable; } - - public void setToggleState(final boolean v) { + public void setToggle(final boolean v) { toggle = v; markStateDirty(); } - public boolean getToggleState() { return toggle; } + public void toggle() { + if( isToggleable() ) { + toggle = !toggle; + } + markStateDirty(); + } + public boolean isToggleOn() { return toggle; } - public final void addMouseListener(final MouseListener l) { + public final void addMouseListener(final MouseGestureListener l) { if(l == null) { return; } @SuppressWarnings("unchecked") - ArrayList<MouseListener> clonedListeners = (ArrayList<MouseListener>) mouseListeners.clone(); + final ArrayList<MouseGestureListener> clonedListeners = (ArrayList<MouseGestureListener>) mouseListeners.clone(); clonedListeners.add(l); mouseListeners = clonedListeners; } - public final void removeMouseListener(MouseListener l) { + public final void removeMouseListener(final MouseGestureListener l) { if (l == null) { return; } @SuppressWarnings("unchecked") - ArrayList<MouseListener> clonedListeners = (ArrayList<MouseListener>) mouseListeners.clone(); + final ArrayList<MouseGestureListener> clonedListeners = (ArrayList<MouseGestureListener>) mouseListeners.clone(); clonedListeners.remove(l); mouseListeners = clonedListeners; } /** + * Combining {@link MouseListener} and {@link GestureListener} + */ + public static interface MouseGestureListener extends MouseListener, GestureListener { + } + + /** + * Convenient adapter combining dummy implementation for {@link MouseListener} and {@link GestureListener} + */ + public static abstract class MouseGestureAdapter extends MouseAdapter implements MouseGestureListener { + @Override + public void gestureDetected(GestureEvent gh) { + } + } + + /** * {@link UIShape} event details for propagated {@link NEWTEvent}s * containing reference of {@link #shape the intended shape} as well as - * the {@link #rotPosition rotated relative position} and {@link #rotBounds bounding box}. + * the {@link #objPos rotated relative position} and {@link #rotBounds bounding box}. * The latter fields are also normalized to lower-left zero origin, allowing easier usage. */ - public static class EventDetails { + public static class PointerEventInfo { /** The intended {@link UIShape} instance for this event */ public final UIShape shape; - /** The {@link AABBox} of the intended {@link UIShape}, rotated about {@link UIShape#getRotation()} and normalized to lower-left zero origin.*/ - public final AABBox rotBounds; - /** The relative mouse pointer position inside the intended {@link UIShape}, rotated about {@link UIShape#getRotation()} and normalized to lower-left zero origin. */ - public final float[] rotPosition; - - EventDetails(final UIShape shape, final AABBox rotatedBounds, final float[] rotatedRelPos) { + /** The relative pointer position inside the intended {@link UIShape}. */ + public final float[] objPos; + /** window x-position in OpenGL model space */ + public final int glWinX; + /** window y-position in OpenGL model space */ + public final int glWinY; + + PointerEventInfo(final int glWinX, final int glWinY, final UIShape shape, final float[] objPos) { + this.glWinX = glWinX; + this.glWinY = glWinY; this.shape = shape; - this.rotBounds = rotatedBounds; - this.rotPosition = rotatedRelPos; + this.objPos = objPos; } public String toString() { - return "EventDetails[pos "+rotPosition[0]+", "+rotPosition[1]+", "+rotPosition[2]+ - ", "+rotBounds+", "+shape+"]"; + return "EventDetails[winPos ["+glWinX+", "+glWinY+"], objPos ["+objPos[0]+", "+objPos[1]+", "+objPos[2]+"], "+shape+"]"; + } + } + + /** + * @param e original Newt {@link GestureEvent} + * @param glWinX x-position in OpenGL model space + * @param glWinY y-position in OpenGL model space + */ + public final void dispatchGestureEvent(final GestureEvent e, final int glWinX, final int glWinY, final float[] objPos) { + e.setAttachment(new PointerEventInfo(glWinX, glWinY, this, objPos)); + for(int i = 0; !e.isConsumed() && i < mouseListeners.size(); i++ ) { + mouseListeners.get(i).gestureDetected(e); } } @@ -381,36 +451,28 @@ public abstract class UIShape { * * @param e original Newt {@link MouseEvent} * @param glX x-position in OpenGL model space - * @param glY x-position in OpenGL model space + * @param glY y-position in OpenGL model space */ - public final void dispatchMouseEvent(final MouseEvent e, final int glX, final int glY) { - // rotate bounding box and normalize to 0/0 - final Quaternion rot = getRotation(); - final float[] bLow = new float[3]; - VectorUtil.copyVec3(bLow, 0, getBounds().getLow(), 0); - VectorUtil.scaleVec3(bLow, bLow, -1f); - final AABBox rbox = new AABBox(getBounds()); - rbox.translate(bLow); - rbox.rotate(rot); - - // get unrotated relative position within shape, rotate and normalize to 0/0 - final float[] relPos = new float[] { glX, glY, 0f }; - VectorUtil.subVec3(relPos, relPos, getTranslate()); - VectorUtil.addVec3(relPos, relPos, bLow); - rot.rotateVector(relPos, 0, relPos, 0); - - // set as attachment - e.setAttachment(new EventDetails(this, rbox, relPos)); + public final void dispatchMouseEvent(final MouseEvent e, final int glWinX, final int glWinY, final float[] objPos) { + e.setAttachment(new PointerEventInfo(glWinX, glWinY, this, objPos)); final short eventType = e.getEventType(); - if( MouseEvent.EVENT_MOUSE_PRESSED == eventType ) { - setPressed(true); - } else if ( MouseEvent.EVENT_MOUSE_RELEASED == eventType ) { - setPressed(false); + if( 1 == e.getPointerCount() ) { + switch( eventType ) { + case MouseEvent.EVENT_MOUSE_CLICKED: + toggle(); + break; + case MouseEvent.EVENT_MOUSE_PRESSED: + setPressed(true); + break; + case MouseEvent.EVENT_MOUSE_RELEASED: + setPressed(false); + break; + } } for(int i = 0; !e.isConsumed() && i < mouseListeners.size(); i++ ) { - final MouseListener l = mouseListeners.get(i); + final MouseGestureListener l = mouseListeners.get(i); switch( eventType ) { case MouseEvent.EVENT_MOUSE_CLICKED: l.mouseClicked(e); |