diff options
Diffstat (limited to 'src/jogl/classes/com/jogamp/graph')
9 files changed, 675 insertions, 258 deletions
diff --git a/src/jogl/classes/com/jogamp/graph/curve/OutlineShape.java b/src/jogl/classes/com/jogamp/graph/curve/OutlineShape.java index 44744584d..d4977669e 100644 --- a/src/jogl/classes/com/jogamp/graph/curve/OutlineShape.java +++ b/src/jogl/classes/com/jogamp/graph/curve/OutlineShape.java @@ -111,6 +111,9 @@ public class OutlineShape implements Comparable<OutlineShape> { } } + /** Initial {@link #getSharpness()} value, which can be modified via {@link #setSharpness(float)}. */ + public static final float DEFAULT_SHARPNESS = 0.5f; + public static final int DIRTY_BOUNDS = 1 << 0; /** * Modified shape, requires to update the vertices and triangles, here: vertices. @@ -131,6 +134,7 @@ public class OutlineShape implements Comparable<OutlineShape> { private final AABBox bbox; private final ArrayList<Triangle> triangles; private final ArrayList<Vertex> vertices; + private int addedVerticeCount; private VerticesState outlineState; @@ -153,12 +157,24 @@ public class OutlineShape implements Comparable<OutlineShape> { this.bbox = new AABBox(); this.triangles = new ArrayList<Triangle>(); this.vertices = new ArrayList<Vertex>(); + this.addedVerticeCount = 0; this.dirtyBits = 0; - this.sharpness = 0.5f; + this.sharpness = DEFAULT_SHARPNESS; + } + + /** + * Return the number of newly added vertices during {@link #getTriangles(VerticesState)} + * while transforming the outlines to {@link VerticesState#QUADRATIC_NURBS} and triangulation. + * @see #setIsQuadraticNurbs() + */ + public int getAddedVerticeCount() { + return addedVerticeCount; } + /** Sharpness value, defaults to {@link #DEFAULT_SHARPNESS}. */ public float getSharpness() { return sharpness; } + /** Sets sharpness, defaults to {@link #DEFAULT_SHARPNESS}. */ public void setSharpness(final float s) { if( this.sharpness != s ) { clearCache(); @@ -174,6 +190,7 @@ public class OutlineShape implements Comparable<OutlineShape> { bbox.reset(); vertices.clear(); triangles.clear(); + addedVerticeCount = 0; dirtyBits = 0; } @@ -418,21 +435,12 @@ public class OutlineShape implements Comparable<OutlineShape> { } /** - * Ensure the outlines represent - * the specified destinationType. - * and removes all overlaps in boundary triangles - * @param destinationType the target outline's vertices state. Currently only - * {@link OutlineShape.VerticesState#QUADRATIC_NURBS} are supported. + * Claim this outline's vertices are all {@link OutlineShape.VerticesState#QUADRATIC_NURBS}, + * hence no cubic transformations will be performed. */ - protected final void transformOutlines(VerticesState destinationType) { - if(outlineState != destinationType){ - if(destinationType == VerticesState.QUADRATIC_NURBS){ - transformOutlines2Quadratic(); - checkOverlaps(); - } else { - throw new IllegalStateException("destinationType "+destinationType.name()+" not supported (currently "+outlineState.name()+")"); - } - } + public final void setIsQuadraticNurbs() { + outlineState = VerticesState.QUADRATIC_NURBS; + // checkPossibleOverlaps = false; } private void subdivideTriangle(final Outline outline, Vertex a, Vertex b, Vertex c, int index){ @@ -446,6 +454,8 @@ public class OutlineShape implements Comparable<OutlineShape> { outline.addVertex(index, vertexFactory.create(tmpV1, 0, 3, false)); outline.addVertex(index+2, vertexFactory.create(tmpV3, 0, 3, false)); + + addedVerticeCount += 2; } /** @@ -470,21 +480,24 @@ public class OutlineShape implements Comparable<OutlineShape> { if ( !currentVertex.isOnCurve()) { final Vertex nextV = outline.getVertex((i+1)%vertexCount); final Vertex prevV = outline.getVertex((i+vertexCount-1)%vertexCount); - Vertex overlap =null; - - //check for overlap even if already set for subdivision - //ensuring both trianglur overlaps get divided - //for pref. only check in first pass - //second pass to clear the overlaps arrray(reduces precision errors) - if(firstpass) { - overlap = checkTriOverlaps(prevV, currentVertex, nextV); + final Vertex overlap; + + // check for overlap even if already set for subdivision + // ensuring both triangular overlaps get divided + // for pref. only check in first pass + // second pass to clear the overlaps array(reduces precision errors) + if( firstpass ) { + overlap = checkTriOverlaps0(prevV, currentVertex, nextV); + } else { + overlap = null; } - if(overlaps.contains(currentVertex) || overlap != null) { + if( overlaps.contains(currentVertex) || overlap != null ) { overlaps.remove(currentVertex); subdivideTriangle(outline, prevV, currentVertex, nextV, i); i+=3; vertexCount+=2; + addedVerticeCount+=2; if(overlap != null && !overlap.isOnCurve()) { if(!overlaps.contains(overlap)) { @@ -496,11 +509,11 @@ public class OutlineShape implements Comparable<OutlineShape> { } } firstpass = false; - }while(!overlaps.isEmpty()); + } while( !overlaps.isEmpty() ); } - private Vertex checkTriOverlaps(Vertex a, Vertex b, Vertex c) { - int count = getOutlineNumber(); + private Vertex checkTriOverlaps0(final Vertex a, final Vertex b, final Vertex c) { + final int count = getOutlineNumber(); for (int cc = 0; cc < count; cc++) { final Outline outline = getOutline(cc); int vertexCount = outline.getVertexCount(); @@ -524,45 +537,81 @@ public class OutlineShape implements Comparable<OutlineShape> { } if(VectorUtil.testTri2SegIntersection(a, b, c, prevV, current) || VectorUtil.testTri2SegIntersection(a, b, c, current, nextV) || - VectorUtil.testTri2SegIntersection(a, b, c, prevV, nextV)) { + VectorUtil.testTri2SegIntersection(a, b, c, prevV, nextV) ) { return current; } } } return null; } + @SuppressWarnings("unused") + private Vertex checkTriOverlaps1(final Vertex a, final Vertex b, final Vertex c) { + final int count = getOutlineNumber(); + for (int cc = 0; cc < count; cc++) { + final Outline outline = getOutline(cc); + int vertexCount = outline.getVertexCount(); + for(int i=0; i < vertexCount; i++) { + final Vertex current = outline.getVertex(i); + if(current.isOnCurve() || current == a || current == b || current == c) { + continue; + } + final Vertex nextV = outline.getVertex((i+1)%vertexCount); + final Vertex prevV = outline.getVertex((i+vertexCount-1)%vertexCount); - private void transformOutlines2Quadratic() { + //skip neighboring triangles + if(prevV == c || nextV == a) { + continue; + } + + if( VectorUtil.isVec3InTriangle3(a.getCoord(), b.getCoord(), c.getCoord(), + current.getCoord(), nextV.getCoord(), prevV.getCoord(), + tmpV1, tmpV2, tmpV3, FloatUtil.EPSILON) ) { + return current; + } + if(VectorUtil.testTri2SegIntersection(a, b, c, prevV, current, FloatUtil.EPSILON) || + VectorUtil.testTri2SegIntersection(a, b, c, current, nextV, FloatUtil.EPSILON) || + VectorUtil.testTri2SegIntersection(a, b, c, prevV, nextV, FloatUtil.EPSILON) ) { + return current; + } + } + } + return null; + } + + private void cleanupOutlines() { + final boolean transformOutlines2Quadratic = VerticesState.QUADRATIC_NURBS != outlineState; int count = getOutlineNumber(); for (int cc = 0; cc < count; cc++) { final Outline outline = getOutline(cc); int vertexCount = outline.getVertexCount(); - for(int i=0; i < vertexCount; i++) { - final Vertex currentVertex = outline.getVertex(i); - final Vertex nextVertex = outline.getVertex((i+1)%vertexCount); - if ( !currentVertex.isOnCurve() && !nextVertex.isOnCurve() ) { - VectorUtil.midVec3(tmpV1, currentVertex.getCoord(), nextVertex.getCoord()); - final Vertex v = vertexFactory.create(tmpV1, 0, 3, true); - i++; - vertexCount++; - outline.addVertex(i, v); + if( transformOutlines2Quadratic ) { + for(int i=0; i < vertexCount; i++) { + final Vertex currentVertex = outline.getVertex(i); + final int j = (i+1)%vertexCount; + final Vertex nextVertex = outline.getVertex(j); + if ( !currentVertex.isOnCurve() && !nextVertex.isOnCurve() ) { + VectorUtil.midVec3(tmpV1, currentVertex.getCoord(), nextVertex.getCoord()); + System.err.println("XXX: Cubic: "+i+": "+currentVertex+", "+j+": "+nextVertex); + final Vertex v = vertexFactory.create(tmpV1, 0, 3, true); + i++; + vertexCount++; + addedVerticeCount++; + outline.addVertex(i, v); + } } } - if(vertexCount <= 0) { + if( 0 >= vertexCount ) { outlines.remove(outline); cc--; count--; - continue; - } - - if( vertexCount > 0 ) { - if(VectorUtil.isVec3Equal( outline.getVertex(0).getCoord(), 0, outline.getLastVertex().getCoord(), 0, FloatUtil.EPSILON )) { - outline.removeVertex(vertexCount-1); - } + } else if( 0 < vertexCount && + VectorUtil.isVec3Equal( outline.getVertex(0).getCoord(), 0, outline.getLastVertex().getCoord(), 0, FloatUtil.EPSILON )) { + outline.removeVertex(vertexCount-1); } } outlineState = VerticesState.QUADRATIC_NURBS; + checkOverlaps(); } private int generateVertexIds() { @@ -616,6 +665,7 @@ public class OutlineShape implements Comparable<OutlineShape> { triangulator2d.addCurve(triangles, outlines.get(index), sharpness); } triangulator2d.generate(triangles); + addedVerticeCount += triangulator2d.getAddedVerticeCount(); triangulator2d.reset(); } } @@ -631,8 +681,11 @@ public class OutlineShape implements Comparable<OutlineShape> { */ public ArrayList<Triangle> getTriangles(VerticesState destinationType) { final boolean updated; + if(destinationType != VerticesState.QUADRATIC_NURBS) { + throw new IllegalStateException("destinationType "+destinationType.name()+" not supported (currently "+outlineState.name()+")"); + } if( 0 != ( DIRTY_TRIANGLES & dirtyBits ) ) { - transformOutlines(destinationType); + cleanupOutlines(); triangulateImpl(); updated = true; dirtyBits |= DIRTY_VERTICES; diff --git a/src/jogl/classes/com/jogamp/graph/curve/OutlineShapeXForm.java b/src/jogl/classes/com/jogamp/graph/curve/OutlineShapeXForm.java index b1c99f5ed..cf4d38450 100644 --- a/src/jogl/classes/com/jogamp/graph/curve/OutlineShapeXForm.java +++ b/src/jogl/classes/com/jogamp/graph/curve/OutlineShapeXForm.java @@ -4,10 +4,13 @@ import jogamp.graph.geom.plane.AffineTransform; public class OutlineShapeXForm { public final OutlineShape shape; - public final AffineTransform t; + private AffineTransform t; public OutlineShapeXForm(final OutlineShape shape, final AffineTransform t) { this.shape = shape; this.t = t; } + + public final AffineTransform getTransform() { return t; } + public final void setTransform(final AffineTransform t) { this.t = t; } }
\ No newline at end of file diff --git a/src/jogl/classes/com/jogamp/graph/curve/Region.java b/src/jogl/classes/com/jogamp/graph/curve/Region.java index 15a0d6bff..2cd68a806 100644 --- a/src/jogl/classes/com/jogamp/graph/curve/Region.java +++ b/src/jogl/classes/com/jogamp/graph/curve/Region.java @@ -78,11 +78,31 @@ public abstract class Region { * being applied. * </p> */ - public static final int VARIABLE_CURVE_WEIGHT_BIT = 1 << 8; + public static final int VARWEIGHT_RENDERING_BIT = 1 << 8; + + /** + * Rendering-Mode bit for {@link Region#getRenderModes() Region} and {@link com.jogamp.graph.curve.opengl.RegionRenderer#getRenderModes() RegionRenderer}. + * <p> + * If set, a color channel attribute per vertex is added to the stream, + * otherwise only the + * {@link com.jogamp.graph.curve.opengl.RegionRenderer#setColorStatic(javax.media.opengl.GL2ES2, float, float, float, float) static color} + * is being used. + * </p> + */ + public static final int COLORCHANNEL_RENDERING_BIT = 1 << 9; + + /** + * Rendering-Mode bit for {@link Region#getRenderModes() Region} and {@link com.jogamp.graph.curve.opengl.RegionRenderer#getRenderModes() RegionRenderer}. + * <p> + * If set, a color texture is used to determine the color. + * </p> + */ + public static final int COLORTEXTURE_RENDERING_BIT = 1 << 10; public static final int TWO_PASS_DEFAULT_TEXTURE_UNIT = 0; private final int renderModes; + private int quality; private boolean dirty = true; private int numVertices = 0; protected final AABBox box = new AABBox(); @@ -91,48 +111,73 @@ public abstract class Region { public static boolean isVBAA(int renderModes) { return 0 != (renderModes & Region.VBAA_RENDERING_BIT); } + public static boolean isMSAA(int renderModes) { return 0 != (renderModes & Region.MSAA_RENDERING_BIT); } + public static boolean isTwoPass(int renderModes) { + return 0 != ( renderModes & ( Region.VBAA_RENDERING_BIT | Region.MSAA_RENDERING_BIT) ); + } + /** - * Check if render mode capable of non uniform weights - * - * @param renderModes - * bit-field of modes, e.g. - * {@link Region#VARIABLE_CURVE_WEIGHT_BIT}, - * {@link Region#VBAA_RENDERING_BIT} - * @return true of capable of non uniform weights */ - public static boolean isNonUniformWeight(int renderModes) { - return 0 != (renderModes & Region.VARIABLE_CURVE_WEIGHT_BIT); + * Returns true if render mode capable of variable weights, + * i.e. the bit {@link #VARWEIGHT_RENDERING_BIT} is set, + * otherwise false. + */ + public static boolean hasVariableWeight(int renderModes) { + return 0 != (renderModes & Region.VARWEIGHT_RENDERING_BIT); + } + + /** + * Returns true if render mode has a color channel, + * i.e. the bit {@link #COLORCHANNEL_RENDERING_BIT} is set, + * otherwise false. + */ + public static boolean hasColorChannel(int renderModes) { + return 0 != (renderModes & Region.COLORCHANNEL_RENDERING_BIT); + } + + /** + * Returns true if render mode has a color texture, + * i.e. the bit {@link #COLORTEXTURE_RENDERING_BIT} is set, + * otherwise false. + */ + public static boolean hasColorTexture(int renderModes) { + return 0 != (renderModes & Region.COLORTEXTURE_RENDERING_BIT); } public static String getRenderModeString(int renderModes) { - final String curveS = isNonUniformWeight(renderModes) ? "-curve" : ""; + final String curveS = hasVariableWeight(renderModes) ? "-curve" : ""; + final String cChanS = hasColorChannel(renderModes) ? "-cols" : ""; + final String cTexS = hasColorTexture(renderModes) ? "-ctex" : ""; if( Region.isVBAA(renderModes) ) { - return "vbaa"+curveS; + return "vbaa"+curveS+cChanS+cTexS; } else if( Region.isMSAA(renderModes) ) { - return "msaa"+curveS; + return "msaa"+curveS+cChanS+cTexS; } else { - return "norm"+curveS; + return "norm"+curveS+cChanS+cTexS; } } protected Region(int regionRenderModes) { this.renderModes = regionRenderModes; + this.quality = 99; } // FIXME: Better handling of impl. buffer growth .. ! + // protected abstract void setupInitialComponentCount(int attributeCount, int indexCount); - protected abstract void pushVertex(float[] coords, float[] texParams); + protected abstract void pushVertex(final float[] coords, final float[] texParams, float[] rgba); protected abstract void pushIndex(int idx); /** * Return bit-field of render modes, see {@link #create(int)}. */ - public final int getRenderModes() { - return renderModes; - } + public final int getRenderModes() { return renderModes; } + + public final int getQuality() { return quality; } + public final void setQuality(int q) { quality=q; } protected void clearImpl() { dirty = true; @@ -141,31 +186,50 @@ public abstract class Region { } /** - * Return true if capable of two pass rendering - VBAA, otherwise false. + * Returns true if capable of two pass rendering - VBAA, otherwise false. */ public final boolean isVBAA() { - return isVBAA(renderModes); + return Region.isVBAA(renderModes); } /** - * Return true if capable of two pass rendering - MSAA, otherwise false. + * Returns true if capable of two pass rendering - MSAA, otherwise false. */ public final boolean isMSAA() { - return isMSAA(renderModes); + return Region.isMSAA(renderModes); + } + + /** + * Returns true if capable of variable weights, otherwise false. + */ + public final boolean hasVariableWeight() { + return Region.hasVariableWeight(renderModes); } /** - * Return true if capable of nonuniform weights, otherwise false. + * Returns true if render mode has a color channel, + * i.e. the bit {@link #COLORCHANNEL_RENDERING_BIT} is set, + * otherwise false. */ - public final boolean isNonUniformWeight() { - return Region.isNonUniformWeight(renderModes); + public boolean hasColorChannel() { + return Region.hasColorChannel(renderModes); } + /** + * Returns true if render mode has a color texture, + * i.e. the bit {@link #COLORTEXTURE_RENDERING_BIT} is set, + * otherwise false. + */ + public boolean hasColorTexture() { + return Region.hasColorTexture(renderModes); + } + + /** See {@link #setFrustum(Frustum)} */ public final Frustum getFrustum() { return frustum; } /** - * Set {@link Frustum} culling for {@link #addOutlineShape(OutlineShape, AffineTransform)}. + * Set {@link Frustum} culling for {@link #addOutlineShape(OutlineShape, AffineTransform, float[])}. */ public final void setFrustum(Frustum frustum) { this.frustum = frustum; @@ -173,23 +237,23 @@ public abstract class Region { final float[] coordsEx = new float[3]; - private void pushNewVertexImpl(final Vertex vertIn, final AffineTransform transform) { + private void pushNewVertexImpl(final Vertex vertIn, final AffineTransform transform, float[] rgba) { if( null != transform ) { final float[] coordsIn = vertIn.getCoord(); transform.transform(coordsIn, coordsEx); coordsEx[2] = coordsIn[2]; box.resize(coordsEx[0], coordsEx[1], coordsEx[2]); - pushVertex(coordsEx, vertIn.getTexCoord()); + pushVertex(coordsEx, vertIn.getTexCoord(), rgba); } else { box.resize(vertIn.getX(), vertIn.getY(), vertIn.getZ()); - pushVertex(vertIn.getCoord(), vertIn.getTexCoord()); + pushVertex(vertIn.getCoord(), vertIn.getTexCoord(), rgba); } numVertices++; } - private void pushNewVertexIdxImpl(final Vertex vertIn, final AffineTransform transform) { + private void pushNewVertexIdxImpl(final Vertex vertIn, final AffineTransform transform, float[] rgba) { pushIndex(numVertices); - pushNewVertexImpl(vertIn, transform); + pushNewVertexImpl(vertIn, transform, rgba); } private final AABBox tmpBox = new AABBox(); @@ -201,8 +265,9 @@ public abstract class Region { * is dropped if it's {@link OutlineShape#getBounds() bounding-box} is fully outside of the frustum. * The optional {@link AffineTransform} is applied to the bounding-box beforehand. * </p> + * @param rgbaColor TODO */ - public final void addOutlineShape(final OutlineShape shape, final AffineTransform t) { + public final void addOutlineShape(final OutlineShape shape, final AffineTransform t, final float[] rgbaColor) { if( null != frustum ) { final AABBox shapeBox = shape.getBounds(); final AABBox shapeBoxT; @@ -222,8 +287,15 @@ public abstract class Region { final List<Triangle> trisIn = shape.getTriangles(OutlineShape.VerticesState.QUADRATIC_NURBS); final ArrayList<Vertex> vertsIn = shape.getVertices(); if(DEBUG_INSTANCE) { + final int addedVerticeCount = shape.getAddedVerticeCount(); + final int verticeCount = vertsIn.size() + addedVerticeCount; + final int indexCount = trisIn.size() * 3; System.err.println("Region.addOutlineShape().0: tris: "+trisIn.size()+", verts "+vertsIn.size()+", transform "+t); + System.err.println("Region.addOutlineShape().0: VerticeCount "+vertsIn.size()+" + "+addedVerticeCount+" = "+verticeCount); + System.err.println("Region.addOutlineShape().0: IndexCount "+indexCount); } + // setupInitialComponentCount(verticeCount, indexCount); // FIXME: Use it ? + final int idxOffset = numVertices; int vertsVNewIdxCount = 0, vertsTMovIdxCount = 0, vertsTNewIdxCount = 0, tris = 0; int vertsDupCountV = 0, vertsDupCountT = 0, vertsKnownMovedT = 0; @@ -232,7 +304,7 @@ public abstract class Region { System.err.println("Region.addOutlineShape(): Processing Vertices"); } for(int i=0; i<vertsIn.size(); i++) { - pushNewVertexImpl(vertsIn.get(i), t); + pushNewVertexImpl(vertsIn.get(i), t, rgbaColor); vertsVNewIdxCount++; } if(DEBUG_INSTANCE) { @@ -261,9 +333,9 @@ public abstract class Region { if(Region.DEBUG_INSTANCE) { System.err.println("T["+i+"]: New Idx "+numVertices); } - pushNewVertexIdxImpl(triInVertices[0], t); - pushNewVertexIdxImpl(triInVertices[1], t); - pushNewVertexIdxImpl(triInVertices[2], t); + pushNewVertexIdxImpl(triInVertices[0], t, rgbaColor); + pushNewVertexIdxImpl(triInVertices[1], t, rgbaColor); + pushNewVertexIdxImpl(triInVertices[2], t, rgbaColor); vertsTNewIdxCount+=3; } tris++; @@ -280,9 +352,9 @@ public abstract class Region { setDirty(true); } - public final void addOutlineShapes(final List<OutlineShape> shapes, final AffineTransform transform) { + public final void addOutlineShapes(final List<OutlineShape> shapes, final AffineTransform transform, final float[] rgbaColor) { for (int i = 0; i < shapes.size(); i++) { - addOutlineShape(shapes.get(i), transform); + addOutlineShape(shapes.get(i), transform, rgbaColor); } } @@ -306,6 +378,6 @@ public abstract class Region { } public String toString() { - return "Region["+getRenderModeString(this.renderModes)+", dirty "+dirty+", vertices "+numVertices+", box "+box+"]"; + return "Region["+getRenderModeString(this.renderModes)+", q "+quality+", dirty "+dirty+", vertices "+numVertices+", box "+box+"]"; } }
\ No newline at end of file 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 96d285898..e305cc336 100644 --- a/src/jogl/classes/com/jogamp/graph/curve/opengl/GLRegion.java +++ b/src/jogl/classes/com/jogamp/graph/curve/opengl/GLRegion.java @@ -56,7 +56,7 @@ public abstract class GLRegion extends Region { * {@link Region#TWO_PASS_DEFAULT_TEXTURE_UNIT} is being used.</p>
*
* @param rs the RenderState to be used
- * @param renderModes bit-field of modes, e.g. {@link Region#VARIABLE_CURVE_WEIGHT_BIT}, {@link Region#VBAA_RENDERING_BIT}
+ * @param renderModes bit-field of modes, e.g. {@link Region#VARWEIGHT_RENDERING_BIT}, {@link Region#VBAA_RENDERING_BIT}
*/
public static GLRegion create(int renderModes) {
if( isVBAA(renderModes) ) {
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 8233d4262..8df34ead5 100644 --- a/src/jogl/classes/com/jogamp/graph/curve/opengl/RegionRenderer.java +++ b/src/jogl/classes/com/jogamp/graph/curve/opengl/RegionRenderer.java @@ -27,16 +27,21 @@ */ package com.jogamp.graph.curve.opengl; -import java.nio.FloatBuffer; +import java.io.IOException; +import java.util.Iterator; import javax.media.opengl.GL; import javax.media.opengl.GL2ES2; import javax.media.opengl.GLException; import javax.media.opengl.fixedfunc.GLMatrixFunc; +import jogamp.graph.curve.opengl.shader.AttributeNames; + +import com.jogamp.opengl.GLExtensions; import com.jogamp.opengl.util.glsl.ShaderCode; -import com.jogamp.opengl.util.glsl.ShaderState; +import com.jogamp.opengl.util.glsl.ShaderProgram; import com.jogamp.opengl.util.PMVMatrix; +import com.jogamp.common.util.IntObjectHashMap; import com.jogamp.graph.curve.Region; /** @@ -46,7 +51,7 @@ import com.jogamp.graph.curve.Region; * are passed through an instance of this class. * </p> */ -public abstract class RegionRenderer { +public class RegionRenderer { protected static final boolean DEBUG = Region.DEBUG; protected static final boolean DEBUG_INSTANCE = Region.DEBUG_INSTANCE; @@ -95,10 +100,6 @@ public abstract class RegionRenderer { } }; - public static boolean isWeightValid(float v) { - return 0.0f <= v && v <= 1.9f ; - } - /** * Create a Hardware accelerated Region Renderer. * <p> @@ -108,7 +109,7 @@ public abstract class RegionRenderer { * can be utilized to enable and disable {@link GL#GL_BLEND}. * </p> * @param rs the used {@link RenderState} - * @param renderModes bit-field of modes, e.g. {@link Region#VARIABLE_CURVE_WEIGHT_BIT}, {@link Region#VBAA_RENDERING_BIT} + * @param renderModes bit-field of modes, e.g. {@link Region#VARWEIGHT_RENDERING_BIT}, {@link Region#VBAA_RENDERING_BIT} * @param enableCallback optional {@link GLCallback}, if not <code>null</code> will be issued at * {@link #init(GL2ES2) init(gl)} and {@link #enable(GL2ES2, boolean) enable(gl, true)}. * @param disableCallback optional {@link GLCallback}, if not <code>null</code> will be issued at @@ -118,7 +119,7 @@ public abstract class RegionRenderer { */ public static RegionRenderer create(final RenderState rs, final int renderModes, final GLCallback enableCallback, final GLCallback disableCallback) { - return new jogamp.graph.curve.opengl.RegionRendererImpl01(rs, renderModes, enableCallback, disableCallback); + return new RegionRenderer(rs, renderModes, enableCallback, disableCallback); } private final int renderModes; @@ -139,19 +140,10 @@ public abstract class RegionRenderer { /** Return height of current viewport */ public final int getHeight() { return vp_height; } - public final float getWeight() { return rs.getWeight().floatValue(); } - public final float getAlpha() { return rs.getAlpha().floatValue(); } - public final PMVMatrix getMatrix() { return rs.pmvMatrix(); } - - /** - * Implementation shall load, compile and link the shader program and leave it active. - * @param gl referencing the current GLContext to which the ShaderState is bound to - * @return - */ - protected abstract boolean initImpl(GL2ES2 gl); - - /** Delete and clean the associated OGL objects */ - protected abstract void destroyImpl(GL2ES2 gl); + public final PMVMatrix getMatrix() { return rs.getMatrix(); } + public final PMVMatrix getMatrixMutable() { return rs.getMatrixMutable(); } + public final void setMatrixDirty() { rs.setMatrixDirty(); } + public final boolean isMatrixDirty() { return rs.isMatrixDirty(); } ////////////////////////////////////// @@ -170,14 +162,14 @@ public abstract class RegionRenderer { return renderModes; } - public final boolean usesVariableCurveWeight() { return Region.isNonUniformWeight(renderModes); } + public final boolean usesVariableCurveWeight() { return Region.hasVariableWeight(renderModes); } /** * @return true if Region's renderModes contains all bits as this Renderer's renderModes - * except {@link Region#VARIABLE_CURVE_WEIGHT_BIT}, otherwise false. + * except {@link Region#VARWEIGHT_RENDERING_BIT}, otherwise false. */ public final boolean areRenderModesCompatible(final Region region) { - final int cleanRenderModes = getRenderModes() & ( Region.VARIABLE_CURVE_WEIGHT_BIT ); + final int cleanRenderModes = getRenderModes() & ( Region.VARWEIGHT_RENDERING_BIT ); return cleanRenderModes == ( region.getRenderModes() & cleanRenderModes ); } @@ -217,28 +209,11 @@ public abstract class RegionRenderer { enableCallback.run(gl, this); } - initialized = initImpl(gl); + useShaderProgram(gl, renderModes, true, 0, 0); + initialized = rs.update(gl, true, renderModes, true); if(!initialized) { throw new GLException("Shader initialization failed"); } - - if(!rs.getShaderState().uniform(gl, rs.getPMVMatrix())) { - throw new GLException("Error setting PMVMatrix in shader: "+rs.getShaderState()); - } - - if( Region.isNonUniformWeight( getRenderModes() ) ) { - if(!rs.getShaderState().uniform(gl, rs.getWeight())) { - throw new GLException("Error setting weight in shader: "+rs.getShaderState()); - } - } - - if(!rs.getShaderState().uniform(gl, rs.getAlpha())) { - throw new GLException("Error setting global alpha in shader: "+rs.getShaderState()); - } - - if(!rs.getShaderState().uniform(gl, rs.getColorStatic())) { - throw new GLException("Error setting global color in shader: "+rs.getShaderState()); - } } public final void destroy(GL2ES2 gl) { @@ -248,18 +223,19 @@ public abstract class RegionRenderer { } return; } - rs.getShaderState().useProgram(gl, false); - destroyImpl(gl); + for(final Iterator<IntObjectHashMap.Entry> i = shaderPrograms.iterator(); i.hasNext(); ) { + final ShaderProgram sp = (ShaderProgram) i.next().getValue(); + sp.destroy(gl); + } rs.destroy(gl); initialized = false; } public final RenderState getRenderState() { return rs; } - public final ShaderState getShaderState() { return rs.getShaderState(); } /** - * Enabling or disabling the {@link RenderState#getShaderState() RenderState}'s - * {@link ShaderState#useProgram(GL2ES2, boolean) ShaderState program}. + * Enabling or disabling the {@link #getRenderState() RenderState}'s + * {@link RenderState#getShaderProgram() shader program}. * <p> * In case enable and disable {@link GLCallback}s are setup via {@link #create(RenderState, int, GLCallback, GLCallback)}, * they will be called before toggling the shader program. @@ -276,103 +252,59 @@ public abstract class RegionRenderer { disableCallback.run(gl, this); } } - rs.getShaderState().useProgram(gl, enable); - } - - public final void setWeight(GL2ES2 gl, float v) { - if( !isWeightValid(v) ) { - throw new IllegalArgumentException("Weight out of range"); - } - rs.getWeight().setData(v); - if(null != gl && rs.getShaderState().inUse() && Region.isNonUniformWeight( getRenderModes() ) ) { - rs.getShaderState().uniform(gl, rs.getWeight()); - } - } - - public final void setAlpha(GL2ES2 gl, float alpha_t) { - rs.getAlpha().setData(alpha_t); - if(null != gl && rs.getShaderState().inUse()) { - rs.getShaderState().uniform(gl, rs.getAlpha()); - } - - } - - public final void getColorStatic(float[] rgb) { - FloatBuffer fb = (FloatBuffer) rs.getColorStatic().getBuffer(); - rgb[0] = fb.get(0); - rgb[1] = fb.get(1); - rgb[2] = fb.get(2); - } - - public final void setColorStatic(GL2ES2 gl, float r, float g, float b){ - FloatBuffer fb = (FloatBuffer) rs.getColorStatic().getBuffer(); - fb.put(0, r); - fb.put(1, g); - fb.put(2, b); - if(null != gl && rs.getShaderState().inUse()) { - rs.getShaderState().uniform(gl, rs.getColorStatic()); - } - } - - public final void updateMatrix(GL2ES2 gl) { - if(initialized && null != gl && rs.getShaderState().inUse()) { - rs.getShaderState().uniform(gl, rs.getPMVMatrix()); + if( !enable ) { + final ShaderProgram sp = rs.getShaderProgram(); + if( null != sp ) { + sp.useProgram(gl, false); + } } } - /** No PMVMatrix operation is performed here. PMVMatrix will be updated if gl is not null. */ - public final boolean reshapeNotify(GL2ES2 gl, int width, int height) { + /** No PMVMatrix operation is performed here. PMVMatrix is marked dirty. */ + public final void reshapeNotify(int width, int height) { this.vp_width = width; this.vp_height = height; - updateMatrix(gl); - return true; + rs.setMatrixDirty(); } - public final boolean reshapePerspective(GL2ES2 gl, float angle, int width, int height, float near, float far) { + public final void reshapePerspective(float angle, int width, int height, float near, float far) { this.vp_width = width; this.vp_height = height; final float ratio = (float)width/(float)height; - final PMVMatrix p = rs.pmvMatrix(); + final PMVMatrix p = rs.getMatrixMutable(); p.glMatrixMode(GLMatrixFunc.GL_PROJECTION); p.glLoadIdentity(); p.gluPerspective(angle, ratio, near, far); - updateMatrix(gl); - return true; } - public final boolean reshapeOrtho(GL2ES2 gl, int width, int height, float near, float far) { + public final void reshapeOrtho(int width, int height, float near, float far) { this.vp_width = width; this.vp_height = height; - final PMVMatrix p = rs.pmvMatrix(); + final PMVMatrix p = rs.getMatrixMutable(); p.glMatrixMode(GLMatrixFunc.GL_PROJECTION); p.glLoadIdentity(); p.glOrthof(0, width, 0, height, near, far); - updateMatrix(gl); - return true; } - protected String getVertexShaderName() { - return "curverenderer" + getImplVersion(); - } + // + // Shader Management + // - protected String getFragmentShaderName() { - final String version = getImplVersion(); - final String pass; - if( Region.isVBAA(renderModes) ) { - pass = "-2pass_vbaa"; - } else if( Region.isMSAA(renderModes) ) { - pass = "-2pass_msaa"; - } else { - pass = "-1pass_norm" ; - } - final String weight = Region.isNonUniformWeight(renderModes) ? "-weight" : "" ; - return "curverenderer" + version + pass + weight; + private static final String SHADER_SRC_SUB = ""; + private static final String SHADER_BIN_SUB = "bin"; + + 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 String getVersionedShaderName() { + return "curverenderer01"; } // FIXME: Really required to have sampler2D def. precision ? If not, we can drop getFragmentShaderPrecision(..) and use default ShaderCode .. - public static final String es2_precision_fp = "\nprecision mediump float;\nprecision mediump int;\nprecision mediump sampler2D;\n"; + private static final String es2_precision_fp = "\nprecision mediump float;\nprecision mediump int;\nprecision mediump sampler2D;\n"; - protected String getFragmentShaderPrecision(GL2ES2 gl) { + private final String getFragmentShaderPrecision(GL2ES2 gl) { if( gl.isGLES() ) { return es2_precision_fp; } @@ -382,7 +314,173 @@ public abstract class RegionRenderer { return null; } - protected String getImplVersion() { - return "01"; + private static enum ShaderModeSelector1 { + /** Pass-1: Curve Simple */ + PASS1_SIMPLE("curve", "_simple", 0), + /** Pass-1: Curve Varying Weight */ + PASS1_WEIGHT("curve", "_weight", 0), + /** Pass-2: MSAA */ + PASS2_MSAA("msaa", "", 0), + /** Pass-2: VBAA Flipquad3, 1 sample */ + PASS2_VBAA_QUAL0_SAMPLES1("vbaa", "_flipquad3", 1), + /** Pass-2: VBAA Flipquad3, 2 samples */ + PASS2_VBAA_QUAL0_SAMPLES2("vbaa", "_flipquad3", 2), + /** Pass-2: VBAA Flipquad3, 4 samples */ + PASS2_VBAA_QUAL0_SAMPLES4("vbaa", "_flipquad3", 4), + /** Pass-2: VBAA Flipquad3, 8 samples */ + PASS2_VBAA_QUAL0_SAMPLES8("vbaa", "_flipquad3", 8), + /** Pass-2: VBAA All-Equal, 2 samples */ + PASS2_VBAA_QUAL1_SAMPLES2("vbaa", "_allequal", 2), + /** Pass-2: VBAA All-Equal, 4 samples */ + PASS2_VBAA_QUAL1_SAMPLES4("vbaa", "_allequal", 4), + /** Pass-2: VBAA All-Equal, 6 samples */ + PASS2_VBAA_QUAL1_SAMPLES6("vbaa", "_allequal", 6), + /** Pass-2: VBAA All-Equal, 8 samples */ + PASS2_VBAA_QUAL1_SAMPLES8("vbaa", "_allequal", 8); + + public final String tech; + public final String sub; + public final int sampleCount; + + ShaderModeSelector1(final String tech, final String sub, final int sampleCount) { + this.tech = tech; + this.sub= sub; + this.sampleCount = sampleCount; + } + + public static ShaderModeSelector1 selectPass1(final int renderModes) { + return Region.hasVariableWeight(renderModes) ? PASS1_WEIGHT : PASS1_SIMPLE; + } + + public static ShaderModeSelector1 selectPass2(final int renderModes, final int quality, final int sampleCount) { + if( Region.isMSAA(renderModes) ) { + return PASS2_MSAA; + } else if( Region.isVBAA(renderModes) ) { + if( 0 == quality ) { + if( sampleCount < 2 ) { + return PASS2_VBAA_QUAL0_SAMPLES1; + } else if( sampleCount < 4 ) { + return PASS2_VBAA_QUAL0_SAMPLES2; + } else if( sampleCount < 8 ) { + return PASS2_VBAA_QUAL0_SAMPLES4; + } else { + return PASS2_VBAA_QUAL0_SAMPLES8; + } + } else { + if( sampleCount < 4 ) { + return PASS2_VBAA_QUAL1_SAMPLES2; + } else if( sampleCount < 6 ) { + return PASS2_VBAA_QUAL1_SAMPLES4; + } else if( sampleCount < 8 ) { + return PASS2_VBAA_QUAL1_SAMPLES6; + } else { + return PASS2_VBAA_QUAL1_SAMPLES8; + } + } + } else { + return null; + } + } } + private final IntObjectHashMap shaderPrograms = new IntObjectHashMap(); + + private static final int HIGH_MASK = Region.COLORCHANNEL_RENDERING_BIT | Region.COLORTEXTURE_RENDERING_BIT; + + /** + * @param gl + * @param renderModes + * @param pass1 + * @param quality + * @param sampleCount + * @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 ShaderModeSelector1 sel1 = pass1 ? ShaderModeSelector1.selectPass1(renderModes) : + ShaderModeSelector1.selectPass2(renderModes, quality, sampleCount); + final int shaderKey = sel1.ordinal() | ( HIGH_MASK & renderModes ); + + /** + if(DEBUG) { + System.err.printf("RegionRendererImpl01.useShaderProgram.0: renderModes %s, sel1 %s, key 0x%X (pass1 %b, q %d, samples %d) - Thread %s%n", + Region.getRenderModeString(renderModes), sel1, shaderKey, pass1, quality, sampleCount, Thread.currentThread()); + } */ + + ShaderProgram sp = (ShaderProgram) shaderPrograms.get( shaderKey ); + if( null != sp ) { + final boolean spChanged = getRenderState().setShaderProgram(gl, sp); + if(DEBUG) { + if( spChanged ) { + System.err.printf("RegionRendererImpl01.useShaderProgram.X1: GOT renderModes %s, sel1 %s, key 0x%X (changed)%n", Region.getRenderModeString(renderModes), sel1, shaderKey); + } + } + return spChanged; + } + final String versionedBaseName = getVersionedShaderName(); + final String vertexShaderName; + if( Region.isTwoPass( renderModes ) ) { + vertexShaderName = versionedBaseName+"-pass"+(pass1?1:2); + } else { + vertexShaderName = versionedBaseName+"-single"; + } + 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); + // rsFp.defaultShaderCustomization(gl, true, true); + int posFp = rsFp.addGLSLVersion(gl); + if( 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( Region.hasColorChannel( renderModes ) ) { + posVp = rsVp.insertShaderSource(0, posVp, USE_COLOR_CHANNEL); + posFp = rsFp.insertShaderSource(0, posFp, USE_COLOR_CHANNEL); + } + if( Region.hasColorTexture( renderModes ) ) { + posVp = rsVp.insertShaderSource(0, posVp, USE_COLOR_TEXTURE); + posFp = rsFp.insertShaderSource(0, posFp, USE_COLOR_TEXTURE); + } + if( !pass1 ) { + posFp = rsFp.insertShaderSource(0, posFp, DEF_SAMPLE_COUNT+sel1.sampleCount+"\n"); + } + + 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); + } 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"); + + sp = new ShaderProgram(); + sp.add(rsVp); + sp.add(rsFp); + + if( !sp.init(gl) ) { + throw new GLException("RegionRenderer: Couldn't init program: "+sp); + } + if( !sp.link(gl, System.err) ) { + throw new GLException("could not link program: "+sp); + } + getRenderState().setShaderProgram(gl, sp); + + shaderPrograms.put(shaderKey, sp); + if(DEBUG) { + System.err.printf("RegionRendererImpl01.useShaderProgram.X1: PUT renderModes %s, sel1 %s, key 0x%X -> SP %s (changed)%n", + Region.getRenderModeString(renderModes), sel1, shaderKey, sp); + } + 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 490af140a..f915b7d49 100644 --- a/src/jogl/classes/com/jogamp/graph/curve/opengl/RenderState.java +++ b/src/jogl/classes/com/jogamp/graph/curve/opengl/RenderState.java @@ -27,20 +27,22 @@ */ package com.jogamp.graph.curve.opengl; +import java.nio.FloatBuffer; + import javax.media.opengl.GL; import javax.media.opengl.GL2ES2; import javax.media.opengl.GLUniformData; -import jogamp.graph.curve.opengl.RenderStateImpl; import jogamp.graph.curve.opengl.shader.UniformNames; import com.jogamp.common.os.Platform; import com.jogamp.graph.curve.Region; import com.jogamp.graph.geom.Vertex; +import com.jogamp.opengl.util.GLArrayDataServer; import com.jogamp.opengl.util.PMVMatrix; -import com.jogamp.opengl.util.glsl.ShaderState; +import com.jogamp.opengl.util.glsl.ShaderProgram; -public abstract class RenderState { +public class RenderState { private static final String thisKey = "jogamp.graph.curve.RenderState" ; /** @@ -51,7 +53,7 @@ public abstract class RenderState { * </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 #getColorStatic() foreground color} and <i>zero alpha</i>, + * 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. * </p> * <p> @@ -62,37 +64,192 @@ public abstract class RenderState { */ public static final int BITHINT_BLENDING_ENABLED = 1 << 0 ; - public static RenderState createRenderState(ShaderState st, Vertex.Factory<? extends Vertex> pointFactory) { - return new RenderStateImpl(st, pointFactory, null); + public static RenderState createRenderState(Vertex.Factory<? extends Vertex> pointFactory) { + return new RenderState(pointFactory, null); } - public static RenderState createRenderState(ShaderState st, Vertex.Factory<? extends Vertex> pointFactory, PMVMatrix pmvMatrix) { - return new RenderStateImpl(st, pointFactory, pmvMatrix); + public static RenderState createRenderState(Vertex.Factory<? extends Vertex> pointFactory, PMVMatrix pmvMatrix) { + return new RenderState(pointFactory, pmvMatrix); } public static final RenderState getRenderState(GL2ES2 gl) { return (RenderState) gl.getContext().getAttachedObject(thisKey); } - protected final ShaderState st; - protected final Vertex.Factory<? extends Vertex> vertexFactory; - protected final PMVMatrix pmvMatrix; - protected final GLUniformData gcu_PMVMatrix; - protected int hintBitfield; + private final Vertex.Factory<? extends Vertex> vertexFactory; + private final PMVMatrix pmvMatrix; + private final GLUniformData gcu_PMVMatrix01; + private final GLUniformData gcu_Weight; + private final GLUniformData gcu_ColorStatic; + private boolean gcu_PMVMatrix01_dirty = true; + private boolean gcu_Weight_dirty = true; + private boolean gcu_ColorStatic_dirty = true; + private ShaderProgram sp; + private int hintBitfield; - protected RenderState(ShaderState st, Vertex.Factory<? extends Vertex> vertexFactory, PMVMatrix pmvMatrix) { - this.st = st; + protected RenderState(Vertex.Factory<? extends Vertex> vertexFactory, PMVMatrix pmvMatrix) { + this.sp = null; this.vertexFactory = vertexFactory; this.pmvMatrix = null != pmvMatrix ? pmvMatrix : new PMVMatrix(); - this.gcu_PMVMatrix = new GLUniformData(UniformNames.gcu_PMVMatrix, 4, 4, this.pmvMatrix.glGetPMvMatrixf()); - st.ownUniform(gcu_PMVMatrix); + this.gcu_PMVMatrix01 = new GLUniformData(UniformNames.gcu_PMVMatrix01, 4, 4, this.pmvMatrix.glGetPMvMatrixf()); + this.gcu_Weight = new GLUniformData(UniformNames.gcu_Weight, 1.0f); + this.gcu_ColorStatic = new GLUniformData(UniformNames.gcu_ColorStatic, 4, FloatBuffer.allocate(4)); this.hintBitfield = 0; } - public final ShaderState getShaderState() { return st; } + public final ShaderProgram getShaderProgram() { return sp; } + public final boolean isShaderProgramInUse() { return null != sp ? sp.inUse() : false; } + + /** + * Set a {@link ShaderProgram} and enable it. If the given {@link ShaderProgram} is new, + * method returns true, otherwise false. + * @param gl + * @param sp + * @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 setShaderProgram(final GL2ES2 gl, final ShaderProgram sp) { + if( sp.equals(this.sp) ) { + sp.useProgram(gl, true); + return false; + } + this.sp = sp; + sp.useProgram(gl, true); + return true; + } + public final Vertex.Factory<? extends Vertex> getVertexFactory() { return vertexFactory; } - public final PMVMatrix pmvMatrix() { return pmvMatrix; } - public final GLUniformData getPMVMatrix() { return gcu_PMVMatrix; } + + public final PMVMatrix getMatrix() { return pmvMatrix; } + public final PMVMatrix getMatrixMutable() { + gcu_PMVMatrix01_dirty = true; + return pmvMatrix; + } + public final GLUniformData getMatrixUniform() { return gcu_PMVMatrix01; } + public final void setMatrixDirty() { gcu_PMVMatrix01_dirty = true; } + public final boolean isMatrixDirty() { return gcu_PMVMatrix01_dirty;} + + public final void updateMatrix(GL2ES2 gl) { + if( gcu_PMVMatrix01_dirty && sp.inUse() ) { + gl.glUniform( gcu_PMVMatrix01 ); + gcu_PMVMatrix01_dirty = false; + } + } + + public static boolean isWeightValid(float v) { + return 0.0f <= v && v <= 1.9f ; + } + public final float getWeight() { return gcu_Weight.floatValue(); } + public final void setWeight(float v) { + if( !isWeightValid(v) ) { + throw new IllegalArgumentException("Weight out of range"); + } + gcu_Weight_dirty = true; + gcu_Weight.setData(v); + } + + + public final float[] getColorStatic(float[] rgbaColor) { + FloatBuffer fb = (FloatBuffer) gcu_ColorStatic.getBuffer(); + rgbaColor[0] = fb.get(0); + rgbaColor[1] = fb.get(1); + rgbaColor[2] = fb.get(2); + rgbaColor[3] = fb.get(3); + return rgbaColor; + } + public final void setColorStatic(float r, float g, float b, float a){ + final FloatBuffer fb = (FloatBuffer) gcu_ColorStatic.getBuffer(); + fb.put(0, r); + fb.put(1, g); + fb.put(2, b); + fb.put(3, a); + gcu_ColorStatic_dirty = true; + } + + + /** + * + * @param gl + * @param updateLocation + * @param renderModes + * @return true if no error occurred, i.e. all locations found, otherwise false. + */ + public final boolean update(GL2ES2 gl, final boolean updateLocation, final int renderModes, final boolean pass1) { + boolean res = true; + if( null != sp && sp.inUse() ) { + if( ( !Region.isTwoPass(renderModes) || !pass1 ) && ( gcu_PMVMatrix01_dirty || updateLocation ) ) { + final boolean r0 = updateUniformDataLoc(gl, updateLocation, gcu_PMVMatrix01_dirty, gcu_PMVMatrix01); + System.err.println("XXX gcu_PMVMatrix01.update: "+r0); + res = res && r0; + gcu_PMVMatrix01_dirty = !r0; + } + if( pass1 ) { + if( Region.hasVariableWeight( renderModes ) && ( gcu_Weight_dirty || updateLocation ) ) { + final boolean r0 = updateUniformDataLoc(gl, updateLocation, gcu_Weight_dirty, gcu_Weight); + System.err.println("XXX gcu_Weight.update: "+r0); + res = res && r0; + gcu_Weight_dirty = !r0; + } + if( gcu_ColorStatic_dirty || updateLocation ) { + final boolean r0 = updateUniformDataLoc(gl, updateLocation, gcu_ColorStatic_dirty, gcu_ColorStatic); + System.err.println("XXX gcu_ColorStatic.update: "+r0); + res = res && r0; + gcu_ColorStatic_dirty = false; + } + } + } + return res; + } + + /** + * + * @param gl + * @param updateLocation + * @param data + * @return true if no error occured, i.e. all locations found, otherwise false. + */ + public final boolean updateUniformLoc(final GL2ES2 gl, final boolean updateLocation, final GLUniformData data) { + if( updateLocation || 0 > data.getLocation() ) { + return 0 <= data.setLocation(gl, sp.program()); + } else { + return true; + } + } + + /** + * + * @param gl + * @param updateLocation + * @param updateData TODO + * @param data + * @return true if no error occured, i.e. all locations found, otherwise false. + */ + public final boolean updateUniformDataLoc(final GL2ES2 gl, boolean updateLocation, boolean updateData, final GLUniformData data) { + updateLocation = updateLocation || 0 > data.getLocation(); + if( updateLocation ) { + updateData = 0 <= data.setLocation(gl, sp.program()); + } + if( updateData ){ + gl.glUniform(data); + return true; + } else { + return !updateLocation; + } + } + + /** + * @param gl + * @param data + * @return true if no error occured, i.e. all locations found, otherwise false. + */ + public final boolean updateAttributeLoc(final GL2ES2 gl, final boolean updateLocation, final GLArrayDataServer data) { + if( updateLocation || 0 > data.getLocation() ) { + return 0 <= data.setLocation(gl, sp.program()); + } else { + return true; + } + } + public final boolean isHintMaskSet(int mask) { return mask == ( hintBitfield & mask ); @@ -105,14 +262,12 @@ public abstract class RenderState { } public void destroy(GL2ES2 gl) { - st.destroy(gl); + if( null != sp ) { + sp.destroy(gl); + sp = null; + } } - public abstract GLUniformData getWeight(); - public abstract GLUniformData getAlpha(); - public abstract GLUniformData getColorStatic(); - // public abstract GLUniformData getStrength(); - public final RenderState attachTo(GL2ES2 gl) { return (RenderState) gl.getContext().attachObject(thisKey, this); } @@ -130,11 +285,12 @@ public abstract class RenderState { if(null==sb) { sb = new StringBuilder(); } - - sb.append("RenderState["); - st.toString(sb, alsoUnlocated).append(Platform.getNewline()); - sb.append("]"); - + sb.append("RenderState[").append(sp).append(Platform.NEWLINE); + // pmvMatrix.toString(sb, "%.2f"); + sb.append(", dirty[pmv "+gcu_PMVMatrix01_dirty+", color "+gcu_ColorStatic_dirty+", weight "+gcu_Weight_dirty+"], ").append(Platform.NEWLINE); + sb.append(gcu_PMVMatrix01).append(", ").append(Platform.NEWLINE); + sb.append(gcu_ColorStatic).append(", "); + sb.append(gcu_Weight).append("]"); return sb; } diff --git a/src/jogl/classes/com/jogamp/graph/curve/opengl/TextRegionUtil.java b/src/jogl/classes/com/jogamp/graph/curve/opengl/TextRegionUtil.java index 140e03cfb..6d9fdab0b 100644 --- a/src/jogl/classes/com/jogamp/graph/curve/opengl/TextRegionUtil.java +++ b/src/jogl/classes/com/jogamp/graph/curve/opengl/TextRegionUtil.java @@ -68,7 +68,7 @@ public class TextRegionUtil { * additionally passing the progressed {@link AffineTransform}. * The latter reflects the given font metric, pixelSize and hence character position. * @param visitor - * @param transform + * @param transform optional given transform * @param font the target {@link Font} * @param pixelSize Use {@link Font#getPixelSize(float, float)} for resolution correct pixel-size. * @param str string text @@ -82,7 +82,7 @@ public class TextRegionUtil { final float lineHeight = font.getLineHeight(pixelSize); final float scale = metrics.getScale(pixelSize); - final AffineTransform t = new AffineTransform(transform); + final AffineTransform t = null != transform ? new AffineTransform(transform) : new AffineTransform(); float y = 0; float advanceTotal = 0; @@ -98,7 +98,12 @@ public class TextRegionUtil { if(Region.DEBUG_INSTANCE) { System.err.println("XXXXXXXXXXXXXXx char: "+character+", scale: "+scale+"; translate: "+advanceTotal+", "+y); } - t.setTransform(transform); // reset transform + // reset transform + if( null != transform ) { + t.setTransform(transform); + } else { + t.setToIdentity(); + } t.translate(advanceTotal, y); t.scale(scale, scale); @@ -121,14 +126,15 @@ public class TextRegionUtil { * @param font the target {@link Font} * @param pixelSize Use {@link Font#getPixelSize(float, float)} for resolution correct pixel-size. * @param str string text + * @param rgbaColor if {@link Region#hasColorChannel()} RGBA color must be passed, otherwise value is ignored. */ public static void addStringToRegion(final GLRegion region, final Factory<? extends Vertex> vertexFactory, - final Font font, final float pixelSize, final CharSequence str) { + final Font font, final float pixelSize, final CharSequence str, final float[] rgbaColor) { final ShapeVisitor visitor = new ShapeVisitor() { public final void visit(final OutlineShape shape, final AffineTransform t) { - region.addOutlineShape(shape, t); + region.addOutlineShape(shape, t, region.hasColorChannel() ? rgbaColor : null); } }; - processString(visitor, new AffineTransform(), font, pixelSize, str); + processString(visitor, null, font, pixelSize, str); } /** @@ -141,13 +147,14 @@ public class TextRegionUtil { * @param font {@link Font} to be used * @param pixelSize Use {@link Font#getPixelSize(float, float)} for resolution correct pixel-size. * @param str text to be rendered + * @param rgbaColor if {@link Region#hasColorChannel()} RGBA color must be passed, otherwise value is ignored. * @param sampleCount desired multisampling sample count for msaa-rendering. * The actual used scample-count is written back when msaa-rendering is enabled, otherwise the store is untouched. * @throws Exception if TextRenderer not initialized */ public void drawString3D(final GL2ES2 gl, final Font font, final float pixelSize, final CharSequence str, - final int[/*1*/] sampleCount) { + final float[] rgbaColor, final int[/*1*/] sampleCount) { if( !renderer.isInitialized() ) { throw new GLException("TextRendererImpl01: not initialized!"); } @@ -155,7 +162,7 @@ public class TextRegionUtil { GLRegion region = getCachedRegion(font, str, pixelSize, special); if(null == region) { region = GLRegion.create(renderer.getRenderModes()); - addStringToRegion(region, renderer.getRenderState().getVertexFactory(), font, pixelSize, str); + addStringToRegion(region, renderer.getRenderState().getVertexFactory(), font, pixelSize, str, rgbaColor); addCachedRegion(gl, font, str, pixelSize, special, region); } region.draw(gl, renderer, sampleCount); @@ -167,25 +174,26 @@ public class TextRegionUtil { * <p> * In case of a multisampling region renderer, i.e. {@link Region#VBAA_RENDERING_BIT}, recreating the {@link GLRegion} * is a huge performance impact. - * In such case better use {@link #drawString3D(GLRegion, RegionRenderer, GL2ES2, Font, float, CharSequence, int[])} + * In such case better use {@link #drawString3D(GLRegion, RegionRenderer, GL2ES2, Font, float, CharSequence, float[], int[])} * instead. * </p> * @param gl the current GL state * @param font {@link Font} to be used * @param pixelSize Use {@link Font#getPixelSize(float, float)} for resolution correct pixel-size. * @param str text to be rendered + * @param rgbaColor if {@link Region#hasColorChannel()} RGBA color must be passed, otherwise value is ignored. * @param sampleCount desired multisampling sample count for msaa-rendering. * The actual used scample-count is written back when msaa-rendering is enabled, otherwise the store is untouched. * @throws Exception if TextRenderer not initialized */ public static void drawString3D(final RegionRenderer renderer, final GL2ES2 gl, final Font font, final float pixelSize, final CharSequence str, - final int[/*1*/] sampleCount) { + final float[] rgbaColor, final int[/*1*/] sampleCount) { if(!renderer.isInitialized()){ throw new GLException("TextRendererImpl01: not initialized!"); } final GLRegion region = GLRegion.create(renderer.getRenderModes()); - addStringToRegion(region, renderer.getRenderState().getVertexFactory(), font, pixelSize, str); + addStringToRegion(region, renderer.getRenderState().getVertexFactory(), font, pixelSize, str, rgbaColor); region.draw(gl, renderer, sampleCount); region.destroy(gl, renderer); } @@ -197,18 +205,19 @@ public class TextRegionUtil { * @param font {@link Font} to be used * @param pixelSize Use {@link Font#getPixelSize(float, float)} for resolution correct pixel-size. * @param str text to be rendered + * @param rgbaColor if {@link Region#hasColorChannel()} RGBA color must be passed, otherwise value is ignored. * @param sampleCount desired multisampling sample count for msaa-rendering. * The actual used scample-count is written back when msaa-rendering is enabled, otherwise the store is untouched. * @throws Exception if TextRenderer not initialized */ public static void drawString3D(final GLRegion region, final RegionRenderer renderer, final GL2ES2 gl, final Font font, final float pixelSize, final CharSequence str, - final int[/*1*/] sampleCount) { + final float[] rgbaColor, final int[/*1*/] sampleCount) { if(!renderer.isInitialized()){ throw new GLException("TextRendererImpl01: not initialized!"); } region.clear(gl, renderer); - addStringToRegion(region, renderer.getRenderState().getVertexFactory(), font, pixelSize, str); + addStringToRegion(region, renderer.getRenderState().getVertexFactory(), font, pixelSize, str, rgbaColor); region.draw(gl, renderer, sampleCount); } diff --git a/src/jogl/classes/com/jogamp/graph/curve/tess/Triangulator.java b/src/jogl/classes/com/jogamp/graph/curve/tess/Triangulator.java index 96ff4bbb8..20fe9bfd9 100644 --- a/src/jogl/classes/com/jogamp/graph/curve/tess/Triangulator.java +++ b/src/jogl/classes/com/jogamp/graph/curve/tess/Triangulator.java @@ -64,8 +64,14 @@ public interface Triangulator { */ public void generate(List<Triangle> sink); - /** Reset the triangulation to initial state - * Clearing cached data + /** + * Reset the triangulation to initial state + * Clearing cached data */ public void reset(); + + /** + * Return the number of newly added vertices during {@link #addCurve(List, Outline, float)}. + */ + public int getAddedVerticeCount(); } diff --git a/src/jogl/classes/com/jogamp/graph/font/Font.java b/src/jogl/classes/com/jogamp/graph/font/Font.java index ac7a904e7..811ab9d94 100644 --- a/src/jogl/classes/com/jogamp/graph/font/Font.java +++ b/src/jogl/classes/com/jogamp/graph/font/Font.java @@ -27,7 +27,10 @@ */ package com.jogamp.graph.font; +import jogamp.graph.geom.plane.AffineTransform; + import com.jogamp.graph.curve.OutlineShape; +import com.jogamp.graph.curve.opengl.TextRegionUtil.ShapeVisitor; import com.jogamp.opengl.math.geom.AABBox; /** @@ -155,9 +158,26 @@ public interface Font { public int getNumGlyphs(); public float getLineHeight(float pixelSize); - public float getStringWidth(CharSequence string, float pixelSize); - public float getStringHeight(CharSequence string, float pixelSize); - public AABBox getStringBounds(CharSequence string, float pixelSize); + public float getMetricWidth(CharSequence string, float pixelSize); + public float getMetricHeight(CharSequence string, float pixelSize); + /** + * Return the <i>layout</i> bounding box as computed by each glyph's metrics. + * The result is not pixel correct, bit reflects layout specific metrics. + * <p> + * See {@link #getPointsBounds(AffineTransform, CharSequence, float)} for pixel correct results. + * </p> + * @param string string text + * @param pixelSize Use {@link Font#getPixelSize(float, float)} for resolution correct pixel-size. + */ + public AABBox getMetricBounds(CharSequence string, float pixelSize); + + /** + * Return the bounding box by taking each glyph's point-based bounding box into account. + * @param transform optional given transform + * @param string string text + * @param pixelSize Use {@link Font#getPixelSize(float, float)} for resolution correct pixel-size. + */ + public AABBox getPointsBounds(final AffineTransform transform, CharSequence string, float pixelSize); public boolean isPrintableChar( char c ); |